diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/Kemono.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/Kemono.kt index 0585c1f06..31eba715f 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/Kemono.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/Kemono.kt @@ -20,7 +20,6 @@ import okhttp3.Callback import okhttp3.Request import okhttp3.Response import okio.blackholeSink -import org.jsoup.nodes.Element import org.jsoup.select.Evaluator import rx.Observable import uy.kohesive.injekt.Injekt @@ -32,12 +31,12 @@ import kotlin.math.min open class Kemono( override val name: String, - override val baseUrl: String, + private val defaultUrl: String, override val lang: String = "all", ) : HttpSource(), ConfigurableSource { override val supportsLatest = true - private val isNewDesign get() = name == "Kemono" + private val mirrorUrls get() = arrayOf(defaultUrl, defaultUrl.removeSuffix(".party") + ".su") override val client = network.client.newBuilder().rateLimit(2).build() @@ -46,47 +45,33 @@ open class Kemono( private val json: Json by injectLazy() - private val preferences by lazy { + private val preferences = Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - override fun popularMangaRequest(page: Int): Request = - GET("$baseUrl/artists?o=${PAGE_SIZE * (page - 1)}", headers) + override val baseUrl = preferences.getString(BASE_URL_PREF, defaultUrl)!! - override fun popularMangaParse(response: Response): MangasPage { - val document = response.asJsoup() - val cardList = document.selectFirst(Evaluator.Class("card-list"))!! - val creators = cardList.select(Evaluator.Tag("article")).map { - val children = it.children() - val avatar = children[0].selectFirst(Evaluator.Tag("img"))!!.attr("src") - val link = children[1].child(0) - val service = children[2].ownText() - SManga.create().apply { - url = link.attr("href") - title = link.ownText() - author = service - thumbnail_url = baseUrl + avatar - description = PROMPT - initialized = true - } - }.filterUnsupported() - return MangasPage(creators, document.hasNextPage()) - } + private val imgCdnUrl = when (name) { + "Kemono" -> baseUrl + else -> defaultUrl + }.replace("//", "//img.") - override fun latestUpdatesRequest(page: Int): Request = - GET("$baseUrl/artists/updated?o=${PAGE_SIZE * (page - 1)}", headers) + private fun String.formatAvatarUrl(): String = removePrefix("https://").replaceBefore('/', imgCdnUrl) - override fun latestUpdatesParse(response: Response) = popularMangaParse(response) + override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException() + + override fun popularMangaParse(response: Response) = throw UnsupportedOperationException() + + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() + + override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException() override fun fetchPopularManga(page: Int): Observable { - if (!isNewDesign) return super.fetchPopularManga(page) return Observable.fromCallable { fetchNewDesignListing(page, "/artists", compareByDescending { it.favorited }) } } override fun fetchLatestUpdates(page: Int): Observable { - if (!isNewDesign) return super.fetchLatestUpdates(page) return Observable.fromCallable { fetchNewDesignListing(page, "/artists/updated", compareByDescending { it.updatedDate }) } @@ -106,7 +91,7 @@ open class Kemono( url = it.attr("href") title = it.selectFirst(Evaluator.Class("user-card__name"))!!.ownText() author = it.selectFirst(Evaluator.Class("user-card__service"))!!.ownText() - thumbnail_url = baseUrl + it.selectFirst(Evaluator.Tag("img"))!!.attr("src") + thumbnail_url = it.selectFirst(Evaluator.Tag("img"))!!.absUrl("src").formatAvatarUrl() description = PROMPT initialized = true } @@ -135,14 +120,14 @@ open class Kemono( page: Int, block: (ArrayList) -> List, ): MangasPage { - val baseUrl = this.baseUrl + val imgCdnUrl = this.imgCdnUrl val response = client.newCall(GET("$baseUrl/api/creators", headers)).execute() val allCreators = block(response.parseAs()) val count = allCreators.size val fromIndex = (page - 1) * NEW_PAGE_SIZE val toIndex = min(count, fromIndex + NEW_PAGE_SIZE) val creators = allCreators.subList(fromIndex, toIndex) - .map { it.toSManga(baseUrl) } + .map { it.toSManga(imgCdnUrl) } .filterUnsupported() return MangasPage(creators, toIndex < count) } @@ -160,12 +145,15 @@ open class Kemono( client.newCall(GET("$baseUrl/api/creators", headers)).enqueue(callback) } - override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Not used.") - override fun searchMangaParse(response: Response) = throw UnsupportedOperationException("Not used.") + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException() + override fun searchMangaParse(response: Response) = throw UnsupportedOperationException() - override fun fetchMangaDetails(manga: SManga): Observable = Observable.just(manga) + override fun fetchMangaDetails(manga: SManga): Observable { + manga.thumbnail_url = manga.thumbnail_url!!.formatAvatarUrl() + return Observable.just(manga) + } - override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException("Not used.") + override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException() override fun fetchChapterList(manga: SManga): Observable> = Observable.fromCallable { KemonoPostDto.dateFormat.timeZone = when (manga.author) { @@ -187,7 +175,7 @@ open class Kemono( result } - override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Not used.") + override fun chapterListParse(response: Response) = throw UnsupportedOperationException() override fun pageListRequest(chapter: SChapter): Request = GET("$baseUrl/api${chapter.url}", headers) @@ -197,7 +185,7 @@ open class Kemono( return post[0].images.mapIndexed { i, path -> Page(i, imageUrl = baseUrl + path) } } - override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not used.") + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() private inline fun Response.parseAs(): T = use { json.decodeFromStream(it.body.byteStream()) @@ -214,10 +202,18 @@ open class Kemono( }.toTypedArray() setDefaultValue(POST_PAGES_DEFAULT) }.let { screen.addPreference(it) } + + ListPreference(screen.context).apply { + key = BASE_URL_PREF + title = "Mirror URL" + summary = "%s\nRequires app restart to take effect" + entries = mirrorUrls + entryValues = mirrorUrls + setDefaultValue(defaultUrl) + }.let(screen::addPreference) } companion object { - private const val PAGE_SIZE = 25 private const val NEW_PAGE_SIZE = 50 const val PROMPT = "You can change how many posts to load in the extension preferences." @@ -226,11 +222,8 @@ open class Kemono( private const val POST_PAGES_DEFAULT = "1" private const val POST_PAGES_MAX = 50 - private fun Element.hasNextPage(): Boolean { - val pagination = selectFirst(Evaluator.Class("paginator"))!! - return pagination.selectFirst("a[title=Next page]") != null - } - private fun List.filterUnsupported() = filterNot { it.author == "Discord" } + + private const val BASE_URL_PREF = "BASE_URL" } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoDto.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoDto.kt index 4a7bcbc4e..90b432095 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoDto.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoDto.kt @@ -21,11 +21,11 @@ class KemonoCreatorDto( else -> (updated.double * 1000).toLong() } - fun toSManga(baseUrl: String) = SManga.create().apply { + fun toSManga(imgCdnUrl: String) = SManga.create().apply { url = "/$service/user/$id" // should be /server/ for Discord but will be filtered anyway title = name author = service.serviceName() - thumbnail_url = "$baseUrl/icons/$service/$id" + thumbnail_url = "$imgCdnUrl/icons/$service/$id" description = Kemono.PROMPT initialized = true } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoGenerator.kt index 8fdc9c010..5084ff25d 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoGenerator.kt @@ -9,7 +9,7 @@ class KemonoGenerator : ThemeSourceGenerator { override val themePkg = "kemono" - override val baseVersionCode = 5 + override val baseVersionCode = 6 override val sources = listOf( SingleLang("Kemono", "https://kemono.party", "all", isNsfw = true),