From 0cf256cccebd4a82bb98ea57cd9a52077e5a2fd9 Mon Sep 17 00:00:00 2001 From: "Cuong M. Tran" Date: Thu, 25 Apr 2024 16:25:40 +0700 Subject: [PATCH] =?UTF-8?q?using=20search=20to=20enable=20all=20extensions?= =?UTF-8?q?=E2=80=99=20related=20mangas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tachiyomi/ui/manga/MangaScreenModel.kt | 2 +- .../eu/kanade/tachiyomi/source/Source.kt | 7 -- .../tachiyomi/source/online/HttpSource.kt | 77 ++++++++++++++++--- .../source/online/ParsedHttpSource.kt | 7 +- 4 files changed, 74 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 1ef329acc..6c65a8968 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -1109,7 +1109,7 @@ class MangaScreenModel( try { withIOContext { if (state.source !is MergedSource) { - val relatedMangas = if (state.source.supportsRelatedMangas) + val relatedMangas = if (true)//state.source.supportsRelatedMangas) withIOContext { state.source.getRelatedMangaList(state.manga.toSManga()) .map { diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/Source.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/Source.kt index 2fb6db2db..a0b454b7f 100644 --- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/Source.kt +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/Source.kt @@ -49,13 +49,6 @@ interface Source { } // KMK --> - /** - * Whether the source has support for related mangas. - * - * @since extensions-lib 1.6 - */ - val supportsRelatedMangas: Boolean get() = false - /** * Get all the available related mangas for a manga. * diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt index 2a98cf480..99270494d 100644 --- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt @@ -16,6 +16,10 @@ import eu.kanade.tachiyomi.source.model.SManga import exh.log.maybeInjectEHLogger import exh.pref.DelegateSourcePreferences import exh.source.DelegatedHttpSource +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Request @@ -27,6 +31,7 @@ import uy.kohesive.injekt.api.get import java.net.URI import java.net.URISyntaxException import java.security.MessageDigest +import java.util.Locale /** * A simple implementation for sources from a website. @@ -275,6 +280,14 @@ abstract class HttpSource : CatalogueSource { protected abstract fun mangaDetailsParse(response: Response): SManga // KMK --> + /** + * Whether parsing related mangas in manga page or extension provide custom related mangas request (true) + * or using search keyword to search for related mangas (false - default) + * @default false + * @since extensions-lib 1.6 + */ + protected open val supportsRelatedMangas: Boolean get() = false + /** * Get all the available related mangas for a manga. * Normally it's not needed to override this method. @@ -285,9 +298,60 @@ abstract class HttpSource : CatalogueSource { */ @Suppress("DEPRECATION") override suspend fun getRelatedMangaList(manga: SManga): List { - return fetchRelatedMangaList(manga).awaitSingle() + return if (supportsRelatedMangas) + fetchRelatedMangaList(manga).awaitSingle() + else + return fetchRelatedMangaListBySearch(manga) } + /** + * Fetch related mangas by searching for each keywords from manga's title + */ + protected open suspend fun fetchRelatedMangaListBySearch(manga: SManga): List { + fun String.stripKeyword(): List { + val regexWhitespace = Regex("\\s+") + // remove special character + return replace(Regex("[\\[(!~@#$%^&*|,?:\"<>)\\]]"), " ") + .split(regexWhitespace) + .map { + // remove number only + it.replace(Regex("^\\d+$"), "") + .lowercase(Locale.getDefault()) + } + // exclude single character + .filter { it.length > 1 } + } + val scope = CoroutineScope(Dispatchers.IO) + val words = HashSet() + words += manga.title.stripKeyword() + manga.originalTitle.let { title -> + words += title.stripKeyword() + } + if (words.isEmpty()) { + return emptyList() + } + val results = words.map { keyword -> + scope.async { + client.newCall(searchMangaRequest(0, keyword, FilterList())) + .execute() + .let { response -> + searchMangaParse(response).mangas + .filter { + it.url != manga.url && + it.title.lowercase().contains(keyword) + } + } + } + }.awaitAll() + .minBy { if (it.isEmpty()) Int.MAX_VALUE else it.size } + + return results + } + + /** + * Fetch related mangas found in manga's page which is provided by sites. + * If using this, must also: 'override val supportsRelatedMangas = true' + */ @Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getRelatedMangaList")) override fun fetchRelatedMangaList(manga: SManga): Observable> { return client.newCall(relatedMangaListRequest(manga)) @@ -300,6 +364,7 @@ abstract class HttpSource : CatalogueSource { /** * Returns the request for get related manga list. Override only if it's needed to override * the url, send different headers or request method like POST. + * If using this, must also: 'override val supportsRelatedMangas = true' * * @param manga the manga to look for related mangas. */ @@ -309,17 +374,11 @@ abstract class HttpSource : CatalogueSource { /** * Parses the response from the site and returns a list of related mangas. + * If using this, must also: 'override val supportsRelatedMangas = true' * * @param response the response from the site. */ - protected open fun relatedMangaListParse(response: Response): List = throw UnsupportedOperationException() - - /** - * Parses the response from the site and returns a SManga Object. - * - * @param response the response from the site. - */ - protected open fun relatedMangaPageParse(response: Response): SManga = throw UnsupportedOperationException() + protected open fun relatedMangaListParse(response: Response): List = popularMangaParse(response).mangas // KMK <-- /** diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/ParsedHttpSource.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/ParsedHttpSource.kt index 97f251a59..b87e60f58 100644 --- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/ParsedHttpSource.kt +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/ParsedHttpSource.kt @@ -148,21 +148,24 @@ abstract class ParsedHttpSource : HttpSource() { // KMK --> /** * Parses the response from the site and returns a list of related mangas. + * If using this, must also: 'override val supportsRelatedMangas = true' * * @param response the response from the site. */ override fun relatedMangaListParse(response: Response): List { - val document = response.asJsoup() - return document.select(relatedMangaListSelector()).map { relatedMangaFromElement(it) } + return response.asJsoup() + .select(relatedMangaListSelector()).map { relatedMangaFromElement(it) } } /** * Returns the Jsoup selector that returns a list of [Element] corresponding to each related mangas. + * If using this, must also: 'override val supportsRelatedMangas = true' */ protected open fun relatedMangaListSelector(): String = popularMangaSelector() /** * Returns a manga from the given element. + * If using this, must also: 'override val supportsRelatedMangas = true' * * @param element an element obtained from [relatedMangaListSelector]. */