mirror of
https://github.com/keiyoushi/extensions-source.git
synced 2024-11-22 02:12:42 +01:00
MangaThemesia: add class to handle dynamic urls in sources (#1793)
* MangaThemesia: add alternative class to handle dynamic urls * use MangaThemesiaAlt on Asura & Luminous * use MangaThemesiaAlt on Rizz * don't update in getMangaUrl * small cleanup * remove other pref as well LuminousScans * wording * remove from FlameComics, since they no longer appear to do it * review comments * lint * actual old pref key Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com> * actual old pref key x2 Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com> --------- Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com>
This commit is contained in:
parent
cc6e67f0e2
commit
d3f33327c2
@ -0,0 +1,162 @@
|
||||
package eu.kanade.tachiyomi.multisrc.mangathemesia
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.lang.ref.SoftReference
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
abstract class MangaThemesiaAlt(
|
||||
name: String,
|
||||
baseUrl: String,
|
||||
lang: String,
|
||||
mangaUrlDirectory: String = "/manga",
|
||||
dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US),
|
||||
private val randomUrlPrefKey: String = "pref_auto_random_url",
|
||||
) : MangaThemesia(name, baseUrl, lang, mangaUrlDirectory, dateFormat), ConfigurableSource {
|
||||
|
||||
protected val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
SwitchPreferenceCompat(screen.context).apply {
|
||||
key = randomUrlPrefKey
|
||||
title = "Automatically update dynamic URLs"
|
||||
summary = "Automatically update random numbers in manga URLs.\n" +
|
||||
"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)
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
|
||||
private fun getRandomUrlPref() = preferences.getBoolean(randomUrlPrefKey, true)
|
||||
|
||||
private var randomPartCache = SuspendLazy(::updateRandomPart)
|
||||
|
||||
protected open fun getRandomPart(response: Response): String {
|
||||
return response.asJsoup()
|
||||
.selectFirst(searchMangaSelector())!!
|
||||
.select("a").attr("href")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
.substringBefore("-")
|
||||
}
|
||||
|
||||
protected suspend fun updateRandomPart() =
|
||||
client.newCall(GET("$baseUrl$mangaUrlDirectory/", headers))
|
||||
.await()
|
||||
.use(::getRandomPart)
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val mp = super.searchMangaParse(response)
|
||||
|
||||
if (!getRandomUrlPref()) return mp
|
||||
|
||||
val mangas = mp.mangas.toPermanentMangaUrls()
|
||||
|
||||
return MangasPage(mangas, mp.hasNextPage)
|
||||
}
|
||||
|
||||
protected fun List<SManga>.toPermanentMangaUrls(): List<SManga> {
|
||||
for (i in indices) {
|
||||
val permaSlug = this[i].url
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
.replaceFirst(slugRegex, "")
|
||||
|
||||
this[i].url = "$mangaUrlDirectory/$permaSlug/"
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
protected open val slugRegex = Regex("""^\d+-""")
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
if (!getRandomUrlPref()) return super.mangaDetailsRequest(manga)
|
||||
|
||||
val slug = manga.url
|
||||
.substringBefore("#")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
.replaceFirst(slugRegex, "")
|
||||
|
||||
val randomPart = randomPartCache.blockingGet()
|
||||
|
||||
return GET("$baseUrl$mangaUrlDirectory/$randomPart-$slug/", headers)
|
||||
}
|
||||
|
||||
override fun getMangaUrl(manga: SManga): String {
|
||||
if (!getRandomUrlPref()) return super.getMangaUrl(manga)
|
||||
|
||||
val slug = manga.url
|
||||
.substringBefore("#")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
.replaceFirst(slugRegex, "")
|
||||
|
||||
// we don't want to make network calls when user simply opens the entry
|
||||
val randomPart = randomPartCache.peek()?.let { "$it-" } ?: ""
|
||||
|
||||
return "$baseUrl$mangaUrlDirectory/$randomPart$slug/"
|
||||
}
|
||||
|
||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
||||
}
|
||||
|
||||
internal class SuspendLazy<T : Any>(
|
||||
private val initializer: suspend () -> T,
|
||||
) {
|
||||
|
||||
private val mutex = Mutex()
|
||||
private var cachedValue: SoftReference<T>? = null
|
||||
private var fetchTime = 0L
|
||||
|
||||
suspend fun get(): T {
|
||||
if (fetchTime + 3600000 < System.currentTimeMillis()) {
|
||||
// reset cache
|
||||
cachedValue = null
|
||||
}
|
||||
|
||||
// fast way
|
||||
cachedValue?.get()?.let {
|
||||
return it
|
||||
}
|
||||
return mutex.withLock {
|
||||
cachedValue?.get()?.let {
|
||||
return it
|
||||
}
|
||||
val result = initializer()
|
||||
cachedValue = SoftReference(result)
|
||||
fetchTime = System.currentTimeMillis()
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fun peek(): T? {
|
||||
return cachedValue?.get()
|
||||
}
|
||||
|
||||
fun blockingGet(): T {
|
||||
return runBlocking { get() }
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ ext {
|
||||
extClass = '.AsuraScans'
|
||||
themePkg = 'mangathemesia'
|
||||
baseUrl = 'https://asuratoon.com'
|
||||
overrideVersionCode = 1
|
||||
overrideVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -1,53 +1,35 @@
|
||||
package eu.kanade.tachiyomi.extension.en.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.multisrc.mangathemesia.MangaThemesiaAlt
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
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
|
||||
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 rx.Observable
|
||||
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
|
||||
|
||||
class AsuraScans :
|
||||
MangaThemesia(
|
||||
"Asura Scans",
|
||||
"https://asuratoon.com",
|
||||
"en",
|
||||
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US),
|
||||
),
|
||||
ConfigurableSource {
|
||||
|
||||
private val preferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
class AsuraScans : MangaThemesiaAlt(
|
||||
"Asura Scans",
|
||||
"https://asuratoon.com",
|
||||
"en",
|
||||
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US),
|
||||
randomUrlPrefKey = "pref_permanent_manga_url_2_en",
|
||||
) {
|
||||
init {
|
||||
// remove legacy preferences
|
||||
preferences.run {
|
||||
if (contains("pref_url_map")) {
|
||||
edit().remove("pref_url_map").apply()
|
||||
}
|
||||
if (contains("pref_base_url_host")) {
|
||||
edit().remove("pref_base_url_host").apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val baseUrl by lazy {
|
||||
preferences.baseUrlHost.let { "https://$it" }
|
||||
}
|
||||
|
||||
override val client: OkHttpClient = super.client.newBuilder()
|
||||
.addInterceptor(::urlChangeInterceptor)
|
||||
.addInterceptor(::domainChangeIntercept)
|
||||
.rateLimit(1, 3, TimeUnit.SECONDS)
|
||||
override val client = super.client.newBuilder()
|
||||
.rateLimit(1, 3)
|
||||
.apply {
|
||||
val interceptors = interceptors()
|
||||
val index = interceptors.indexOfFirst { "Brotli" in it.javaClass.simpleName }
|
||||
@ -64,19 +46,6 @@ class AsuraScans :
|
||||
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<MangasPage> {
|
||||
return super.fetchPopularManga(page).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||
return super.fetchLatestUpdates(page).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return super.fetchSearchManga(page, query, filters).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val request = super.searchMangaRequest(page, query, filters)
|
||||
if (query.isBlank()) return request
|
||||
@ -93,232 +62,10 @@ class AsuraScans :
|
||||
.build()
|
||||
}
|
||||
|
||||
// Temp Url for manga/chapter
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val newManga = manga.titleToUrlFrag()
|
||||
|
||||
return super.fetchChapterList(newManga)
|
||||
}
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
val newManga = manga.titleToUrlFrag()
|
||||
|
||||
return super.fetchMangaDetails(newManga)
|
||||
}
|
||||
|
||||
override fun getMangaUrl(manga: SManga): String {
|
||||
val dbSlug = manga.url
|
||||
.substringBefore("#")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val storedSlug = preferences.slugMap[dbSlug] ?: dbSlug
|
||||
|
||||
return "$baseUrl$mangaUrlDirectory/$storedSlug/"
|
||||
}
|
||||
|
||||
// Skip scriptPages
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
return document.select(pageSelector)
|
||||
.filterNot { it.attr("src").isNullOrEmpty() }
|
||||
.mapIndexed { i, img -> Page(i, document.location(), img.attr("abs:src")) }
|
||||
}
|
||||
|
||||
private fun Observable<MangasPage>.tempUrlToPermIfNeeded(): Observable<MangasPage> {
|
||||
return this.map { mangasPage ->
|
||||
MangasPage(
|
||||
mangasPage.mangas.map { it.tempUrlToPermIfNeeded() },
|
||||
mangasPage.hasNextPage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SManga.tempUrlToPermIfNeeded(): SManga {
|
||||
if (!preferences.permaUrlPref) return this
|
||||
|
||||
val slugMap = preferences.slugMap
|
||||
|
||||
val sMangaTitleFirstWord = this.title.split(" ")[0]
|
||||
if (!this.url.contains("/$sMangaTitleFirstWord", ignoreCase = true)) {
|
||||
val currentSlug = this.url
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val permaSlug = currentSlug.replaceFirst(TEMP_TO_PERM_REGEX, "")
|
||||
|
||||
slugMap[permaSlug] = currentSlug
|
||||
|
||||
this.url = "$mangaUrlDirectory/$permaSlug/"
|
||||
}
|
||||
preferences.slugMap = slugMap
|
||||
return this
|
||||
}
|
||||
|
||||
private fun SManga.titleToUrlFrag(): SManga {
|
||||
return try {
|
||||
this.apply {
|
||||
url = "$url#${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 frag = request.url.fragment
|
||||
|
||||
if (frag.isNullOrEmpty()) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
val dbSlug = request.url.toString()
|
||||
.substringBefore("#")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val slugMap = preferences.slugMap
|
||||
|
||||
val storedSlug = slugMap[dbSlug] ?: dbSlug
|
||||
|
||||
val response = chain.proceed(
|
||||
request.newBuilder()
|
||||
.url("$baseUrl$mangaUrlDirectory/$storedSlug/")
|
||||
.build(),
|
||||
)
|
||||
|
||||
if (!response.isSuccessful && response.code == 404) {
|
||||
response.close()
|
||||
|
||||
val newSlug = getNewSlug(storedSlug, frag)
|
||||
?: throw IOException("Migrate from Asura to Asura")
|
||||
|
||||
slugMap[dbSlug] = newSlug
|
||||
preferences.slugMap = slugMap
|
||||
|
||||
return chain.proceed(
|
||||
request.newBuilder()
|
||||
.url("$baseUrl$mangaUrlDirectory/$newSlug/")
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
private fun getNewSlug(existingSlug: String, frag: String): String? {
|
||||
val permaSlug = existingSlug
|
||||
.replaceFirst(TEMP_TO_PERM_REGEX, "")
|
||||
|
||||
val search = frag.substringBefore("#")
|
||||
|
||||
val mangas = client.newCall(searchMangaRequest(1, search, FilterList()))
|
||||
.execute()
|
||||
.use {
|
||||
searchMangaParse(it)
|
||||
}
|
||||
|
||||
return mangas.mangas.firstOrNull { newManga ->
|
||||
newManga.url.contains(permaSlug, true)
|
||||
}
|
||||
?.url
|
||||
?.removeSuffix("/")
|
||||
?.substringAfterLast("/")
|
||||
}
|
||||
|
||||
private fun String.toSearchQuery(): String {
|
||||
return this.trim()
|
||||
.lowercase()
|
||||
.replace(titleSpecialCharactersRegex, "+")
|
||||
.replace(trailingPlusRegex, "")
|
||||
}
|
||||
|
||||
private var lastDomain = ""
|
||||
|
||||
private fun domainChangeIntercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
|
||||
if (request.url.host !in listOf(preferences.baseUrlHost, lastDomain)) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
if (lastDomain.isNotEmpty()) {
|
||||
val newUrl = request.url.newBuilder()
|
||||
.host(preferences.baseUrlHost)
|
||||
.build()
|
||||
|
||||
return chain.proceed(
|
||||
request.newBuilder()
|
||||
.url(newUrl)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
val response = chain.proceed(request)
|
||||
|
||||
if (request.url.host == response.request.url.host) return response
|
||||
|
||||
response.close()
|
||||
|
||||
preferences.baseUrlHost = response.request.url.host
|
||||
|
||||
lastDomain = request.url.host
|
||||
|
||||
val newUrl = request.url.newBuilder()
|
||||
.host(response.request.url.host)
|
||||
.build()
|
||||
|
||||
return chain.proceed(
|
||||
request.newBuilder()
|
||||
.url(newUrl)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
private val SharedPreferences.permaUrlPref
|
||||
get() = getBoolean(PREF_PERM_MANGA_URL_KEY_PREFIX + lang, true)
|
||||
|
||||
private var SharedPreferences.slugMap: MutableMap<String, String>
|
||||
get() {
|
||||
val serialized = getString(PREF_URL_MAP, null) ?: return mutableMapOf()
|
||||
|
||||
return try {
|
||||
json.decodeFromString(serialized)
|
||||
} catch (e: Exception) {
|
||||
mutableMapOf()
|
||||
}
|
||||
}
|
||||
set(slugMap) {
|
||||
val serialized = json.encodeToString(slugMap)
|
||||
edit().putString(PREF_URL_MAP, serialized).commit()
|
||||
}
|
||||
|
||||
private var SharedPreferences.baseUrlHost
|
||||
get() = getString(BASE_URL_PREF, defaultBaseUrlHost) ?: defaultBaseUrlHost
|
||||
set(newHost) {
|
||||
edit().putString(BASE_URL_PREF, newHost).commit()
|
||||
}
|
||||
|
||||
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 const val BASE_URL_PREF = "pref_base_url_host"
|
||||
private const val defaultBaseUrlHost = "asuratoon.com"
|
||||
private val TEMP_TO_PERM_REGEX = Regex("""^\d+-""")
|
||||
private val titleSpecialCharactersRegex = Regex("""[^a-z0-9]+""")
|
||||
private val trailingPlusRegex = Regex("""\++$""")
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ ext {
|
||||
extClass = '.FlameComics'
|
||||
themePkg = 'mangathemesia'
|
||||
baseUrl = 'https://flamecomics.com'
|
||||
overrideVersionCode = 0
|
||||
overrideVersionCode = 1
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -1,47 +1,30 @@
|
||||
package eu.kanade.tachiyomi.extension.en.flamecomics
|
||||
|
||||
import android.app.Application
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
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.ConfigurableSource
|
||||
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
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.Protocol
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import org.jsoup.nodes.Document
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
class FlameComics :
|
||||
MangaThemesia(
|
||||
"Flame Comics",
|
||||
"https://flamecomics.com",
|
||||
"en",
|
||||
mangaUrlDirectory = "/series",
|
||||
),
|
||||
ConfigurableSource {
|
||||
class FlameComics : MangaThemesia(
|
||||
"Flame Comics",
|
||||
"https://flamecomics.com",
|
||||
"en",
|
||||
mangaUrlDirectory = "/series",
|
||||
) {
|
||||
|
||||
// Flame Scans -> Flame Comics
|
||||
override val id = 6350607071566689772
|
||||
|
||||
private val preferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override val client = super.client.newBuilder()
|
||||
.rateLimit(2, 7)
|
||||
.addInterceptor(::composedImageIntercept)
|
||||
@ -130,114 +113,8 @@ class FlameComics :
|
||||
}
|
||||
// Split Image Fixer End
|
||||
|
||||
// Permanent Url start
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
return super.fetchPopularManga(page).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||
return super.fetchLatestUpdates(page).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return super.fetchSearchManga(page, query, filters).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
private fun Observable<MangasPage>.tempUrlToPermIfNeeded(): Observable<MangasPage> {
|
||||
return this.map { mangasPage ->
|
||||
MangasPage(
|
||||
mangasPage.mangas.map { it.tempUrlToPermIfNeeded() },
|
||||
mangasPage.hasNextPage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SManga.tempUrlToPermIfNeeded(): SManga {
|
||||
val turnTempUrlToPerm = preferences.getBoolean(getPermanentMangaUrlPreferenceKey(), true)
|
||||
if (!turnTempUrlToPerm) return this
|
||||
|
||||
val path = this.url.removePrefix("/").removeSuffix("/").split("/")
|
||||
path.lastOrNull()?.let { slug -> this.url = "$mangaUrlDirectory/${deobfuscateSlug(slug)}/" }
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga) = super.fetchChapterList(manga.tempUrlToPermIfNeeded())
|
||||
.map { sChapterList -> sChapterList.map { it.tempUrlToPermIfNeeded() } }
|
||||
|
||||
private fun SChapter.tempUrlToPermIfNeeded(): SChapter {
|
||||
val turnTempUrlToPerm = preferences.getBoolean(getPermanentChapterUrlPreferenceKey(), true)
|
||||
if (!turnTempUrlToPerm) return this
|
||||
|
||||
val path = this.url.removePrefix("/").removeSuffix("/").split("/")
|
||||
path.lastOrNull()?.let { slug -> this.url = "/${deobfuscateSlug(slug)}/" }
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
val permanentMangaUrlPref = SwitchPreferenceCompat(screen.context).apply {
|
||||
key = getPermanentMangaUrlPreferenceKey()
|
||||
title = PREF_PERM_MANGA_URL_TITLE
|
||||
summary = PREF_PERM_MANGA_URL_SUMMARY
|
||||
setDefaultValue(true)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit()
|
||||
.putBoolean(getPermanentMangaUrlPreferenceKey(), checkValue)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
val permanentChapterUrlPref = SwitchPreferenceCompat(screen.context).apply {
|
||||
key = getPermanentChapterUrlPreferenceKey()
|
||||
title = PREF_PERM_CHAPTER_URL_TITLE
|
||||
summary = PREF_PERM_CHAPTER_URL_SUMMARY
|
||||
setDefaultValue(true)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit()
|
||||
.putBoolean(getPermanentChapterUrlPreferenceKey(), checkValue)
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
screen.addPreference(permanentMangaUrlPref)
|
||||
screen.addPreference(permanentChapterUrlPref)
|
||||
}
|
||||
|
||||
private fun getPermanentMangaUrlPreferenceKey(): String {
|
||||
return PREF_PERM_MANGA_URL_KEY_PREFIX + lang
|
||||
}
|
||||
|
||||
private fun getPermanentChapterUrlPreferenceKey(): String {
|
||||
return PREF_PERM_CHAPTER_URL_KEY_PREFIX + lang
|
||||
}
|
||||
// Permanent Url for Manga/Chapter End
|
||||
|
||||
companion object {
|
||||
private const val COMPOSED_SUFFIX = "?comp"
|
||||
|
||||
private const val PREF_PERM_MANGA_URL_KEY_PREFIX = "pref_permanent_manga_url_"
|
||||
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_PERM_CHAPTER_URL_KEY_PREFIX = "pref_permanent_chapter_url"
|
||||
private const val PREF_PERM_CHAPTER_URL_TITLE = "Permanent Chapter URL"
|
||||
private const val PREF_PERM_CHAPTER_URL_SUMMARY = "Turns all chapter urls into permanent ones."
|
||||
|
||||
/**
|
||||
*
|
||||
* De-obfuscates the slug of a series or chapter to the permanent slug
|
||||
* * For a series: "12345678-this-is-a-series" -> "this-is-a-series"
|
||||
* * For a chapter: "12345678-this-is-a-series-chapter-1" -> "this-is-a-series-chapter-1"
|
||||
*
|
||||
* @param obfuscated_slug the obfuscated slug of a series or chapter
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private fun deobfuscateSlug(obfuscated_slug: String) = obfuscated_slug
|
||||
.replaceFirst(Regex("""^\d+-"""), "")
|
||||
|
||||
private val MEDIA_TYPE = "image/png".toMediaType()
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ ext {
|
||||
extClass = '.LuminousScans'
|
||||
themePkg = 'mangathemesia'
|
||||
baseUrl = 'https://lumitoon.com'
|
||||
overrideVersionCode = 3
|
||||
overrideVersionCode = 4
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -1,57 +1,30 @@
|
||||
package eu.kanade.tachiyomi.extension.en.luminousscans
|
||||
|
||||
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.multisrc.mangathemesia.MangaThemesiaAlt
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.IOException
|
||||
|
||||
class LuminousScans :
|
||||
MangaThemesia(
|
||||
"Luminous Scans",
|
||||
"https://lumitoon.com",
|
||||
"en",
|
||||
mangaUrlDirectory = "/series",
|
||||
),
|
||||
ConfigurableSource {
|
||||
class LuminousScans : MangaThemesiaAlt(
|
||||
"Luminous Scans",
|
||||
"https://lumitoon.com",
|
||||
"en",
|
||||
mangaUrlDirectory = "/series",
|
||||
randomUrlPrefKey = "pref_permanent_manga_url_2_en",
|
||||
) {
|
||||
init {
|
||||
// remove legacy preferences
|
||||
preferences.run {
|
||||
if (contains("pref_url_map")) {
|
||||
edit().remove("pref_url_map").apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val client = super.client.newBuilder()
|
||||
.addInterceptor(::urlChangeInterceptor)
|
||||
.rateLimit(2)
|
||||
.build()
|
||||
|
||||
private val preferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
// Permanent Url for Manga/Chapter End
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
return super.fetchPopularManga(page).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||
return super.fetchLatestUpdates(page).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return super.fetchSearchManga(page, query, filters).tempUrlToPermIfNeeded()
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val request = super.searchMangaRequest(page, query, filters)
|
||||
if (query.isBlank()) return request
|
||||
@ -67,176 +40,4 @@ class LuminousScans :
|
||||
.url(url)
|
||||
.build()
|
||||
}
|
||||
|
||||
// Temp Url for manga/chapter
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val newManga = manga.titleToUrlFrag()
|
||||
|
||||
return super.fetchChapterList(newManga)
|
||||
}
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
val newManga = manga.titleToUrlFrag()
|
||||
|
||||
return super.fetchMangaDetails(newManga)
|
||||
}
|
||||
|
||||
override fun getMangaUrl(manga: SManga): String {
|
||||
val dbSlug = manga.url
|
||||
.substringBefore("#")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val storedSlug = preferences.slugMap[dbSlug] ?: dbSlug
|
||||
|
||||
return "$baseUrl$mangaUrlDirectory/$storedSlug/"
|
||||
}
|
||||
|
||||
private fun Observable<MangasPage>.tempUrlToPermIfNeeded(): Observable<MangasPage> {
|
||||
return this.map { mangasPage ->
|
||||
MangasPage(
|
||||
mangasPage.mangas.map { it.tempUrlToPermIfNeeded() },
|
||||
mangasPage.hasNextPage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SManga.tempUrlToPermIfNeeded(): SManga {
|
||||
if (!preferences.permaUrlPref) return this
|
||||
|
||||
val slugMap = preferences.slugMap
|
||||
|
||||
val sMangaTitleFirstWord = this.title.split(" ")[0]
|
||||
if (!this.url.contains("/$sMangaTitleFirstWord", ignoreCase = true)) {
|
||||
val currentSlug = this.url
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val permaSlug = currentSlug.replaceFirst(TEMP_TO_PERM_REGEX, "")
|
||||
|
||||
slugMap[permaSlug] = currentSlug
|
||||
|
||||
this.url = "$mangaUrlDirectory/$permaSlug/"
|
||||
}
|
||||
preferences.slugMap = slugMap
|
||||
return this
|
||||
}
|
||||
|
||||
private fun SManga.titleToUrlFrag(): SManga {
|
||||
return try {
|
||||
this.apply {
|
||||
url = "$url#${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 frag = request.url.fragment
|
||||
|
||||
if (frag.isNullOrEmpty()) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
val dbSlug = request.url.toString()
|
||||
.substringBefore("#")
|
||||
.removeSuffix("/")
|
||||
.substringAfterLast("/")
|
||||
|
||||
val slugMap = preferences.slugMap
|
||||
|
||||
val storedSlug = slugMap[dbSlug] ?: dbSlug
|
||||
|
||||
val response = chain.proceed(
|
||||
request.newBuilder()
|
||||
.url("$baseUrl$mangaUrlDirectory/$storedSlug/")
|
||||
.build(),
|
||||
)
|
||||
|
||||
if (!response.isSuccessful && response.code == 404) {
|
||||
response.close()
|
||||
|
||||
val newSlug = getNewSlug(storedSlug, frag)
|
||||
?: throw IOException("Migrate from Luminous to Luminous")
|
||||
|
||||
slugMap[dbSlug] = newSlug
|
||||
preferences.slugMap = slugMap
|
||||
|
||||
return chain.proceed(
|
||||
request.newBuilder()
|
||||
.url("$baseUrl$mangaUrlDirectory/$newSlug/")
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
private fun getNewSlug(existingSlug: String, frag: String): String? {
|
||||
val permaSlug = existingSlug
|
||||
.replaceFirst(TEMP_TO_PERM_REGEX, "")
|
||||
|
||||
val search = frag.substringBefore("#")
|
||||
|
||||
val mangas = client.newCall(searchMangaRequest(1, search, FilterList()))
|
||||
.execute()
|
||||
.use {
|
||||
searchMangaParse(it)
|
||||
}
|
||||
|
||||
return mangas.mangas.firstOrNull { newManga ->
|
||||
newManga.url.contains(permaSlug, true)
|
||||
}
|
||||
?.url
|
||||
?.removeSuffix("/")
|
||||
?.substringAfterLast("/")
|
||||
}
|
||||
|
||||
private fun String.toSearchQuery(): String {
|
||||
return this.trim()
|
||||
.lowercase()
|
||||
.replace(titleSpecialCharactersRegex, "+")
|
||||
.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)
|
||||
}
|
||||
|
||||
private val SharedPreferences.permaUrlPref
|
||||
get() = getBoolean(PREF_PERM_MANGA_URL_KEY_PREFIX + lang, true)
|
||||
|
||||
private var SharedPreferences.slugMap: MutableMap<String, String>
|
||||
get() {
|
||||
val serialized = getString(PREF_URL_MAP, null) ?: return mutableMapOf()
|
||||
|
||||
return try {
|
||||
json.decodeFromString(serialized)
|
||||
} catch (e: Exception) {
|
||||
mutableMapOf()
|
||||
}
|
||||
}
|
||||
set(slugMap) {
|
||||
val serialized = json.encodeToString(slugMap)
|
||||
edit().putString(PREF_URL_MAP, serialized).commit()
|
||||
}
|
||||
|
||||
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]+""")
|
||||
private val trailingPlusRegex = Regex("""\++$""")
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ ext {
|
||||
extClass = '.RizzComic'
|
||||
themePkg = 'mangathemesia'
|
||||
baseUrl = 'https://rizzcomic.com'
|
||||
overrideVersionCode = 0
|
||||
overrideVersionCode = 1
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -26,53 +26,49 @@ abstract class SelectFilter(
|
||||
class SortFilter(defaultOrder: String? = null) : SelectFilter("Sort By", sort, defaultOrder) {
|
||||
override val formParameter = "OrderValue"
|
||||
companion object {
|
||||
private val sort = listOf(
|
||||
Pair("Default", "all"),
|
||||
Pair("A-Z", "title"),
|
||||
Pair("Z-A", "titlereverse"),
|
||||
Pair("Latest Update", "update"),
|
||||
Pair("Latest Added", "latest"),
|
||||
Pair("Popular", "popular"),
|
||||
)
|
||||
|
||||
val POPULAR = FilterList(StatusFilter(), TypeFilter(), SortFilter("popular"))
|
||||
val LATEST = FilterList(StatusFilter(), TypeFilter(), SortFilter("update"))
|
||||
}
|
||||
}
|
||||
|
||||
private val sort = listOf(
|
||||
Pair("Default", "all"),
|
||||
Pair("A-Z", "title"),
|
||||
Pair("Z-A", "titlereverse"),
|
||||
Pair("Latest Update", "update"),
|
||||
Pair("Latest Added", "latest"),
|
||||
Pair("Popular", "popular"),
|
||||
)
|
||||
|
||||
class StatusFilter : SelectFilter("Status", status) {
|
||||
override val formParameter = "StatusValue"
|
||||
companion object {
|
||||
private val status = listOf(
|
||||
Pair("All", "all"),
|
||||
Pair("Ongoing", "ongoing"),
|
||||
Pair("Complete", "completed"),
|
||||
Pair("Hiatus", "hiatus"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val status = listOf(
|
||||
Pair("All", "all"),
|
||||
Pair("Ongoing", "ongoing"),
|
||||
Pair("Complete", "completed"),
|
||||
Pair("Hiatus", "hiatus"),
|
||||
)
|
||||
|
||||
class TypeFilter : SelectFilter("Type", type) {
|
||||
override val formParameter = "TypeValue"
|
||||
companion object {
|
||||
private val type = listOf(
|
||||
Pair("All", "all"),
|
||||
Pair("Manga", "Manga"),
|
||||
Pair("Manhwa", "Manhwa"),
|
||||
Pair("Manhua", "Manhua"),
|
||||
Pair("Comic", "Comic"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val type = listOf(
|
||||
Pair("All", "all"),
|
||||
Pair("Manga", "Manga"),
|
||||
Pair("Manhwa", "Manhwa"),
|
||||
Pair("Manhua", "Manhua"),
|
||||
Pair("Comic", "Comic"),
|
||||
)
|
||||
|
||||
class CheckBoxFilter(
|
||||
name: String,
|
||||
val value: String,
|
||||
) : Filter.CheckBox(name)
|
||||
|
||||
class GenreFilter(
|
||||
genres: List<Pair<String, String>>,
|
||||
) : FormBodyFilter, Filter.Group<CheckBoxFilter>(
|
||||
class GenreFilter : FormBodyFilter, Filter.Group<CheckBoxFilter>(
|
||||
"Genre",
|
||||
genres.map { CheckBoxFilter(it.first, it.second) },
|
||||
) {
|
||||
@ -82,3 +78,34 @@ class GenreFilter(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val genres = listOf(
|
||||
Pair("Abilities", "2"),
|
||||
Pair("Action", "3"),
|
||||
Pair("Adaptation", "4"),
|
||||
Pair("Adventure", "5"),
|
||||
Pair("Another Chance", "6"),
|
||||
Pair("Apocalypse", "7"),
|
||||
Pair("Based On A Novel", "8"),
|
||||
Pair("Cheat", "9"),
|
||||
Pair("Comedy", "10"),
|
||||
Pair("Conspiracy", "11"),
|
||||
Pair("Cultivation", "12"),
|
||||
Pair("Demon", "13"),
|
||||
Pair("Demon King", "14"),
|
||||
Pair("Dragon", "15"),
|
||||
Pair("Drama", "16"),
|
||||
Pair("Drop", "17"),
|
||||
Pair("Dungeon", "18"),
|
||||
Pair("Dungeons", "19"),
|
||||
Pair("Fantasy", "20"),
|
||||
Pair("Game", "21"),
|
||||
Pair("Genius", "22"),
|
||||
Pair("Ghosts", "23"),
|
||||
Pair("Harem", "24"),
|
||||
Pair("Hero", "25"),
|
||||
Pair("Hidden Identity", "26"),
|
||||
Pair("HighFantasy", "27"),
|
||||
Pair("Historical", "28"),
|
||||
Pair("Horror", "29"),
|
||||
)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.extension.en.rizzcomic
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesiaAlt
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
@ -9,9 +10,7 @@ import eu.kanade.tachiyomi.source.model.Filter
|
||||
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
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
@ -22,7 +21,7 @@ import rx.Observable
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class RizzComic : MangaThemesia(
|
||||
class RizzComic : MangaThemesiaAlt(
|
||||
"Rizz Comic",
|
||||
"https://rizzcomic.com",
|
||||
"en",
|
||||
@ -40,43 +39,12 @@ class RizzComic : MangaThemesia(
|
||||
.build()
|
||||
}
|
||||
|
||||
private var urlPrefix: String? = null
|
||||
private var genreCache: List<Pair<String, String>> = emptyList()
|
||||
private var attempts = 0
|
||||
override val versionId = 2
|
||||
|
||||
private fun updateCache() {
|
||||
if ((urlPrefix.isNullOrEmpty() || genreCache.isEmpty()) && attempts < 3) {
|
||||
runCatching {
|
||||
val document = client.newCall(GET("$baseUrl$mangaUrlDirectory", headers))
|
||||
.execute().use { it.asJsoup() }
|
||||
override val slugRegex = Regex("""^r\d+-""")
|
||||
|
||||
urlPrefix = document.selectFirst(".listupd a")
|
||||
?.attr("href")
|
||||
?.substringAfter("$mangaUrlDirectory/")
|
||||
?.substringBefore("-")
|
||||
|
||||
genreCache = document.selectFirst(".filter .genrez")
|
||||
?.select("li")
|
||||
.orEmpty()
|
||||
.map {
|
||||
val name = it.select("label").text()
|
||||
val id = it.select("input").attr("value")
|
||||
|
||||
Pair(name, id)
|
||||
}
|
||||
}
|
||||
|
||||
attempts++
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUrlPrefix(): String {
|
||||
if (urlPrefix.isNullOrEmpty()) {
|
||||
updateCache()
|
||||
}
|
||||
|
||||
return urlPrefix ?: throw Exception("Unable to update dynamic urls")
|
||||
}
|
||||
// don't allow disabling random part setting
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) { }
|
||||
|
||||
override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", SortFilter.POPULAR)
|
||||
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
||||
@ -103,30 +71,17 @@ class RizzComic : MangaThemesia(
|
||||
}
|
||||
|
||||
override fun getFilterList(): FilterList {
|
||||
val filters: MutableList<Filter<*>> = mutableListOf(
|
||||
return FilterList(
|
||||
Filter.Header("Filters don't work with text search"),
|
||||
SortFilter(),
|
||||
StatusFilter(),
|
||||
TypeFilter(),
|
||||
GenreFilter(),
|
||||
)
|
||||
|
||||
filters += if (genreCache.isEmpty()) {
|
||||
listOf(
|
||||
Filter.Separator(),
|
||||
Filter.Header("Press reset to attempt to load genres"),
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
GenreFilter(genreCache),
|
||||
)
|
||||
}
|
||||
|
||||
return FilterList(filters)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class Comic(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
@SerialName("image_url") val cover: String? = null,
|
||||
@SerialName("long_description") val synopsis: String? = null,
|
||||
@ -150,13 +105,11 @@ class RizzComic : MangaThemesia(
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
updateCache()
|
||||
|
||||
val result = response.parseAs<List<Comic>>()
|
||||
|
||||
val entries = result.map { comic ->
|
||||
SManga.create().apply {
|
||||
url = "${comic.slug}#${comic.id}"
|
||||
url = "$mangaUrlDirectory/${comic.slug}/"
|
||||
title = comic.title
|
||||
description = comic.synopsis
|
||||
author = listOfNotNull(comic.author, comic.serialization).joinToString()
|
||||
@ -166,7 +119,7 @@ class RizzComic : MangaThemesia(
|
||||
genre = buildList {
|
||||
add(comic.type?.capitalize())
|
||||
comic.genreIds?.onEach { gId ->
|
||||
add(genreCache.firstOrNull { it.second == gId }?.first)
|
||||
add(genres.firstOrNull { it.second == gId }?.first)
|
||||
}
|
||||
}.filterNotNull().joinToString()
|
||||
initialized = true
|
||||
@ -182,37 +135,6 @@ class RizzComic : MangaThemesia(
|
||||
.map { mangaDetailsParse(it).apply { description = manga.description } }
|
||||
}
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
val slug = manga.url.substringBefore("#")
|
||||
val randomPart = getUrlPrefix()
|
||||
|
||||
return GET("$baseUrl/series/$randomPart-$slug", headers)
|
||||
}
|
||||
|
||||
override fun getMangaUrl(manga: SManga): String {
|
||||
val slug = manga.url.substringBefore("#")
|
||||
|
||||
val urlPart = urlPrefix?.let { "$it-" } ?: ""
|
||||
|
||||
return "$baseUrl/series/$urlPart$slug"
|
||||
}
|
||||
|
||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
return super.chapterListParse(response).map { chapter ->
|
||||
chapter.apply {
|
||||
url = url.removeSuffix("/")
|
||||
.substringAfter("/")
|
||||
.substringAfter("-")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
return GET("$baseUrl/chapter/${getUrlPrefix()}-${chapter.url}", headers)
|
||||
}
|
||||
|
||||
override fun imageRequest(page: Page): Request {
|
||||
val newHeaders = headersBuilder()
|
||||
.set("Accept", "image/avif,image/webp,image/png,image/jpeg,*/*")
|
||||
|
Loading…
Reference in New Issue
Block a user