diff --git a/src/all/fmreader/build.gradle b/src/all/fmreader/build.gradle index 944b67b72..0250315eb 100644 --- a/src/all/fmreader/build.gradle +++ b/src/all/fmreader/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'FMReader (multiple aggregators)' pkgNameSuffix = 'all.fmreader' extClass = '.FMReaderFactory' - extVersionCode = 19 + extVersionCode = 20 libVersion = '1.2' } diff --git a/src/all/fmreader/src/eu/kanade/tachiyomi/extension/all/fmreader/FMReaderFactory.kt b/src/all/fmreader/src/eu/kanade/tachiyomi/extension/all/fmreader/FMReaderFactory.kt index ad15e65a3..e47e4c1ba 100644 --- a/src/all/fmreader/src/eu/kanade/tachiyomi/extension/all/fmreader/FMReaderFactory.kt +++ b/src/all/fmreader/src/eu/kanade/tachiyomi/extension/all/fmreader/FMReaderFactory.kt @@ -33,7 +33,6 @@ class FMReaderFactory : SourceFactory { Manhwa18(), EighteenLHPlus(), MangaTR(), - Comicastle(), Manhwa18Net(), Manhwa18NetRaw(), SayTruyen(), @@ -207,43 +206,6 @@ class MangaTR : FMReader("Manga-TR", "https://manga-tr.com", "tr") { override fun pageListRequest(chapter: SChapter): Request = GET("$baseUrl/${chapter.url.substringAfter("cek/")}", headers) } -class Comicastle : FMReader("Comicastle", "https://www.comicastle.org", "en") { - override fun popularMangaRequest(page: Int): Request = - GET("$baseUrl/comic-dir?sorting=views&c-page=$page&sorting-type=DESC", headers) - override fun popularMangaNextPageSelector() = "li:contains(ยป):not(.disabled)" - override fun latestUpdatesRequest(page: Int): Request = - GET("$baseUrl/comic-dir?sorting=lastUpdate&c-page=$page&sorting-type=ASC", headers) - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = - GET("$baseUrl/comic-dir?q=$query" + if (page > 1) "&c-page=$page" else "", headers) - override fun getFilterList() = FilterList() - override fun mangaDetailsParse(document: Document): SManga { - val manga = SManga.create() - val infoElement = document.select("div.col-md-9").first() - - manga.author = infoElement.select("tr + tr td a").first().text() - manga.artist = infoElement.select("tr + tr td + td a").text() - manga.genre = infoElement.select("tr + tr td + td + td").text() - manga.description = infoElement.select("p").text().trim() - manga.thumbnail_url = document.select("img.manga-cover").attr("abs:src") - - return manga - } - - override fun chapterListSelector() = "div.col-md-9 table:last-of-type tr" - override fun chapterListParse(response: Response): List = super.chapterListParse(response).reversed() - override fun pageListParse(document: Document): List { - val pages = mutableListOf() - - document.select("div.text-center select option").forEachIndexed { i, imgPage -> - pages.add(Page(i, imgPage.attr("value"))) - } - return pages - } - - override fun imageUrlParse(document: Document): String = document.select("img.chapter-img").attr("abs:src").trim() - override fun getGenreList() = getComicsGenreList() -} - class Manhwa18Net : FMReader("Manhwa18.net", "https://manhwa18.net", "en") { override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/$requestPath?listType=pagination&page=$page&sort=views&sort_type=DESC&ungenre=raw", headers) diff --git a/src/en/comicastle/build.gradle b/src/en/comicastle/build.gradle new file mode 100644 index 000000000..4b8af6f89 --- /dev/null +++ b/src/en/comicastle/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'Comicastle' + pkgNameSuffix = 'en.comicastle' + extClass = '.Comicastle' + extVersionCode = 1 + libVersion = '1.2' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/comicastle/res/mipmap-hdpi/ic_launcher.png b/src/en/comicastle/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..ed218573d Binary files /dev/null and b/src/en/comicastle/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/comicastle/res/mipmap-mdpi/ic_launcher.png b/src/en/comicastle/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..25b0ea50c Binary files /dev/null and b/src/en/comicastle/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/comicastle/res/mipmap-xhdpi/ic_launcher.png b/src/en/comicastle/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..adaeea6ad Binary files /dev/null and b/src/en/comicastle/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/comicastle/res/mipmap-xxhdpi/ic_launcher.png b/src/en/comicastle/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..f96e3c35a Binary files /dev/null and b/src/en/comicastle/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/comicastle/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/comicastle/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..26d0efe56 Binary files /dev/null and b/src/en/comicastle/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/comicastle/res/web_hi_res_512.png b/src/en/comicastle/res/web_hi_res_512.png new file mode 100644 index 000000000..c9b3c22e5 Binary files /dev/null and b/src/en/comicastle/res/web_hi_res_512.png differ diff --git a/src/en/comicastle/src/eu/kanade/tachiyomi/extension/en/comicastle/Comicastle.kt b/src/en/comicastle/src/eu/kanade/tachiyomi/extension/en/comicastle/Comicastle.kt new file mode 100644 index 000000000..2d0f1d1dc --- /dev/null +++ b/src/en/comicastle/src/eu/kanade/tachiyomi/extension/en/comicastle/Comicastle.kt @@ -0,0 +1,163 @@ +package eu.kanade.tachiyomi.extension.en.comicastle + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import okhttp3.HttpUrl +import okhttp3.MediaType +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.net.URLEncoder +import java.util.Calendar +import java.util.Locale + +class Comicastle : ParsedHttpSource() { + + override val name = "Comicastle" + + override val versionId = 2 + + override val baseUrl = "https://www.comicastle.org" + + override val lang = "en" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient + + private fun pageSegments(page: Int): String = if (page > 1) "/index/${(page - 1) * 30}" else "" + + // Popular + + override fun popularMangaRequest(page: Int): Request { + return GET("$baseUrl/library/popular/desc" + pageSegments(page), headers) + } + + override fun popularMangaSelector() = "div.col-lg-2" + + override fun popularMangaFromElement(element: Element): SManga { + return SManga.create().apply { + title = element.select("p").text() + setUrlWithoutDomain(element.select("a").first().attr("href")) + thumbnail_url = element.select("img").attr("abs:src") + } + } + + override fun popularMangaNextPageSelector() = "li.page-item.next a" + + // Latest + + override fun latestUpdatesRequest(page: Int): Request { + return GET("$baseUrl/library/postdate/desc" + pageSegments(page), headers) + } + + override fun latestUpdatesSelector() = popularMangaSelector() + + override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element) + + override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() + + // Search + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = HttpUrl.parse("$baseUrl/library/search")!!.newBuilder() + var rBody: RequestBody? = null + + (filters.let { if (it.isEmpty()) getFilterList() else filters }) + .filterIsInstance() + .firstOrNull { it.hasSelection() } + ?.let { filter -> + url.addPathSegment(filter.pathSegment) + rBody = filter.toRequestBody() + } + + if (rBody == null) rBody = createRequestBody(query) + + return POST(url.toString(), headers, rBody!!) + } + + override fun searchMangaSelector() = popularMangaSelector() + + override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element) + + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + + // Details + + override fun mangaDetailsParse(document: Document): SManga { + return SManga.create().apply { + with(document.select("div.card-body > div.mb-5")) { + thumbnail_url = select("img").attr("abs:src") + val publisher = select("p:contains(Publisher) button") + .firstOrNull()?.let { "Publisher: ${it.text()}\n" } + description = publisher + select("h5:contains(Description) + p").text() + author = select("p:contains(Writer) button").joinToString { it.text() } + artist = select("p:contains(Artist) button").joinToString { it.text() } + status = select("p span.mr-1 strong").text().toStatus() + genre = select("p:contains(Genre) button").joinToString { it.text() } + } + } + } + + private fun String.toStatus() = when { + this.contains("Ongoing", ignoreCase = true) -> SManga.ONGOING + this.contains("Completed", ignoreCase = true) -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + + // Chapters + + override fun chapterListParse(response: Response): List { + return super.chapterListParse(response).reversed() + } + + override fun chapterListSelector() = "table tr a" + + override fun chapterFromElement(element: Element): SChapter { + return SChapter.create().apply { + name = element.text() + setUrlWithoutDomain(element.attr("href")) + } + } + + // Pages + + override fun pageListParse(document: Document): List { + return document.select("div.img-list img").mapIndexed { i, img -> + Page(i, "", img.attr("abs:src")) + } + } + + override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") + + // Filters + + override fun getFilterList() = FilterList( + Filter.Header("Cannot combine search types!"), + Filter.Separator(), + PostFilter("Genre", getGenreList()), + PostFilter("Year", getYearList()), + PostFilter("Publisher", getPublisherList()) + ) + + private open class PostFilter(name: String, val vals: Array) : Filter.Select(name, vals) { + val pathSegment = name.toLowerCase(Locale.US) + fun hasSelection(): Boolean = state != 0 + fun toRequestBody(): RequestBody = createRequestBody(vals[state]) + } + + private fun getGenreList() = arrayOf("") + (Calendar.getInstance()[1] downTo 1963).map { it.toString() }.toTypedArray() + private fun getPublisherList() = arrayOf("