From f97d742c40d2b249dd6449293f49374e511c7db3 Mon Sep 17 00:00:00 2001 From: Mathis Date: Fri, 20 Jun 2025 01:04:42 -0500 Subject: [PATCH] feat(fr/animesama): update AnimeSama with new filters, improved search functionality and add a default player preference in param (#1018) Checklist: - [X] Updated `extVersionCode` value in `build.gradle` for individual extensions - [X] Updated `overrideVersionCode` or `baseVersionCode` as needed for all multisrc extensions - [X] Referenced all related issues in the PR body (e.g. "Closes #xyz") - [X] Added the `isNsfw = true` flag in `build.gradle` when appropriate - [X] Have not changed source names - [X] Have explicitly kept the `id` if a source's name or language were changed - [X] Have tested the modifications by compiling and running the extension through Android Studio - [X] Have removed `web_hi_res_512.png` when adding a new extension - [X] Have made sure all the icons are in png format Reviewed-on: https://kohiden.xyz/Kohi-den/extensions-source/pulls/1018 Co-authored-by: Mathis Co-committed-by: Mathis --- src/fr/animesama/build.gradle | 2 +- .../animeextension/fr/animesama/AnimeSama.kt | 128 +++++++++++------- .../fr/animesama/AnimeSamaFilters.kt | 120 +++++++++++----- 3 files changed, 166 insertions(+), 84 deletions(-) diff --git a/src/fr/animesama/build.gradle b/src/fr/animesama/build.gradle index ae52c318..5df0cf10 100644 --- a/src/fr/animesama/build.gradle +++ b/src/fr/animesama/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Anime-Sama' extClass = '.AnimeSama' - extVersionCode = 12 + extVersionCode = 13 } apply from: "$rootDir/common.gradle" diff --git a/src/fr/animesama/src/eu/kanade/tachiyomi/animeextension/fr/animesama/AnimeSama.kt b/src/fr/animesama/src/eu/kanade/tachiyomi/animeextension/fr/animesama/AnimeSama.kt index fa94d5d9..ac8edfa2 100644 --- a/src/fr/animesama/src/eu/kanade/tachiyomi/animeextension/fr/animesama/AnimeSama.kt +++ b/src/fr/animesama/src/eu/kanade/tachiyomi/animeextension/fr/animesama/AnimeSama.kt @@ -4,6 +4,7 @@ import android.app.Application import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen +import app.cash.quickjs.QuickJs import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimesPage @@ -17,6 +18,7 @@ import eu.kanade.tachiyomi.lib.vkextractor.VkExtractor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.parallelCatchingFlatMap +import eu.kanade.tachiyomi.util.parallelFlatMapBlocking import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import okhttp3.HttpUrl.Companion.toHttpUrl @@ -49,12 +51,11 @@ class AnimeSama : ConfigurableAnimeSource, AnimeHttpSource() { // ============================== Popular =============================== override fun popularAnimeParse(response: Response): AnimesPage { - val doc = response.body.string() + val doc = response.asJsoup() val page = response.request.url.fragment?.toInt() ?: 0 - val regex = Regex("^\\s*carteClassique\\(\\s*.*?\\s*,\\s*\"(.*?)\".*\\)", RegexOption.MULTILINE) - val chunks = regex.findAll(doc).chunked(5).toList() + val chunks = doc.select("#containerPepites > div a").chunked(5) val seasons = chunks.getOrNull(page - 1)?.flatMap { - val animeUrl = "$baseUrl/catalogue/${it.groupValues[1]}" + val animeUrl = "$baseUrl${it.attr("href")}" fetchAnimeSeasons(animeUrl) }?.toList().orEmpty() return AnimesPage(seasons, page < chunks.size) @@ -72,7 +73,7 @@ class AnimeSama : ConfigurableAnimeSource, AnimeHttpSource() { .removePathSegment(animeUrl.pathSize - 3) .build() fetchAnimeSeasons(url.toString()) - } + }.distinctBy { it.url } return AnimesPage(seasons, false) } override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl) @@ -80,29 +81,26 @@ class AnimeSama : ConfigurableAnimeSource, AnimeHttpSource() { // =============================== Search =============================== override fun getFilterList() = AnimeSamaFilters.FILTER_LIST - override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage { - if (query.startsWith(PREFIX_SEARCH)) { - return AnimesPage(fetchAnimeSeasons("$baseUrl/catalogue/${query.removePrefix(PREFIX_SEARCH)}/"), false) - } + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { + val url = "$baseUrl/catalogue/".toHttpUrl().newBuilder() val params = AnimeSamaFilters.getSearchFilters(filters) - val elements = database - .asSequence() - .filter { it.select("h1, p").fold(false) { v, e -> v || e.text().contains(query, true) } } - .filter { params.include.all { p -> it.className().contains(p) } } - .filter { params.exclude.none { p -> it.className().contains(p) } } - .filter { params.types.fold(params.types.isEmpty()) { v, p -> v || it.className().contains(p) } } - .filter { params.language.fold(params.language.isEmpty()) { v, p -> v || it.className().contains(p) } } - .chunked(5) - .toList() - if (elements.isEmpty()) return AnimesPage(emptyList(), false) - val animes = elements[page - 1].flatMap { - fetchAnimeSeasons(it.getElementsByTag("a").attr("href")) - } - return AnimesPage(animes, page < elements.size) + params.types.forEach { url.addQueryParameter("type[]", it) } + params.language.forEach { url.addQueryParameter("langue[]", it) } + params.genres.forEach { url.addQueryParameter("genre[]", it) } + url.addQueryParameter("search", query) + url.addQueryParameter("page", "$page") + return GET(url.build(), headers) } - override fun searchAnimeParse(response: Response): AnimesPage = throw UnsupportedOperationException() - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw UnsupportedOperationException() + override fun searchAnimeParse(response: Response): AnimesPage { + val document = response.asJsoup() + val anime = document.select("#list_catalog > div a").parallelFlatMapBlocking { + fetchAnimeSeasons(it.attr("href")) + } + val page = response.request.url.queryParameterValues("page").first() + val hasNextPage = document.select("#list_pagination a:last-child").text() != page + return AnimesPage(anime, hasNextPage) + } // =========================== Anime Details ============================ override suspend fun getAnimeDetails(anime: SAnime): SAnime = anime @@ -121,6 +119,10 @@ class AnimeSama : ConfigurableAnimeSource, AnimeHttpSource() { override fun episodeListParse(response: Response): List = throw UnsupportedOperationException() // ============================ Video Links ============================= + private val sibnetExtractor by lazy { SibnetExtractor(client) } + private val vkExtractor by lazy { VkExtractor(client, headers) } + private val sendvidExtractor by lazy { SendvidExtractor(client, headers) } + override suspend fun getVideoList(episode: SEpisode): List