From 57e51e8ef135f22dae4ddb09ea4a847982cd3e7d Mon Sep 17 00:00:00 2001 From: Jake Date: Wed, 12 Mar 2025 19:32:53 +0800 Subject: [PATCH] Rewrite Mangabox (Mangakakalot, Manganato, Mangabat) to Allow Mirrors and CDN Fallbacks (#7915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added CDN Fallback For Mangabox-based extensions * Improved CDN testing Now prioritizes last-worked CDNs Seems like they "fixed" the issue by changing the alternative/backup CDNs to a single, working CDN. * re-added the removed null check at line 68 * refactored, made fallbacks configurable * Removed mangairo * Added mirrors * lint * lint again * final lint * review changes, lint * refactor, lint * lint again 😩 --- lib-multisrc/mangabox/build.gradle.kts | 2 +- .../tachiyomi/multisrc/mangabox/MangaBox.kt | 455 +++++++++++------- .../multisrc/mangabox/MangaBoxLinkedCdnSet.kt | 20 + src/en/mangabat/build.gradle | 4 +- .../extension/en/mangabat/Mangabat.kt | 20 +- src/en/mangairo/build.gradle | 10 - .../mangairo/res/mipmap-hdpi/ic_launcher.png | Bin 3648 -> 0 bytes .../mangairo/res/mipmap-mdpi/ic_launcher.png | Bin 2165 -> 0 bytes .../mangairo/res/mipmap-xhdpi/ic_launcher.png | Bin 4947 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 8848 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 13088 -> 0 bytes .../extension/en/mangairo/Mangairo.kt | 32 -- src/en/mangakakalot/build.gradle | 2 +- .../extension/en/mangakakalot/Mangakakalot.kt | 119 +---- src/en/manganelo/build.gradle | 2 +- .../extension/en/manganelo/Manganato.kt | 121 +---- 16 files changed, 331 insertions(+), 456 deletions(-) create mode 100644 lib-multisrc/mangabox/src/eu/kanade/tachiyomi/multisrc/mangabox/MangaBoxLinkedCdnSet.kt delete mode 100644 src/en/mangairo/build.gradle delete mode 100644 src/en/mangairo/res/mipmap-hdpi/ic_launcher.png delete mode 100644 src/en/mangairo/res/mipmap-mdpi/ic_launcher.png delete mode 100644 src/en/mangairo/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 src/en/mangairo/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 src/en/mangairo/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 src/en/mangairo/src/eu/kanade/tachiyomi/extension/en/mangairo/Mangairo.kt diff --git a/lib-multisrc/mangabox/build.gradle.kts b/lib-multisrc/mangabox/build.gradle.kts index b45873b53..ede652be5 100644 --- a/lib-multisrc/mangabox/build.gradle.kts +++ b/lib-multisrc/mangabox/build.gradle.kts @@ -2,4 +2,4 @@ plugins { id("lib-multisrc") } -baseVersionCode = 5 +baseVersionCode = 6 diff --git a/lib-multisrc/mangabox/src/eu/kanade/tachiyomi/multisrc/mangabox/MangaBox.kt b/lib-multisrc/mangabox/src/eu/kanade/tachiyomi/multisrc/mangabox/MangaBox.kt index df3f5165b..c070e7fbd 100644 --- a/lib-multisrc/mangabox/src/eu/kanade/tachiyomi/multisrc/mangabox/MangaBox.kt +++ b/lib-multisrc/mangabox/src/eu/kanade/tachiyomi/multisrc/mangabox/MangaBox.kt @@ -1,7 +1,11 @@ package eu.kanade.tachiyomi.multisrc.mangabox import android.annotation.SuppressLint +import android.content.SharedPreferences +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.Page @@ -9,42 +13,144 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup +import keiyoushi.utils.getPreferencesLazy +import keiyoushi.utils.tryParse import okhttp3.Headers +import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response +import okio.IOException import org.jsoup.nodes.Document import org.jsoup.nodes.Element -import java.text.ParseException import java.text.SimpleDateFormat -import java.util.Calendar import java.util.Locale -import java.util.concurrent.TimeUnit +import java.util.TimeZone +import java.util.regex.Pattern -// Based off of Mangakakalot 1.2.8 abstract class MangaBox( override val name: String, - override val baseUrl: String, + private val mirrorEntries: Array, override val lang: String, - private val dateformat: SimpleDateFormat = SimpleDateFormat("MMM-dd-yy", Locale.ENGLISH), -) : ParsedHttpSource() { + private val dateFormat: SimpleDateFormat = SimpleDateFormat( + "MMM-dd-yyyy HH:mm", + Locale.ENGLISH, + ).apply { + timeZone = TimeZone.getTimeZone("UTC") + }, +) : ParsedHttpSource(), ConfigurableSource { override val supportsLatest = true + override val baseUrl: String get() = mirror + override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(15, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + .addInterceptor(::useAltCdnInterceptor) .build() + private fun SharedPreferences.getMirrorPref(): String = + getString(PREF_USE_MIRROR, mirrorEntries[0])!! + + private val preferences: SharedPreferences by getPreferencesLazy { + // if current mirror is not in mirrorEntries, set default + if (getMirrorPref() !in mirrorEntries.map { "${URL_PREFIX}$it" }) { + edit().putString(PREF_USE_MIRROR, "${URL_PREFIX}${mirrorEntries[0]}").apply() + } + } + + private var mirror = "" + get() { + if (field.isNotEmpty()) { + return field + } + + field = preferences.getMirrorPref() + return field + } + + private val cdnSet = + MangaBoxLinkedCdnSet() // Stores all unique CDNs that the extension can use to retrieve chapter images + + private class MangaBoxFallBackTag // Custom empty class tag to use as an identifier that the specific request is fallback-able + + private fun HttpUrl.getBaseUrl(): String = + "${URL_PREFIX}${this.host}${ + when (this.port) { + 80, 443 -> "" + else -> ":${this.port}" + } + }" + + private fun useAltCdnInterceptor(chain: Interceptor.Chain): Response { + val request = chain.request() + val requestTag = request.tag(MangaBoxFallBackTag::class.java) + val originalResponse: Response? = try { + chain.proceed(request) + } catch (e: IOException) { + if (requestTag == null) { + throw e + } else { + null + } + } + + if (requestTag == null || originalResponse?.isSuccessful == true) { + requestTag?.let { + // Move working cdn to first so it gets priority during iteration + cdnSet.moveItemToFirst(request.url.getBaseUrl()) + } + + return originalResponse!! + } + + // Close the original response if it's not successful + originalResponse?.close() + + for (cdnUrl in cdnSet) { + var tryResponse: Response? = null + + try { + val newUrl = cdnUrl.toHttpUrl().newBuilder() + .encodedPath(request.url.encodedPath) + .fragment(request.url.fragment) + .build() + + // Create a new request with the updated URL + val newRequest = request.newBuilder() + .url(newUrl) + .build() + + // Proceed with the new request + tryResponse = chain.proceed(newRequest) + + // Check if the response is successful + if (tryResponse.isSuccessful) { + // Move working cdn to first so it gets priority during iteration + cdnSet.moveItemToFirst(newRequest.url.getBaseUrl()) + + return tryResponse + } + + tryResponse.close() + } catch (_: IOException) { + tryResponse?.close() + } + } + + // If all CDNs fail, throw an error + return throw IOException("All CDN attempts failed.") + } + override fun headersBuilder(): Headers.Builder = super.headersBuilder() - .add("Referer", baseUrl) // for covers + .add("Referer", "$baseUrl/") - open val popularUrlPath = "manga_list?type=topview&category=all&state=all&page=" + open val popularUrlPath = "manga-list/hot-manga?page=" - open val latestUrlPath = "manga_list?type=latest&category=all&state=all&page=" + open val latestUrlPath = "manga-list/latest-manga?page=" - open val simpleQueryPath = "search/" + open val simpleQueryPath = "search/story/" override fun popularMangaSelector() = "div.truyen-list > div.list-truyen-item-wrap" @@ -58,10 +164,11 @@ abstract class MangaBox( return GET("$baseUrl/$latestUrlPath$page", headers) } - protected fun mangaFromElement(element: Element, urlSelector: String = "h3 a"): SManga { + private fun mangaFromElement(element: Element, urlSelector: String = "h3 a"): SManga { return SManga.create().apply { element.select(urlSelector).first()!!.let { - url = it.attr("abs:href").substringAfter(baseUrl) // intentionally not using setUrlWithoutDomain + url = it.attr("abs:href") + .substringAfter(baseUrl) // intentionally not using setUrlWithoutDomain title = it.text() } thumbnail_url = element.select("img").first()!!.attr("abs:src") @@ -72,62 +179,47 @@ abstract class MangaBox( override fun latestUpdatesFromElement(element: Element): SManga = mangaFromElement(element) - override fun popularMangaNextPageSelector() = "div.group_page, div.group-page a:not([href]) + a:not(:contains(Last))" + override fun popularMangaNextPageSelector() = + "div.group_page, div.group-page a:not([href]) + a:not(:contains(Last))" override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - return if (query.isNotBlank() && getAdvancedGenreFilters().isEmpty()) { - GET("$baseUrl/$simpleQueryPath${normalizeSearchQuery(query)}?page=$page", headers) + return if (query.isNotBlank()) { + val url = "$baseUrl/$simpleQueryPath".toHttpUrl().newBuilder() + .addPathSegment(normalizeSearchQuery(query)) + .addQueryParameter("page", page.toString()) + .build() + + return GET(url, headers) } else { - val url = baseUrl.toHttpUrl().newBuilder() - if (getAdvancedGenreFilters().isNotEmpty()) { - url.addPathSegment("advanced_search") - url.addQueryParameter("page", page.toString()) - url.addQueryParameter("keyw", normalizeSearchQuery(query)) - var genreInclude = "" - var genreExclude = "" - filters.forEach { filter -> - when (filter) { - is KeywordFilter -> filter.toUriPart()?.let { url.addQueryParameter("keyt", it) } - is SortFilter -> url.addQueryParameter("orby", filter.toUriPart()) - is StatusFilter -> url.addQueryParameter("sts", filter.toUriPart()) - is AdvGenreFilter -> { - filter.state.forEach { if (it.isIncluded()) genreInclude += "_${it.id}" } - filter.state.forEach { if (it.isExcluded()) genreExclude += "_${it.id}" } - } - else -> {} - } - } - url.addQueryParameter("g_i", genreInclude) - url.addQueryParameter("g_e", genreExclude) - } else { - url.addPathSegment("manga_list") - url.addQueryParameter("page", page.toString()) - filters.forEach { filter -> - when (filter) { - is SortFilter -> url.addQueryParameter("type", filter.toUriPart()) - is StatusFilter -> url.addQueryParameter("state", filter.toUriPart()) - is GenreFilter -> url.addQueryParameter("category", filter.toUriPart()) - else -> {} - } + val url = "$baseUrl/genre".toHttpUrl().newBuilder() + url.addQueryParameter("page", page.toString()) + filters.forEach { filter -> + when (filter) { + is SortFilter -> url.addQueryParameter("type", filter.toUriPart()) + is StatusFilter -> url.addQueryParameter("state", filter.toUriPart()) + is GenreFilter -> url.addPathSegment(filter.toUriPart()!!) + else -> {} } } + GET(url.build(), headers) } } - override fun searchMangaSelector() = ".panel_story_list .story_item" + override fun searchMangaSelector() = ".panel_story_list .story_item, div.list-truyen-item-wrap" override fun searchMangaFromElement(element: Element) = mangaFromElement(element) - override fun searchMangaNextPageSelector() = "a.page_select + a:not(.page_last), a.page-select + a:not(.page-last)" + override fun searchMangaNextPageSelector() = + "a.page_select + a:not(.page_last), a.page-select + a:not(.page-last)" open val mangaDetailsMainSelector = "div.manga-info-top, div.panel-story-info" open val thumbnailSelector = "div.manga-info-pic img, span.info-image img" - open val descriptionSelector = "div#noidungm, div#panel-story-info-description" + open val descriptionSelector = "div#noidungm, div#panel-story-info-description, div#contentBox" override fun mangaDetailsRequest(manga: SManga): Request { if (manga.url.startsWith("http")) { @@ -146,11 +238,15 @@ abstract class MangaBox( return SManga.create().apply { document.select(mangaDetailsMainSelector).firstOrNull()?.let { infoElement -> title = infoElement.select("h1, h2").first()!!.text() - author = infoElement.select("li:contains(author) a, td:containsOwn(author) + td a").eachText().joinToString() - status = parseStatus(infoElement.select("li:contains(status), td:containsOwn(status) + td").text()) + author = infoElement.select("li:contains(author) a, td:containsOwn(author) + td a") + .eachText().joinToString() + status = parseStatus( + infoElement.select("li:contains(status), td:containsOwn(status) + td").text(), + ) genre = infoElement.select("div.manga-info-top li:contains(genres)").firstOrNull() ?.select("a")?.joinToString { it.text() } // kakalot - ?: infoElement.select("td:containsOwn(genres) + td a").joinToString { it.text() } // nelo + ?: infoElement.select("td:containsOwn(genres) + td a") + .joinToString { it.text() } // nelo } ?: checkForRedirectMessage(document) description = document.select(descriptionSelector).firstOrNull()?.ownText() ?.replace("""^$title summary:\s""".toRegex(), "") @@ -199,44 +295,23 @@ abstract class MangaBox( protected open val alternateChapterDateSelector = String() - protected fun Element.selectDateFromElement(): Element { + private fun Element.selectDateFromElement(): Element { val defaultChapterDateSelector = "span" - return this.select(defaultChapterDateSelector).lastOrNull() ?: this.select(alternateChapterDateSelector).last()!! + return this.select(defaultChapterDateSelector).lastOrNull() ?: this.select( + alternateChapterDateSelector, + ).last()!! } override fun chapterFromElement(element: Element): SChapter { return SChapter.create().apply { element.select("a").let { - url = it.attr("abs:href").substringAfter(baseUrl) // intentionally not using setUrlWithoutDomain + url = it.attr("abs:href") + .substringAfter(baseUrl) // intentionally not using setUrlWithoutDomain name = it.text() scanlator = it.attr("abs:href").toHttpUrl().host // show where chapters are actually from } - date_upload = parseChapterDate(element.selectDateFromElement().text(), scanlator!!) ?: 0 - } - } - - private fun parseChapterDate(date: String, host: String): Long? { - return if ("ago" in date) { - val value = date.split(' ')[0].toIntOrNull() - val cal = Calendar.getInstance() - when { - value != null && "min" in date -> cal.apply { add(Calendar.MINUTE, -value) } - value != null && "hour" in date -> cal.apply { add(Calendar.HOUR_OF_DAY, -value) } - value != null && "day" in date -> cal.apply { add(Calendar.DATE, -value) } - else -> null - }?.timeInMillis - } else { - try { - if (host.contains("manganato", ignoreCase = true)) { - // Nelo's date format - SimpleDateFormat("MMM dd,yy", Locale.ENGLISH).parse(date) - } else { - dateformat.parse(date) - } - } catch (e: ParseException) { - null - }?.time + date_upload = dateFormat.tryParse(element.selectDateFromElement().attr("title")) } } @@ -247,26 +322,59 @@ abstract class MangaBox( return super.pageListRequest(chapter) } - open val pageListSelector = "div#vungdoc img, div.container-chapter-reader img" + private fun extractArray(scriptContent: String, arrayName: String): List { + val pattern = Pattern.compile("$arrayName\\s*=\\s*\\[([^]]+)]") + val matcher = pattern.matcher(scriptContent) + val arrayValues = mutableListOf() + + if (matcher.find()) { + val arrayContent = matcher.group(1) + val values = arrayContent?.split(",") + if (values != null) { + for (value in values) { + arrayValues.add( + value.trim() + .removeSurrounding("\"") + .replace("\\/", "/") + .removeSuffix("/"), + ) + } + } + } + + return arrayValues + } override fun pageListParse(document: Document): List { - return document.select(pageListSelector) - // filter out bad elements for mangakakalots - .filterNot { it.attr("src").endsWith("log") } - .mapIndexed { i, element -> - val url = element.attr("abs:src").let { src -> - if (src.startsWith("https://convert_image_digi.mgicdn.com")) { - "https://images.weserv.nl/?url=" + src.substringAfter("//") - } else { - src - } - } - Page(i, document.location(), url) + val element = document.select("head > script").lastOrNull() + ?: return emptyList() + val cdns = + extractArray(element.html(), "cdns") + extractArray(element.html(), "backupImage") + val chapterImages = extractArray(element.html(), "chapterImages") + + // Add all parsed cdns to set + cdnSet.addAll(cdns) + + return chapterImages.mapIndexed { i, imagePath -> + val parsedUrl = cdns[0].toHttpUrl().run { + newBuilder() + .encodedPath( + "/$imagePath".replace( + "//", + "/", + ), + ) // replace ensures that there's at least one trailing slash prefix + .build() + .toString() } + + Page(i, document.location(), parsedUrl) + } } override fun imageRequest(page: Page): Request { - return GET(page.imageUrl!!, headersBuilder().set("Referer", page.url).build()) + return GET(page.imageUrl!!, headers).newBuilder() + .tag(MangaBoxFallBackTag::class.java, MangaBoxFallBackTag()).build() } override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException() @@ -282,47 +390,27 @@ abstract class MangaBox( str = str.replace("[ùúụủũưừứựửữ]".toRegex(), "u") str = str.replace("[ỳýỵỷỹ]".toRegex(), "y") str = str.replace("đ".toRegex(), "d") - str = str.replace("""!|@|%|\^|\*|\(|\)|\+|=|<|>|\?|/|,|\.|:|;|'| |"|&|#|\[|]|~|-|$|_""".toRegex(), "_") + str = str.replace( + """!|@|%|\^|\*|\(|\)|\+|=|<|>|\?|/|,|\.|:|;|'| |"|&|#|\[|]|~|-|$|_""".toRegex(), + "_", + ) str = str.replace("_+_".toRegex(), "_") str = str.replace("""^_+|_+$""".toRegex(), "") return str } - override fun getFilterList() = if (getAdvancedGenreFilters().isNotEmpty()) { - FilterList( - KeywordFilter(getKeywordFilters()), - SortFilter(getSortFilters()), - StatusFilter(getStatusFilters()), - AdvGenreFilter(getAdvancedGenreFilters()), - ) - } else { - FilterList( - Filter.Header("NOTE: Ignored if using text search!"), - Filter.Separator(), - SortFilter(getSortFilters()), - StatusFilter(getStatusFilters()), - GenreFilter(getGenreFilters()), - ) - } - - // Technically, only Sort, Status, and Genre need to be non-private for Mangakakalot and Manganato, but I'll include Keyword to make it uniform. - protected class KeywordFilter(vals: Array>) : UriPartFilter("Keyword search ", vals) - protected class SortFilter(vals: Array>) : UriPartFilter("Order by", vals) - protected class StatusFilter(vals: Array>) : UriPartFilter("Status", vals) - protected class GenreFilter(vals: Array>) : UriPartFilter("Category", vals) - - // For advanced search, specifically tri-state genres - private class AdvGenreFilter(vals: List) : Filter.Group("Category", vals) - class AdvGenre(val id: String?, name: String) : Filter.TriState(name) - - // keyt query parameter - private fun getKeywordFilters(): Array> = arrayOf( - Pair(null, "Everything"), - Pair("title", "Title"), - Pair("alternative", "Alt title"), - Pair("author", "Author"), + override fun getFilterList() = FilterList( + Filter.Header("NOTE: Ignored if using text search!"), + Filter.Separator(), + SortFilter(getSortFilters()), + StatusFilter(getStatusFilters()), + GenreFilter(getGenreFilters()), ) + private class SortFilter(vals: Array>) : UriPartFilter("Order by", vals) + private class StatusFilter(vals: Array>) : UriPartFilter("Status", vals) + private class GenreFilter(vals: Array>) : UriPartFilter("Category", vals) + private fun getSortFilters(): Array> = arrayOf( Pair("latest", "Latest"), Pair("newest", "Newest"), @@ -338,53 +426,72 @@ abstract class MangaBox( open fun getGenreFilters(): Array> = arrayOf( Pair("all", "ALL"), - Pair("2", "Action"), - Pair("3", "Adult"), - Pair("4", "Adventure"), - Pair("6", "Comedy"), - Pair("7", "Cooking"), - Pair("9", "Doujinshi"), - Pair("10", "Drama"), - Pair("11", "Ecchi"), - Pair("12", "Fantasy"), - Pair("13", "Gender bender"), - Pair("14", "Harem"), - Pair("15", "Historical"), - Pair("16", "Horror"), - Pair("45", "Isekai"), - Pair("17", "Josei"), - Pair("44", "Manhua"), - Pair("43", "Manhwa"), - Pair("19", "Martial arts"), - Pair("20", "Mature"), - Pair("21", "Mecha"), - Pair("22", "Medical"), - Pair("24", "Mystery"), - Pair("25", "One shot"), - Pair("26", "Psychological"), - Pair("27", "Romance"), - Pair("28", "School life"), - Pair("29", "Sci fi"), - Pair("30", "Seinen"), - Pair("31", "Shoujo"), - Pair("32", "Shoujo ai"), - Pair("33", "Shounen"), - Pair("34", "Shounen ai"), - Pair("35", "Slice of life"), - Pair("36", "Smut"), - Pair("37", "Sports"), - Pair("38", "Supernatural"), - Pair("39", "Tragedy"), - Pair("40", "Webtoons"), - Pair("41", "Yaoi"), - Pair("42", "Yuri"), + Pair("action", "Action"), + Pair("adult", "Adult"), + Pair("adventure", "Adventure"), + Pair("comedy", "Comedy"), + Pair("cooking", "Cooking"), + Pair("doujinshi", "Doujinshi"), + Pair("drama", "Drama"), + Pair("ecchi", "Ecchi"), + Pair("fantasy", "Fantasy"), + Pair("gender-bender", "Gender bender"), + Pair("harem", "Harem"), + Pair("historical", "Historical"), + Pair("horror", "Horror"), + Pair("isekai", "Isekai"), + Pair("josei", "Josei"), + Pair("manhua", "Manhua"), + Pair("manhwa", "Manhwa"), + Pair("martial-arts", "Martial arts"), + Pair("mature", "Mature"), + Pair("mecha", "Mecha"), + Pair("medical", "Medical"), + Pair("mystery", "Mystery"), + Pair("one-shot", "One shot"), + Pair("psychological", "Psychological"), + Pair("romance", "Romance"), + Pair("school-life", "School life"), + Pair("sci-fi", "Sci fi"), + Pair("seinen", "Seinen"), + Pair("shoujo", "Shoujo"), + Pair("shoujo-ai", "Shoujo ai"), + Pair("shounen", "Shounen"), + Pair("shounen-ai", "Shounen ai"), + Pair("slice-of-life", "Slice of life"), + Pair("smut", "Smut"), + Pair("sports", "Sports"), + Pair("supernatural", "Supernatural"), + Pair("tragedy", "Tragedy"), + Pair("webtoons", "Webtoons"), + Pair("yaoi", "Yaoi"), + Pair("yuri", "Yuri"), ) - // To be overridden if using tri-state genres - protected open fun getAdvancedGenreFilters(): List = emptyList() - open class UriPartFilter(displayName: String, private val vals: Array>) : Filter.Select(displayName, vals.map { it.second }.toTypedArray()) { fun toUriPart() = vals[state].first } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + ListPreference(screen.context).apply { + key = PREF_USE_MIRROR + title = "Mirror" + entries = mirrorEntries + entryValues = mirrorEntries.map { "${URL_PREFIX}$it" }.toTypedArray() + setDefaultValue(entryValues[0]) + summary = "%s" + + setOnPreferenceChangeListener { _, newValue -> + // Update values + mirror = newValue as String + true + } + }.let(screen::addPreference) + } + + companion object { + private const val PREF_USE_MIRROR = "pref_use_mirror" + private const val URL_PREFIX = "https://" + } } diff --git a/lib-multisrc/mangabox/src/eu/kanade/tachiyomi/multisrc/mangabox/MangaBoxLinkedCdnSet.kt b/lib-multisrc/mangabox/src/eu/kanade/tachiyomi/multisrc/mangabox/MangaBoxLinkedCdnSet.kt new file mode 100644 index 000000000..5344fefd5 --- /dev/null +++ b/lib-multisrc/mangabox/src/eu/kanade/tachiyomi/multisrc/mangabox/MangaBoxLinkedCdnSet.kt @@ -0,0 +1,20 @@ +package eu.kanade.tachiyomi.multisrc.mangabox + +class MangaBoxLinkedCdnSet : LinkedHashSet() { + fun moveItemToFirst(item: String) { + // Lock the object to avoid multi threading issues + synchronized(this) { + if (this.contains(item) && this.first() != item) { + // Remove the item from the current set + this.remove(item) + // Create a new list with the item at the first position + val newItems = mutableListOf(item) + // Add the remaining items + newItems.addAll(this) + // Clear the current set and add all items from the new list + this.clear() + this.addAll(newItems) + } + } + } +} diff --git a/src/en/mangabat/build.gradle b/src/en/mangabat/build.gradle index 9362f144e..9be3bca0e 100644 --- a/src/en/mangabat/build.gradle +++ b/src/en/mangabat/build.gradle @@ -2,8 +2,8 @@ ext { extName = 'Mangabat' extClass = '.Mangabat' themePkg = 'mangabox' - baseUrl = 'https://h.mangabat.com' - overrideVersionCode = 5 + baseUrl = 'https://www.mangabats.com' + overrideVersionCode = 6 isNsfw = true } diff --git a/src/en/mangabat/src/eu/kanade/tachiyomi/extension/en/mangabat/Mangabat.kt b/src/en/mangabat/src/eu/kanade/tachiyomi/extension/en/mangabat/Mangabat.kt index 7a95b4b34..3eb6275db 100644 --- a/src/en/mangabat/src/eu/kanade/tachiyomi/extension/en/mangabat/Mangabat.kt +++ b/src/en/mangabat/src/eu/kanade/tachiyomi/extension/en/mangabat/Mangabat.kt @@ -1,17 +1,11 @@ package eu.kanade.tachiyomi.extension.en.mangabat import eu.kanade.tachiyomi.multisrc.mangabox.MangaBox -import eu.kanade.tachiyomi.network.GET -import okhttp3.Request -import java.text.SimpleDateFormat -import java.util.Locale -class Mangabat : MangaBox("Mangabat", "https://h.mangabat.com", "en", SimpleDateFormat("MMM dd,yy", Locale.ENGLISH)) { - override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/manga-list-all/$page?type=topview", headers) - override fun popularMangaSelector() = "div.list-story-item" - override val latestUrlPath = "manga-list-all/" - override fun searchMangaSelector() = "div.list-story-item" - override fun getAdvancedGenreFilters(): List = getGenreFilters() - .drop(1) - .map { AdvGenre(it.first, it.second) } -} +class Mangabat : MangaBox( + "Mangabat", + arrayOf( + "www.mangabats.com", + ), + "en", +) diff --git a/src/en/mangairo/build.gradle b/src/en/mangairo/build.gradle deleted file mode 100644 index 5fdaf3882..000000000 --- a/src/en/mangairo/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -ext { - extName = 'Mangairo' - extClass = '.Mangairo' - themePkg = 'mangabox' - baseUrl = 'https://h.mangairo.com' - overrideVersionCode = 4 - isNsfw = true -} - -apply from: "$rootDir/common.gradle" diff --git a/src/en/mangairo/res/mipmap-hdpi/ic_launcher.png b/src/en/mangairo/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 7ab1b9070e001cab11b3674920650c94f2aed582..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 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�r5xP+P|-1VxTgVFc_^4n3+{GZF#ukIX`@k>}Q02;KaNV;^~-K zJ>7_V2=>W5Ai)dYux3h_YOwO(2>}292b9Q62!Ok}fCM^dYW$+t6K(h3oBszn3qKRP diff --git a/src/en/mangairo/res/mipmap-mdpi/ic_launcher.png b/src/en/mangairo/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index df8c10a5871fa9990c100df55428998ac00c0b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2165 zcmV-*2#WWKP)SjX{fmNz2=)(SV?~h#Hl)CLkg)MveZ`& z+_^}w`bYp&tAlNh$<~IO?H_$-4nVYxw&<;ZyOp_|P1XQd+0e=u zYks#i$_xN`Cj+ns($=;HgT+oD%nMTn3f_y1lL`t7T#_Vx7zhNOR8=*NY^SOb25Wy> z{WpFdJ72VhU?0DtD7C?0u)MRg^BNCb4?u!)|9$s=a^uF0V#Cc<;*NMgX26WrvEmeM zjLYS!>Fn%$0YHca!2=*ayYln%`^Lt`Zr27A1k!JjgMSxftZEHj%cIW2i#v)*a3+x!D(}``{w&{ePfC!ET@#R-v zVt9Bs_Ff2$sH(0;Lqjc+ zlq5*96lc6y&hzhV09fo(N%z4q#15&+<}bhe3W^F>Q2Hd;1Zjr>Eoc<;xfw8#5@PR7Wx1IK?jj=<gqoVqFlX*;JpR}d7!BUUjvYJE z(Q!6TsjNt^M#ddD ze7Ftm$9_a{$!3sxP+tBT)~sHO%Bm^~N>LQlH#EQ@CE?Rg_oKC~IhuNr)f2TV#sIiF zuyEl*QyJwIi2OWb#tbw!H&XyxpMM6asj1k$y%a@6g{rF1*jPtN{QmnNATQU0s;X*y z_gxc`oe^Tk34}p!WGw=KI|hKOh-Oj9%*+%6D2xJVsskC**w~0(@88(F_amG6o8fU01v=QO<)0(Y~6yCloY)9 z(o0zSz=L>w*G{Zkw-)QyKZ?5AdhGkSk^-=rI<`|NCnv`s0yKXj^*h@8YaYwY%0g>P z3rGYA0HQ*rWo1}ZxEj^__R(hU#PJjO^_L5HbI&gH_y0{JWGbJ`9yEp6I-vV9$D|Ms zK*R$PiCbG+DF9LjTrL+%OUr08r`3U7yWT)y;VK;X>Ht2g*>4;LPAqIkVBWlW!U#l+ zX4pykJxRjGjTNJ#;6yxZT;}igiKv`Ki1yE60Nefvk1IWf>&^w66-HPwK3fNCfgccVjdVpml=0`{mc4 zDTxUORu`>7MMWj*8xB(0oIH67va|0(kTiX&iVGLcLs1;MGlB?&WdL*M&P~`#urf*v z(OP8kcLIvE*V{`=bpXgb?ZBgrC6a69F$chqm;f-#MYbE&X}NX@A|VD~Z$$;=07)TU zd3Z4}17fa1k(soR&6zVtEThbEQrnnqhv@>cjEw2iDJhFfN>K37GJIb11zOs^rvL^9 z280J1q2XpSYDQuWfLRobT8zn4rr^x!)A0MrCrrS1Q$F?LL-C%mSE5 z6bPY7vnI-KtpnA#UI-SmJx7+=mq9{cGQbkLecBrCM znK+V>U@+J`G&Hn35D0Vw2(U{DZz-cjrvo52l<`L=ZMOJIG_jfX=LMf;% r(ak)M#W(`2Q4=vxG$+wI6WH@VcMM65#S<;f00000NkvXXu0mjf9pd;J diff --git a/src/en/mangairo/res/mipmap-xhdpi/ic_launcher.png b/src/en/mangairo/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 683d30f8b1722113189ec3400153f704e3e57471..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4947 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 RL}UN}002ovPDHLkV1g{g7g_)S diff --git a/src/en/mangairo/res/mipmap-xxhdpi/ic_launcher.png b/src/en/mangairo/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 545a0404ffcd35fbc9b2a72d57d0bed98a3549e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8848 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 diff --git a/src/en/mangairo/src/eu/kanade/tachiyomi/extension/en/mangairo/Mangairo.kt b/src/en/mangairo/src/eu/kanade/tachiyomi/extension/en/mangairo/Mangairo.kt deleted file mode 100644 index a18a4b039..000000000 --- a/src/en/mangairo/src/eu/kanade/tachiyomi/extension/en/mangairo/Mangairo.kt +++ /dev/null @@ -1,32 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.mangairo - -import eu.kanade.tachiyomi.multisrc.mangabox.MangaBox -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.SManga -import okhttp3.Request -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.Locale - -class Mangairo : MangaBox("Mangairo", "https://h.mangairo.com", "en", SimpleDateFormat("MMM-dd-yy", Locale.ENGLISH)) { - override val popularUrlPath = "manga-list/type-topview/ctg-all/state-all/page-" - override fun popularMangaSelector() = "div.story-item" - override val latestUrlPath = "manga-list/type-latest/ctg-all/state-all/page-" - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - return GET("$baseUrl/list/$simpleQueryPath${normalizeSearchQuery(query)}?page=$page", headers) - } - - override fun searchMangaSelector() = "div.story-item" - override fun searchMangaFromElement(element: Element): SManga = mangaFromElement(element, "h2 a") - override fun searchMangaNextPageSelector() = "div.group-page a.select + a:not(.go-p-end)" - override val mangaDetailsMainSelector = "${super.mangaDetailsMainSelector}, div.story_content" - override val thumbnailSelector = "${super.thumbnailSelector}, div.story_info_left img" - override val descriptionSelector = "${super.descriptionSelector}, div#story_discription p" - override fun chapterListSelector() = "${super.chapterListSelector()}, div#chapter_list li" - override val alternateChapterDateSelector = "p" - override val pageListSelector = "${super.pageListSelector}, div.panel-read-story img" - - // will have to write a separate searchMangaRequest to get filters working for this source - override fun getFilterList() = FilterList() -} diff --git a/src/en/mangakakalot/build.gradle b/src/en/mangakakalot/build.gradle index fe16447db..b2c85b4c6 100644 --- a/src/en/mangakakalot/build.gradle +++ b/src/en/mangakakalot/build.gradle @@ -3,7 +3,7 @@ ext { extClass = '.Mangakakalot' themePkg = 'mangabox' baseUrl = 'https://www.mangakakalot.gg' - overrideVersionCode = 4 + overrideVersionCode = 5 isNsfw = true } diff --git a/src/en/mangakakalot/src/eu/kanade/tachiyomi/extension/en/mangakakalot/Mangakakalot.kt b/src/en/mangakakalot/src/eu/kanade/tachiyomi/extension/en/mangakakalot/Mangakakalot.kt index ab998e042..79fae52d1 100644 --- a/src/en/mangakakalot/src/eu/kanade/tachiyomi/extension/en/mangakakalot/Mangakakalot.kt +++ b/src/en/mangakakalot/src/eu/kanade/tachiyomi/extension/en/mangakakalot/Mangakakalot.kt @@ -1,115 +1,12 @@ package eu.kanade.tachiyomi.extension.en.mangakakalot import eu.kanade.tachiyomi.multisrc.mangabox.MangaBox -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.Request -import org.jsoup.nodes.Element -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.Locale -class Mangakakalot : MangaBox("Mangakakalot", "https://www.mangakakalot.gg", "en") { - private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMM-dd-yyyy HH:mm", Locale.ENGLISH) - - override fun headersBuilder(): Headers.Builder = super.headersBuilder().set("Referer", "$baseUrl/") // for covers - override val popularUrlPath = "manga-list/hot-manga?page=" - override val latestUrlPath = "manga-list/latest-manga?page=" - override val simpleQueryPath = "search/story/" - override fun searchMangaSelector() = "${super.searchMangaSelector()}, div.list-truyen-item-wrap" - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - return if (query.isNotBlank() && getAdvancedGenreFilters().isEmpty()) { - val url = "$baseUrl/$simpleQueryPath".toHttpUrl().newBuilder() - .addPathSegment(normalizeSearchQuery(query)) - .addQueryParameter("page", page.toString()) - .build() - - return GET(url, headers) - } else { - val url = "$baseUrl/genre".toHttpUrl().newBuilder() - url.addQueryParameter("page", page.toString()) - filters.forEach { filter -> - when (filter) { - is SortFilter -> url.addQueryParameter("type", filter.toUriPart()) - is StatusFilter -> url.addQueryParameter("state", filter.toUriPart()) - is GenreFilter -> url.addPathSegment(filter.toUriPart()!!) - else -> {} - } - } - - GET(url.build(), headers) - } - } - - override fun chapterFromElement(element: Element): SChapter { - // parse on title attribute rather than the value - val dateUploadAttr: Long? = try { - dateFormat.parse(element.selectDateFromElement().attr("title"))?.time - } catch (e: ParseException) { - null - } - - return super.chapterFromElement(element).apply { - date_upload = dateUploadAttr ?: date_upload - } - } - - override val descriptionSelector = "div#contentBox" - - override fun imageRequest(page: Page): Request { - return if (page.url.contains(baseUrl)) { - GET(page.imageUrl!!, headersBuilder().build()) - } else { // Avoid 403 errors on non-migrated mangas - super.imageRequest(page) - } - } - - override fun getGenreFilters(): Array> = arrayOf( - Pair("all", "ALL"), - Pair("action", "Action"), - Pair("adult", "Adult"), - Pair("adventure", "Adventure"), - Pair("comedy", "Comedy"), - Pair("cooking", "Cooking"), - Pair("doujinshi", "Doujinshi"), - Pair("drama", "Drama"), - Pair("ecchi", "Ecchi"), - Pair("fantasy", "Fantasy"), - Pair("gender-bender", "Gender bender"), - Pair("harem", "Harem"), - Pair("historical", "Historical"), - Pair("horror", "Horror"), - Pair("isekai", "Isekai"), - Pair("josei", "Josei"), - Pair("manhua", "Manhua"), - Pair("manhwa", "Manhwa"), - Pair("martial-arts", "Martial arts"), - Pair("mature", "Mature"), - Pair("mecha", "Mecha"), - Pair("medical", "Medical"), - Pair("mystery", "Mystery"), - Pair("one-shot", "One shot"), - Pair("psychological", "Psychological"), - Pair("romance", "Romance"), - Pair("school-life", "School life"), - Pair("sci-fi", "Sci fi"), - Pair("seinen", "Seinen"), - Pair("shoujo", "Shoujo"), - Pair("shoujo-ai", "Shoujo ai"), - Pair("shounen", "Shounen"), - Pair("shounen-ai", "Shounen ai"), - Pair("slice-of-life", "Slice of life"), - Pair("smut", "Smut"), - Pair("sports", "Sports"), - Pair("supernatural", "Supernatural"), - Pair("tragedy", "Tragedy"), - Pair("webtoons", "Webtoons"), - Pair("yaoi", "Yaoi"), - Pair("yuri", "Yuri"), - ) -} +class Mangakakalot : MangaBox( + "Mangakakalot", + arrayOf( + "www.mangakakalot.gg", + "www.mangakakalove.com", + ), + "en", +) diff --git a/src/en/manganelo/build.gradle b/src/en/manganelo/build.gradle index 0293ff2ed..cdd46a3a2 100644 --- a/src/en/manganelo/build.gradle +++ b/src/en/manganelo/build.gradle @@ -3,7 +3,7 @@ ext { extClass = '.Manganato' themePkg = 'mangabox' baseUrl = 'https://www.natomanga.com' - overrideVersionCode = 3 + overrideVersionCode = 4 isNsfw = true } diff --git a/src/en/manganelo/src/eu/kanade/tachiyomi/extension/en/manganelo/Manganato.kt b/src/en/manganelo/src/eu/kanade/tachiyomi/extension/en/manganelo/Manganato.kt index 4a877b629..718f450a5 100644 --- a/src/en/manganelo/src/eu/kanade/tachiyomi/extension/en/manganelo/Manganato.kt +++ b/src/en/manganelo/src/eu/kanade/tachiyomi/extension/en/manganelo/Manganato.kt @@ -1,117 +1,16 @@ package eu.kanade.tachiyomi.extension.en.manganelo import eu.kanade.tachiyomi.multisrc.mangabox.MangaBox -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.Request -import org.jsoup.nodes.Element -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.Locale -class Manganato : MangaBox("Manganato", "https://www.natomanga.com", "en") { +class Manganato : MangaBox( + "Manganato", + arrayOf( + "www.natomanga.com", + "www.nelomanga.com", + "www.manganato.gg", + ), + "en", +) { + override val id: Long = 1024627298672457456 - - private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMM-dd-yyyy HH:mm", Locale.ENGLISH) - - override fun headersBuilder(): Headers.Builder = super.headersBuilder().set("Referer", "$baseUrl/") // for covers - override val popularUrlPath = "manga-list/hot-manga?page=" - override val latestUrlPath = "manga-list/latest-manga?page=" - override val simpleQueryPath = "search/story/" - override fun searchMangaSelector() = "${super.searchMangaSelector()}, div.list-truyen-item-wrap" - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - return if (query.isNotBlank() && getAdvancedGenreFilters().isEmpty()) { - val url = "$baseUrl/$simpleQueryPath".toHttpUrl().newBuilder() - .addPathSegment(normalizeSearchQuery(query)) - .addQueryParameter("page", page.toString()) - .build() - - return GET(url, headers) - } else { - val url = "$baseUrl/genre".toHttpUrl().newBuilder() - url.addQueryParameter("page", page.toString()) - filters.forEach { filter -> - when (filter) { - is SortFilter -> url.addQueryParameter("type", filter.toUriPart()) - is StatusFilter -> url.addQueryParameter("state", filter.toUriPart()) - is GenreFilter -> url.addPathSegment(filter.toUriPart()!!) - else -> {} - } - } - - GET(url.build(), headers) - } - } - - override fun chapterFromElement(element: Element): SChapter { - // parse on title attribute rather than the value - val dateUploadAttr: Long? = try { - dateFormat.parse(element.selectDateFromElement().attr("title"))?.time - } catch (e: ParseException) { - null - } - - return super.chapterFromElement(element).apply { - date_upload = dateUploadAttr ?: date_upload - } - } - - override val descriptionSelector = "div#contentBox" - - override fun imageRequest(page: Page): Request { - return if (page.url.contains(baseUrl)) { - GET(page.imageUrl!!, headersBuilder().build()) - } else { // Avoid 403 errors on non-migrated mangas - super.imageRequest(page) - } - } - - override fun getGenreFilters(): Array> = arrayOf( - Pair("all", "ALL"), - Pair("action", "Action"), - Pair("adult", "Adult"), - Pair("adventure", "Adventure"), - Pair("comedy", "Comedy"), - Pair("cooking", "Cooking"), - Pair("doujinshi", "Doujinshi"), - Pair("drama", "Drama"), - Pair("ecchi", "Ecchi"), - Pair("fantasy", "Fantasy"), - Pair("gender-bender", "Gender bender"), - Pair("harem", "Harem"), - Pair("historical", "Historical"), - Pair("horror", "Horror"), - Pair("isekai", "Isekai"), - Pair("josei", "Josei"), - Pair("manhua", "Manhua"), - Pair("manhwa", "Manhwa"), - Pair("martial-arts", "Martial arts"), - Pair("mature", "Mature"), - Pair("mecha", "Mecha"), - Pair("medical", "Medical"), - Pair("mystery", "Mystery"), - Pair("one-shot", "One shot"), - Pair("psychological", "Psychological"), - Pair("romance", "Romance"), - Pair("school-life", "School life"), - Pair("sci-fi", "Sci fi"), - Pair("seinen", "Seinen"), - Pair("shoujo", "Shoujo"), - Pair("shoujo-ai", "Shoujo ai"), - Pair("shounen", "Shounen"), - Pair("shounen-ai", "Shounen ai"), - Pair("slice-of-life", "Slice of life"), - Pair("smut", "Smut"), - Pair("sports", "Sports"), - Pair("supernatural", "Supernatural"), - Pair("tragedy", "Tragedy"), - Pair("webtoons", "Webtoons"), - Pair("yaoi", "Yaoi"), - Pair("yuri", "Yuri"), - ) }