mirror of
https://github.com/keiyoushi/extensions-source.git
synced 2024-11-22 02:12:42 +01:00
MangaThemesiaAlt: random url part fixes (#2054)
* trim description * don't add anything to url if source disabled it * early extract random part when browsing * cache in preference for webview url * fix * new titles fix where no random part * bump * KingOfManga update url * bump luminous too * move preference title & summary to intl
This commit is contained in:
parent
a660ec3a42
commit
c365cd01e8
@ -28,3 +28,5 @@ genre_missing_warning=Press 'Reset' to attempt to show the genres
|
|||||||
genre_exclusion_warning=Genre exclusion is not available for all sources
|
genre_exclusion_warning=Genre exclusion is not available for all sources
|
||||||
project_filter_warning=NOTE: Can't be used with other filter!
|
project_filter_warning=NOTE: Can't be used with other filter!
|
||||||
project_filter_name=%s Project List page
|
project_filter_name=%s Project List page
|
||||||
|
pref_dynamic_url_title=Automatically update dynamic URLs
|
||||||
|
pref_dynamic_url_summary=Automatically update random numbers in manga URLs.\nHelps mitigating HTTP 404 errors during update and "in library" marks when browsing.\nNote: This setting may require clearing database in advanced settings and migrating all manga to the same source.
|
||||||
|
@ -13,6 +13,8 @@ import eu.kanade.tachiyomi.util.asJsoup
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@ -37,58 +39,141 @@ abstract class MangaThemesiaAlt(
|
|||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
SwitchPreferenceCompat(screen.context).apply {
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
key = randomUrlPrefKey
|
key = randomUrlPrefKey
|
||||||
title = "Automatically update dynamic URLs"
|
title = intl["pref_dynamic_url_title"]
|
||||||
summary = "Automatically update random numbers in manga URLs.\n" +
|
summary = intl["pref_dynamic_url_summary"]
|
||||||
"Helps mitigating HTTP 404 errors during update and \"in library\" marks when browsing.\n\n" +
|
|
||||||
"example: https://example.com/manga/12345-cool-manga -> https://example.com/manga/4567-cool-manga\n\n" +
|
|
||||||
"Note: This setting may require clearing database in advanced settings\n" +
|
|
||||||
"and migrating all manga to the same source"
|
|
||||||
setDefaultValue(true)
|
setDefaultValue(true)
|
||||||
}.also(screen::addPreference)
|
}.also(screen::addPreference)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRandomUrlPref() = preferences.getBoolean(randomUrlPrefKey, true)
|
private fun getRandomUrlPref() = preferences.getBoolean(randomUrlPrefKey, true)
|
||||||
|
|
||||||
private var randomPartCache = SuspendLazy(::updateRandomPart)
|
private var randomPartCache = SuspendLazy(::getUpdatedRandomPart) { preferences.randomPartCache = it }
|
||||||
|
|
||||||
protected open fun getRandomPart(response: Response): String {
|
// cache in preference for webview urls
|
||||||
|
private var SharedPreferences.randomPartCache: String
|
||||||
|
get() = getString("__random_part_cache", "")!!
|
||||||
|
set(newValue) = edit().putString("__random_part_cache", newValue).apply()
|
||||||
|
|
||||||
|
// some new titles don't have random part
|
||||||
|
// se we save their slug and when they
|
||||||
|
// finally add it, we remove the slug in the interceptor
|
||||||
|
private var SharedPreferences.titlesWithoutRandomPart: MutableSet<String>
|
||||||
|
get() {
|
||||||
|
val value = getString("titles_without_random_part", null)
|
||||||
|
?: return mutableSetOf()
|
||||||
|
|
||||||
|
return json.decodeFromString(value)
|
||||||
|
}
|
||||||
|
set(newValue) {
|
||||||
|
val encodedValue = json.encodeToString(newValue)
|
||||||
|
|
||||||
|
edit().putString("titles_without_random_part", encodedValue).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun getRandomPartFromUrl(url: String): String {
|
||||||
|
val slug = url
|
||||||
|
.removeSuffix("/")
|
||||||
|
.substringAfterLast("/")
|
||||||
|
|
||||||
|
return slugRegex.find(slug)?.groupValues?.get(1) ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun getRandomPartFromResponse(response: Response): String {
|
||||||
return response.asJsoup()
|
return response.asJsoup()
|
||||||
.selectFirst(searchMangaSelector())!!
|
.selectFirst(searchMangaSelector())!!
|
||||||
.select("a").attr("href")
|
.select("a").attr("href")
|
||||||
.removeSuffix("/")
|
.let(::getRandomPartFromUrl)
|
||||||
.substringAfterLast("/")
|
|
||||||
.substringBefore("-")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected suspend fun updateRandomPart() =
|
protected suspend fun getUpdatedRandomPart(): String =
|
||||||
client.newCall(GET("$baseUrl$mangaUrlDirectory/", headers))
|
client.newCall(GET("$baseUrl$mangaUrlDirectory/", headers))
|
||||||
.await()
|
.await()
|
||||||
.use(::getRandomPart)
|
.use(::getRandomPartFromResponse)
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage {
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
val mp = super.searchMangaParse(response)
|
val mp = super.searchMangaParse(response)
|
||||||
|
|
||||||
if (!getRandomUrlPref()) return mp
|
if (!getRandomUrlPref()) return mp
|
||||||
|
|
||||||
|
// extract random part during browsing to avoid extra call
|
||||||
|
mp.mangas.firstOrNull()?.run {
|
||||||
|
val randomPart = getRandomPartFromUrl(url)
|
||||||
|
|
||||||
|
if (randomPart.isNotEmpty()) {
|
||||||
|
randomPartCache.set(randomPart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val mangas = mp.mangas.toPermanentMangaUrls()
|
val mangas = mp.mangas.toPermanentMangaUrls()
|
||||||
|
|
||||||
return MangasPage(mangas, mp.hasNextPage)
|
return MangasPage(mangas, mp.hasNextPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun List<SManga>.toPermanentMangaUrls(): List<SManga> {
|
protected fun List<SManga>.toPermanentMangaUrls(): List<SManga> {
|
||||||
|
// some absolutely new titles don't have the random part yet
|
||||||
|
// save them so we know where to not apply it
|
||||||
|
val foundTitlesWithoutRandomPart = mutableSetOf<String>()
|
||||||
|
|
||||||
for (i in indices) {
|
for (i in indices) {
|
||||||
val permaSlug = this[i].url
|
val slug = this[i].url
|
||||||
.removeSuffix("/")
|
.removeSuffix("/")
|
||||||
.substringAfterLast("/")
|
.substringAfterLast("/")
|
||||||
|
|
||||||
|
if (slugRegex.find(slug)?.groupValues?.get(1) == null) {
|
||||||
|
foundTitlesWithoutRandomPart.add(slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
val permaSlug = slug
|
||||||
.replaceFirst(slugRegex, "")
|
.replaceFirst(slugRegex, "")
|
||||||
|
|
||||||
this[i].url = "$mangaUrlDirectory/$permaSlug/"
|
this[i].url = "$mangaUrlDirectory/$permaSlug/"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (foundTitlesWithoutRandomPart.isNotEmpty()) {
|
||||||
|
foundTitlesWithoutRandomPart.addAll(preferences.titlesWithoutRandomPart)
|
||||||
|
|
||||||
|
preferences.titlesWithoutRandomPart = foundTitlesWithoutRandomPart
|
||||||
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open val slugRegex = Regex("""^\d+-""")
|
protected open val slugRegex = Regex("""^(\d+-)""")
|
||||||
|
|
||||||
|
override val client = super.client.newBuilder()
|
||||||
|
.addInterceptor { chain ->
|
||||||
|
val request = chain.request()
|
||||||
|
val response = chain.proceed(request)
|
||||||
|
|
||||||
|
if (request.url.fragment != "titlesWithoutRandomPart") {
|
||||||
|
return@addInterceptor response
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.isSuccessful && response.code == 404) {
|
||||||
|
response.close()
|
||||||
|
|
||||||
|
val slug = request.url.toString()
|
||||||
|
.substringBefore("#")
|
||||||
|
.removeSuffix("/")
|
||||||
|
.substringAfterLast("/")
|
||||||
|
|
||||||
|
preferences.titlesWithoutRandomPart.run {
|
||||||
|
remove(slug)
|
||||||
|
|
||||||
|
preferences.titlesWithoutRandomPart = this
|
||||||
|
}
|
||||||
|
|
||||||
|
val randomPart = randomPartCache.blockingGet()
|
||||||
|
val newRequest = request.newBuilder()
|
||||||
|
.url("$baseUrl$mangaUrlDirectory/$randomPart$slug/")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return@addInterceptor chain.proceed(newRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
return@addInterceptor response
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
if (!getRandomUrlPref()) return super.mangaDetailsRequest(manga)
|
if (!getRandomUrlPref()) return super.mangaDetailsRequest(manga)
|
||||||
@ -99,9 +184,13 @@ abstract class MangaThemesiaAlt(
|
|||||||
.substringAfterLast("/")
|
.substringAfterLast("/")
|
||||||
.replaceFirst(slugRegex, "")
|
.replaceFirst(slugRegex, "")
|
||||||
|
|
||||||
|
if (preferences.titlesWithoutRandomPart.contains(slug)) {
|
||||||
|
return GET("$baseUrl${manga.url}#titlesWithoutRandomPart")
|
||||||
|
}
|
||||||
|
|
||||||
val randomPart = randomPartCache.blockingGet()
|
val randomPart = randomPartCache.blockingGet()
|
||||||
|
|
||||||
return GET("$baseUrl$mangaUrlDirectory/$randomPart-$slug/", headers)
|
return GET("$baseUrl$mangaUrlDirectory/$randomPart$slug/", headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga): String {
|
override fun getMangaUrl(manga: SManga): String {
|
||||||
@ -113,8 +202,11 @@ abstract class MangaThemesiaAlt(
|
|||||||
.substringAfterLast("/")
|
.substringAfterLast("/")
|
||||||
.replaceFirst(slugRegex, "")
|
.replaceFirst(slugRegex, "")
|
||||||
|
|
||||||
// we don't want to make network calls when user simply opens the entry
|
if (preferences.titlesWithoutRandomPart.contains(slug)) {
|
||||||
val randomPart = randomPartCache.peek()?.let { "$it-" } ?: ""
|
return "$baseUrl${manga.url}"
|
||||||
|
}
|
||||||
|
|
||||||
|
val randomPart = randomPartCache.peek() ?: preferences.randomPartCache
|
||||||
|
|
||||||
return "$baseUrl$mangaUrlDirectory/$randomPart$slug/"
|
return "$baseUrl$mangaUrlDirectory/$randomPart$slug/"
|
||||||
}
|
}
|
||||||
@ -122,15 +214,16 @@ abstract class MangaThemesiaAlt(
|
|||||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class SuspendLazy<T : Any>(
|
internal class SuspendLazy(
|
||||||
private val initializer: suspend () -> T,
|
private val initializer: suspend () -> String,
|
||||||
|
private val saveCache: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
private var cachedValue: SoftReference<T>? = null
|
private var cachedValue: SoftReference<String>? = null
|
||||||
private var fetchTime = 0L
|
private var fetchTime = 0L
|
||||||
|
|
||||||
suspend fun get(): T {
|
suspend fun get(): String {
|
||||||
if (fetchTime + 3600000 < System.currentTimeMillis()) {
|
if (fetchTime + 3600000 < System.currentTimeMillis()) {
|
||||||
// reset cache
|
// reset cache
|
||||||
cachedValue = null
|
cachedValue = null
|
||||||
@ -144,19 +237,23 @@ internal class SuspendLazy<T : Any>(
|
|||||||
cachedValue?.get()?.let {
|
cachedValue?.get()?.let {
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
val result = initializer()
|
|
||||||
cachedValue = SoftReference(result)
|
|
||||||
fetchTime = System.currentTimeMillis()
|
|
||||||
|
|
||||||
result
|
initializer().also { set(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun peek(): T? {
|
fun set(newVal: String) {
|
||||||
|
cachedValue = SoftReference(newVal)
|
||||||
|
fetchTime = System.currentTimeMillis()
|
||||||
|
|
||||||
|
saveCache(newVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun peek(): String? {
|
||||||
return cachedValue?.get()
|
return cachedValue?.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun blockingGet(): T {
|
fun blockingGet(): String {
|
||||||
return runBlocking { get() }
|
return runBlocking { get() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ ext {
|
|||||||
extName = 'King Of Manga'
|
extName = 'King Of Manga'
|
||||||
extClass = '.KingOfManga'
|
extClass = '.KingOfManga'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://kingofmanga.com'
|
baseUrl = 'https://king-ofmanga.com'
|
||||||
overrideVersionCode = 3
|
overrideVersionCode = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -6,7 +6,7 @@ import java.util.Locale
|
|||||||
|
|
||||||
class KingOfManga : MangaThemesiaAlt(
|
class KingOfManga : MangaThemesiaAlt(
|
||||||
"King Of Manga",
|
"King Of Manga",
|
||||||
"https://kingofmanga.com",
|
"https://king-ofmanga.com",
|
||||||
"ar",
|
"ar",
|
||||||
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")),
|
||||||
) {
|
) {
|
||||||
|
@ -3,7 +3,7 @@ ext {
|
|||||||
extClass = '.AsuraScans'
|
extClass = '.AsuraScans'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://asuratoon.com'
|
baseUrl = 'https://asuratoon.com'
|
||||||
overrideVersionCode = 2
|
overrideVersionCode = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -3,7 +3,7 @@ ext {
|
|||||||
extClass = '.LuminousScans'
|
extClass = '.LuminousScans'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://lumitoon.com'
|
baseUrl = 'https://lumitoon.com'
|
||||||
overrideVersionCode = 4
|
overrideVersionCode = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -3,7 +3,7 @@ ext {
|
|||||||
extClass = '.RizzComic'
|
extClass = '.RizzComic'
|
||||||
themePkg = 'mangathemesia'
|
themePkg = 'mangathemesia'
|
||||||
baseUrl = 'https://rizzcomic.com'
|
baseUrl = 'https://rizzcomic.com'
|
||||||
overrideVersionCode = 1
|
overrideVersionCode = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -41,7 +41,7 @@ class RizzComic : MangaThemesiaAlt(
|
|||||||
|
|
||||||
override val versionId = 2
|
override val versionId = 2
|
||||||
|
|
||||||
override val slugRegex = Regex("""^r\d+-""")
|
override val slugRegex = Regex("""^(r\d+-)""")
|
||||||
|
|
||||||
// don't allow disabling random part setting
|
// don't allow disabling random part setting
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) { }
|
override fun setupPreferenceScreen(screen: PreferenceScreen) { }
|
||||||
|
Loading…
Reference in New Issue
Block a user