From 89433cd894ef80fc7f97ecfe5247d79be815b7b9 Mon Sep 17 00:00:00 2001 From: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:33:45 +0500 Subject: [PATCH] AS: library badge workaround (#16858) * AS: workaround for "in library" mark * fix --- .../src/{AsuraScans.kt => AsuraScansEn.kt} | 172 ++++++++++++------ .../asurascans/src/AsuraScansFactory.kt | 51 ------ .../asurascans/src/AsuraScansTr.kt | 69 +++++++ .../mangathemesia/MangaThemesiaGenerator.kt | 2 +- 4 files changed, 186 insertions(+), 108 deletions(-) rename multisrc/overrides/mangathemesia/asurascans/src/{AsuraScans.kt => AsuraScansEn.kt} (51%) create mode 100644 multisrc/overrides/mangathemesia/asurascans/src/AsuraScansTr.kt diff --git a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScans.kt b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansEn.kt similarity index 51% rename from multisrc/overrides/mangathemesia/asurascans/src/AsuraScans.kt rename to multisrc/overrides/mangathemesia/asurascans/src/AsuraScansEn.kt index d60b61cd5..d8bacc6fc 100644 --- a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScans.kt +++ b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansEn.kt @@ -1,9 +1,13 @@ package eu.kanade.tachiyomi.extension.all.asurascans import android.app.Application +import android.content.SharedPreferences +import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga @@ -11,7 +15,6 @@ import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import okhttp3.Interceptor import okhttp3.OkHttpClient -import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element @@ -20,18 +23,16 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.IOException import java.text.SimpleDateFormat +import java.util.Locale import java.util.concurrent.TimeUnit -open class AsuraScans( - override val baseUrl: String, - override val lang: String, - dateFormat: SimpleDateFormat, -) : MangaThemesia( +class AsuraScansEn : MangaThemesia( "Asura Scans", - baseUrl, - lang, - dateFormat = dateFormat, + "https://www.asurascans.com", + "en", + dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US), ) { + private val preferences = Injekt.get().getSharedPreferences("source_$id", 0x0000) override val client: OkHttpClient = network.cloudflareClient.newBuilder() @@ -42,35 +43,41 @@ open class AsuraScans( .rateLimit(1, 3, TimeUnit.SECONDS) .build() + override val seriesDescriptionSelector = "div.desc p, div.entry-content p, div[itemprop=description]:not(:has(p))" + + override val pageSelector = "div.rdminimal > img, div.rdminimal > p > img, div.rdminimal > a > img, div.rdminimal > p > a > img, " + + "div.rdminimal > noscript > img, div.rdminimal > p > noscript > img, div.rdminimal > a > noscript > img, div.rdminimal > p > a > noscript > img" + + // Permanent Url for Manga/Chapter End + override fun fetchPopularManga(page: Int): Observable { + return super.fetchPopularManga(page).tempUrlToPermIfNeeded() + } + + override fun fetchLatestUpdates(page: Int): Observable { + return super.fetchLatestUpdates(page).tempUrlToPermIfNeeded() + } + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + return super.fetchSearchManga(page, query, filters).tempUrlToPermIfNeeded() + } + + // Temp Url for manga/chapter override fun fetchChapterList(manga: SManga): Observable> { - val newManga = manga.apply { - url = "$url#chapters#${title.toSearchQuery()}" - } + val newManga = manga.permUrlToTemp() + return super.fetchChapterList(newManga) } override fun fetchMangaDetails(manga: SManga): Observable { - val newManga = try { - manga.apply { - url = "$url#details#${title.toSearchQuery()}" - } - } catch (e: UninitializedPropertyAccessException) { - // when called from deep link, title is not present - manga - } + val newManga = manga.permUrlToTemp() return super.fetchMangaDetails(newManga) } - // use updated url for webView override fun getMangaUrl(manga: SManga): String { - val dbSlug = manga.url - .removeSuffix("/") - .substringAfterLast("/") + val newManga = manga.permUrlToTemp() - val storedSlug = getSlugMap()[dbSlug] ?: dbSlug - - return "$baseUrl$mangaUrlDirectory/$storedSlug/" + return baseUrl + newManga.url } // Skip scriptPages @@ -87,38 +94,86 @@ open class AsuraScans( else -> attr("abs:src") } - private fun urlChangeInterceptor(chain: Interceptor.Chain): Response { - val request = chain.request() - val frag = request.url.fragment - - if (frag.isNullOrEmpty()) { - return chain.proceed(request) + private fun Observable.tempUrlToPermIfNeeded(): Observable { + return this.map { mangasPage -> + MangasPage( + mangasPage.mangas.map { it.tempUrlToPermIfNeeded() }, + mangasPage.hasNextPage, + ) } + } - val search = frag.substringAfter("#") - - val dbSlug = request.url.toString() - .substringBefore("#") - .removeSuffix("/") - .substringAfterLast("/") + private fun SManga.tempUrlToPermIfNeeded(): SManga { + if (!preferences.permaUrlPref) return this val slugMap = getSlugMap().toMutableMap() - // make sure db slug key is present in the slugMap - val storedSlug = slugMap[dbSlug] ?: dbSlug + val sMangaTitleFirstWord = this.title.split(" ")[0] + if (!this.url.contains("/$sMangaTitleFirstWord", ignoreCase = true)) { + val currentSlug = this.url + .removeSuffix("/") + .substringAfterLast("/") - val response = chain.proceed(newRequest(frag, storedSlug)) + val permaSlug = currentSlug.replaceFirst(TEMP_TO_PERM_REGEX, "") + + slugMap[permaSlug] = currentSlug + + this.url = "$mangaUrlDirectory/$permaSlug/" + } + putSlugMap(slugMap) + return this + } + + private fun SManga.permUrlToTemp(): SManga { + return try { + val dbSlug = this.url + .removeSuffix("/") + .substringAfterLast("/") + + val storedSlug = getSlugMap()[dbSlug] ?: dbSlug + + this.apply { + url = "$mangaUrlDirectory/$storedSlug/#${title.toSearchQuery()}" + } + } catch (e: UninitializedPropertyAccessException) { + // when called from deep link, title is not present + this + } + } + + private fun urlChangeInterceptor(chain: Interceptor.Chain): Response { + val request = chain.request() + val response = chain.proceed(request) + + val frag = request.url.fragment + + if (frag.isNullOrEmpty()) { + return response + } if (!response.isSuccessful && response.code == 404) { response.close() - val newSlug = getNewSlug(storedSlug, search) + val dbSlug = request.url.toString() + .substringBefore("#") + .removeSuffix("/") + .substringAfterLast("/") + + val slugMap = getSlugMap().toMutableMap() + + val storedSlug = slugMap[dbSlug] ?: dbSlug + + val newSlug = getNewSlug(storedSlug, frag) ?: throw IOException("Migrate from Asura to Asura") slugMap[dbSlug] = newSlug putSlugMap(slugMap) - return chain.proceed(newRequest(frag, newSlug)) + return chain.proceed( + request.newBuilder() + .url("$baseUrl$mangaUrlDirectory/$newSlug/") + .build(), + ) } return response @@ -142,18 +197,6 @@ open class AsuraScans( ?.substringAfterLast("/") } - private fun newRequest(frag: String, slug: String): Request { - val manga = SManga.create().apply { - this.url = "$mangaUrlDirectory/$slug/" - } - - return when (frag.substringBefore("#")) { - "chapters" -> chapterListRequest(manga) - "details" -> mangaDetailsRequest(manga) - else -> throw IOException("unknown url fragment for urlChangeInterceptor") - } - } - private fun putSlugMap(slugMap: MutableMap) { val serialized = json.encodeToString(slugMap) @@ -177,7 +220,24 @@ open class AsuraScans( .replace(trailingPlusRegex, "") } + override fun setupPreferenceScreen(screen: PreferenceScreen) { + SwitchPreferenceCompat(screen.context).apply { + key = PREF_PERM_MANGA_URL_KEY_PREFIX + lang + title = PREF_PERM_MANGA_URL_TITLE + summary = PREF_PERM_MANGA_URL_SUMMARY + setDefaultValue(true) + }.also(screen::addPreference) + + addRandomAndCustomUserAgentPreferences(screen) + } + + private val SharedPreferences.permaUrlPref + get() = getBoolean(PREF_PERM_MANGA_URL_KEY_PREFIX + lang, true) + companion object { + private const val PREF_PERM_MANGA_URL_KEY_PREFIX = "pref_permanent_manga_url_2_" + private const val PREF_PERM_MANGA_URL_TITLE = "Permanent Manga URL" + private const val PREF_PERM_MANGA_URL_SUMMARY = "Turns all manga urls into permanent ones." private const val PREF_URL_MAP = "pref_url_map" private val TEMP_TO_PERM_REGEX = Regex("""^\d+-""") private val titleSpecialCharactersRegex = Regex("""[^a-z0-9]+""") diff --git a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansFactory.kt b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansFactory.kt index 70feef773..47dc509bf 100644 --- a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansFactory.kt +++ b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansFactory.kt @@ -1,13 +1,6 @@ package eu.kanade.tachiyomi.extension.all.asurascans import eu.kanade.tachiyomi.source.SourceFactory -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SManga -import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString -import org.jsoup.nodes.Document -import java.text.SimpleDateFormat -import java.util.Locale class AsuraScansFactory : SourceFactory { override fun createSources() = listOf( @@ -15,47 +8,3 @@ class AsuraScansFactory : SourceFactory { AsuraScansTr(), ) } - -class AsuraScansEn : AsuraScans("https://www.asurascans.com", "en", SimpleDateFormat("MMM d, yyyy", Locale.US)) { - - override val seriesDescriptionSelector = "div.desc p, div.entry-content p, div[itemprop=description]:not(:has(p))" - - override val pageSelector = "div.rdminimal > img, div.rdminimal > p > img, div.rdminimal > a > img, div.rdminimal > p > a > img, " + - "div.rdminimal > noscript > img, div.rdminimal > p > noscript > img, div.rdminimal > a > noscript > img, div.rdminimal > p > a > noscript > img" -} - -class AsuraScansTr : AsuraScans("https://asurascanstr.com", "tr", SimpleDateFormat("MMM d, yyyy", Locale("tr"))) { - - override val seriesArtistSelector = ".fmed b:contains(Çizer)+span" - override val seriesAuthorSelector = ".fmed b:contains(Yazar)+span" - override val seriesStatusSelector = ".imptdt:contains(Durum) i" - override val seriesTypeSelector = ".imptdt:contains(Tür) a" - - override val altNamePrefix: String = "Alternatif isim: " - - override fun String?.parseStatus(): Int = when { - this == null -> SManga.UNKNOWN - this.contains("Devam Ediyor", ignoreCase = true) -> SManga.ONGOING - this.contains("Tamamlandı", ignoreCase = true) -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - - override fun pageListParse(document: Document): List { - val scriptContent = document.selectFirst("script:containsData(ts_reader)")!!.data() - val jsonString = scriptContent.substringAfter("ts_reader.run(").substringBefore(");") - val tsReader = json.decodeFromString(jsonString) - val imageUrls = tsReader.sources.firstOrNull()?.images ?: return emptyList() - return imageUrls.mapIndexed { index, imageUrl -> Page(index, imageUrl = imageUrl) } - } - - @Serializable - data class TSReader( - val sources: List, - ) - - @Serializable - data class ReaderImageSource( - val source: String, - val images: List, - ) -} diff --git a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansTr.kt b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansTr.kt new file mode 100644 index 000000000..433569bf3 --- /dev/null +++ b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansTr.kt @@ -0,0 +1,69 @@ +package eu.kanade.tachiyomi.extension.all.asurascans + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import okhttp3.OkHttpClient +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class AsuraScansTr : MangaThemesia( + "Asura Scans", + "https://asurascanstr.com", + "tr", + dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr")), +) { + override val client: OkHttpClient = network.cloudflareClient.newBuilder() + .addInterceptor(uaIntercept) + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .rateLimit(1, 3, TimeUnit.SECONDS) + .build() + + override val seriesArtistSelector = ".fmed b:contains(Çizer)+span" + override val seriesAuthorSelector = ".fmed b:contains(Yazar)+span" + override val seriesStatusSelector = ".imptdt:contains(Durum) i" + override val seriesTypeSelector = ".imptdt:contains(Tür) a" + + override val altNamePrefix: String = "Alternatif isim: " + + override fun String?.parseStatus(): Int = when { + this == null -> SManga.UNKNOWN + this.contains("Devam Ediyor", ignoreCase = true) -> SManga.ONGOING + this.contains("Tamamlandı", ignoreCase = true) -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + + override fun Element.imgAttr(): String = when { + hasAttr("data-lazy-src") -> attr("abs:data-lazy-src") + hasAttr("data-src") -> attr("abs:data-src") + hasAttr("data-cfsrc") -> attr("abs:data-cfsrc") + else -> attr("abs:src") + } + + override fun pageListParse(document: Document): List { + val scriptContent = document.selectFirst("script:containsData(ts_reader)")?.data() + ?: return super.pageListParse(document) + val jsonString = scriptContent.substringAfter("ts_reader.run(").substringBefore(");") + val tsReader = json.decodeFromString(jsonString) + val imageUrls = tsReader.sources.firstOrNull()?.images ?: return emptyList() + return imageUrls.mapIndexed { index, imageUrl -> Page(index, imageUrl = imageUrl) } + } + + @Serializable + data class TSReader( + val sources: List, + ) + + @Serializable + data class ReaderImageSource( + val source: String, + val images: List, + ) +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesiaGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesiaGenerator.kt index 7f7dc8198..2a63ce18f 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesiaGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesiaGenerator.kt @@ -14,7 +14,7 @@ class MangaThemesiaGenerator : ThemeSourceGenerator { override val baseVersionCode: Int = 25 override val sources = listOf( - MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 21), + MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 22), MultiLang("Flame Scans", "https://flamescans.org", listOf("en"), className = "FlameScansFactory", pkgName = "flamescans", overrideVersionCode = 4), MultiLang("Komik Lab", "https://komiklab.com", listOf("en", "id"), className = "KomikLabFactory", pkgName = "komiklab", overrideVersionCode = 2), MultiLang("Miau Scan", "https://miauscan.com", listOf("es", "pt-BR")),