From 8c7c46e0e23f7e1d8375cb15259a9a77b1f0eff6 Mon Sep 17 00:00:00 2001 From: Yakoo Date: Mon, 11 Aug 2025 04:49:17 +0200 Subject: [PATCH] AnimeSama: add filtering by genre (#10059) * Update AnimeSama Fix description & genre by updating html tag id * Update build.gradle Increasing the code version number * Adding latest items for AnimeSama only the items available on the main page * Fix URL on latest items * AnimeSama: adding genre filter Using almost the same code as FuzzyDoodle, I added the only filter available on animesama: genre * Update AnimeSama.kt remove lint errors * Including code review optimization Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com> * Fix build error --------- Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com> --- src/fr/animesama/build.gradle | 2 +- .../extension/fr/animesama/AnimeSama.kt | 76 ++++++++++++++++--- .../extension/fr/animesama/Requests.kt | 33 ++++++++ 3 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 src/fr/animesama/src/eu/kanade/tachiyomi/extension/fr/animesama/Requests.kt diff --git a/src/fr/animesama/build.gradle b/src/fr/animesama/build.gradle index 6cc18335c..95371b96b 100644 --- a/src/fr/animesama/build.gradle +++ b/src/fr/animesama/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'AnimeSama' extClass = '.AnimeSama' - extVersionCode = 6 + extVersionCode = 7 } apply from: "$rootDir/common.gradle" diff --git a/src/fr/animesama/src/eu/kanade/tachiyomi/extension/fr/animesama/AnimeSama.kt b/src/fr/animesama/src/eu/kanade/tachiyomi/extension/fr/animesama/AnimeSama.kt index 3132f6410..16bc514c4 100644 --- a/src/fr/animesama/src/eu/kanade/tachiyomi/extension/fr/animesama/AnimeSama.kt +++ b/src/fr/animesama/src/eu/kanade/tachiyomi/extension/fr/animesama/AnimeSama.kt @@ -1,12 +1,17 @@ package eu.kanade.tachiyomi.extension.fr.animesama +import android.util.Log import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.await +import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList 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.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull @@ -33,13 +38,55 @@ class AnimeSama : ParsedHttpSource() { override fun headersBuilder(): Headers.Builder = super.headersBuilder() .add("Accept-Language", "fr-FR") + // filters + private var genreList = listOf>() + private var fetchFilterAttempts = 0 + + private suspend fun fetchFilters() { + if (fetchFilterAttempts < 3 && (genreList.isEmpty())) { + try { + val response = client.newCall(filtersRequest()).await().asJsoup() + parseFilters(response) + } catch (e: Exception) { + Log.e("$name: Filters", e.stackTraceToString()) + } + fetchFilterAttempts++ + } + } + + private fun filtersRequest() = GET("$baseUrl/catalogue", headers) + + private fun parseFilters(document: Document) { + genreList = document.select("#list_genres label").mapNotNull { labelElement -> + val input = labelElement.selectFirst("input[name=genre[]]") ?: return@mapNotNull null + val labelText = labelElement.ownText() + val value = input.attr("value") + labelText to value + } + } + + override fun getFilterList(): FilterList { + val filters = mutableListOf>() + launchIO { fetchFilters() } + + if (genreList.isNotEmpty()) { + filters.add(GenreFilter(genreList)) + } + if (filters.size < 1) { + filters.add(0, Filter.Header("Press 'reset' to load more filters")) + } + + return FilterList(filters) + } + + private fun launchIO(block: suspend () -> Unit) = GlobalScope.launch { block() } + // Popular override fun popularMangaRequest(page: Int): Request { val url = "$baseUrl/catalogue".toHttpUrl().newBuilder() .addQueryParameter("type[0]", "Scans") .addQueryParameter("page", page.toString()) .build() - return GET(url, headers) } @@ -78,6 +125,11 @@ class AnimeSama : ParsedHttpSource() { .addQueryParameter("type[0]", "Scans") .addQueryParameter("search", query) .addQueryParameter("page", page.toString()) + .apply { + filters.filterIsInstance().forEach { + it.addUrlParameter(this) + } + } .build() return GET(url, headers) @@ -100,14 +152,14 @@ class AnimeSama : ParsedHttpSource() { override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException() - fun String.containsMultipleTimes(search: String): Boolean { + private fun String.containsMultipleTimes(search: String): Boolean { val regex = Regex(search) val matches = regex.findAll(this) val count = matches.count() return count > 1 } - private fun parseChapterFromResponse(response: Response, translation_name: String): List { + private fun parseChapterFromResponse(response: Response, translationName: String): List { val document = response.asJsoup() val chapterUrl = document.baseUri().toHttpUrl() @@ -146,7 +198,7 @@ class AnimeSama : ParsedHttpSource() { SChapter.create().apply { name = "Chapitre $i" setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).build().toString()) - scanlator = translation_name + scanlator = translationName }, ) } @@ -157,7 +209,7 @@ class AnimeSama : ParsedHttpSource() { SChapter.create().apply { name = "Chapitre $title" setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).build().toString()) - scanlator = translation_name + scanlator = translationName }, ) chapterDelay++ @@ -166,12 +218,12 @@ class AnimeSama : ParsedHttpSource() { } } } - for (index in parsedChapterList.size..parsedJavascriptFileToJson.size - 1) { + for (index in parsedChapterList.size until parsedJavascriptFileToJson.size) { parsedChapterList.add( SChapter.create().apply { name = "Chapitre " + (parsedChapterList.size + 1 - chapterDelay) setUrlWithoutDomain(chapterUrl.newBuilder().addQueryParameter("id", (parsedChapterList.size + 1).toString()).build().toString()) - scanlator = translation_name + scanlator = translationName }, ) } @@ -222,7 +274,7 @@ class AnimeSama : ParsedHttpSource() { val documentString = document.body().toString() - val allChapters: Map = Regex("""eps(\d+)\s*(?:=\s*\[(.*?)\]|\.length\s*=\s*(\d+))""") + val allChapters: Map = Regex("""eps(\d+)\s*(?:=\s*\[(.*?)]|\.length\s*=\s*(\d+))""") .findAll(documentString) .associate { match -> val episode = match.groupValues[1].toInt() @@ -238,16 +290,16 @@ class AnimeSama : ParsedHttpSource() { episode to length } - val chapterSize = allChapters.get(chapter?.toInt()) ?: 1 + val chapterSize = allChapters[chapter?.toInt()] ?: 1 - val image_list = mutableListOf() + val imageList = mutableListOf() for (index in 1 until chapterSize + 1) { - image_list.add( + imageList.add( Page(index, imageUrl = "$cdn$title/$chapter/$index.jpg"), ) } - return image_list + return imageList } override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException() diff --git a/src/fr/animesama/src/eu/kanade/tachiyomi/extension/fr/animesama/Requests.kt b/src/fr/animesama/src/eu/kanade/tachiyomi/extension/fr/animesama/Requests.kt new file mode 100644 index 000000000..5d893bb7b --- /dev/null +++ b/src/fr/animesama/src/eu/kanade/tachiyomi/extension/fr/animesama/Requests.kt @@ -0,0 +1,33 @@ +package eu.kanade.tachiyomi.extension.fr.animesama + +import eu.kanade.tachiyomi.source.model.Filter +import okhttp3.HttpUrl + +interface UrlPartFilter { + fun addUrlParameter(url: HttpUrl.Builder) +} + +class CheckBoxFilter(name: String, val value: String) : Filter.CheckBox(name) + +abstract class CheckBoxGroup( + name: String, + options: List>, + private val urlParameter: String, +) : UrlPartFilter, Filter.Group( + name, + options.map { CheckBoxFilter(it.first, it.second) }, +) { + override fun addUrlParameter(url: HttpUrl.Builder) { + state.filter { it.state }.forEach { + url.addQueryParameter(urlParameter, it.value) + } + } +} + +class GenreFilter( + options: List>, +) : CheckBoxGroup( + "Genres", + options, + "genre[0]", +)