From 9e4b2204bb0902e098eda127b1e6c67010e672a7 Mon Sep 17 00:00:00 2001 From: AlphaBoom <30779939+AlphaBoom@users.noreply.github.com> Date: Tue, 3 Jun 2025 12:43:46 +0800 Subject: [PATCH 1/3] Hanime1: Fix anime details cover (#2) * Hanime1: Fix anime details - Use the series cover image for bangumi entries instead of the episode image. - Version bump to 5 * Hanime1: Filter out ad --- src/zh/hanime1/build.gradle | 2 +- .../animeextension/zh/hanime1/Hanime1.kt | 31 ++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/zh/hanime1/build.gradle b/src/zh/hanime1/build.gradle index 85bc719b..f1b59699 100644 --- a/src/zh/hanime1/build.gradle +++ b/src/zh/hanime1/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Hanime1' extClass = '.Hanime1' - extVersionCode = 4 + extVersionCode = 5 isNsfw = true } diff --git a/src/zh/hanime1/src/eu/kanade/tachiyomi/animeextension/zh/hanime1/Hanime1.kt b/src/zh/hanime1/src/eu/kanade/tachiyomi/animeextension/zh/hanime1/Hanime1.kt index ab8e354b..f3bdfdb9 100644 --- a/src/zh/hanime1/src/eu/kanade/tachiyomi/animeextension/zh/hanime1/Hanime1.kt +++ b/src/zh/hanime1/src/eu/kanade/tachiyomi/animeextension/zh/hanime1/Hanime1.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.animeextension.zh.hanime1 import android.app.Application import android.content.SharedPreferences +import android.util.Log import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource @@ -19,9 +20,11 @@ import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import okhttp3.Cookie @@ -65,14 +68,32 @@ class Hanime1 : AnimeHttpSource(), ConfigurableAnimeSource { } override fun animeDetailsParse(response: Response): SAnime { - val jsoup = response.asJsoup() + val doc = response.asJsoup() return SAnime.create().apply { - genre = jsoup.select(".single-video-tag").not("[data-toggle]").eachText().joinToString() - author = jsoup.select("#video-artist-name").text() - jsoup.select("script[type=application/ld+json]").first()?.data()?.let { + genre = doc.select(".single-video-tag").not("[data-toggle]").eachText().joinToString() + author = doc.select("#video-artist-name").text() + doc.select("script[type=application/ld+json]").first()?.data()?.let { val info = json.decodeFromString(it).jsonObject title = info["name"]!!.jsonPrimitive.content description = info["description"]!!.jsonPrimitive.content + thumbnail_url = info["thumbnailUrl"]?.jsonArray?.get(0)?.jsonPrimitive?.content + } + val type = doc.select("a#video-artist-name + a").text().trim() + if (type == "裏番" || type == "泡麵番") { + // Use the series cover image for bangumi entries instead of the episode image. + runBlocking { + try { + val animesPage = + getSearchAnime( + 1, + title, + AnimeFilterList(GenreFilter(arrayOf("", type)).apply { state = 1 }), + ) + thumbnail_url = animesPage.animes.first().thumbnail_url + } catch (e: Exception) { + Log.e(name, "Failed to get bangumi cover image") + } + } } } } @@ -137,7 +158,7 @@ class Hanime1 : AnimeHttpSource(), ConfigurableAnimeSource { override fun searchAnimeParse(response: Response): AnimesPage { val jsoup = response.asJsoup() - val nodes = jsoup.select("div.search-doujin-videos.hidden-xs") + val nodes = jsoup.select("div.search-doujin-videos.hidden-xs:not(:has(a[target=_blank]))") val list = if (nodes.isNotEmpty()) { nodes.map { SAnime.create().apply { -- 2.47.2 From ae982dcfd5f6fc2994bbdba50e9f85e882603746 Mon Sep 17 00:00:00 2001 From: AlphaBoom <30779939+AlphaBoom@users.noreply.github.com> Date: Tue, 3 Jun 2025 12:44:20 +0800 Subject: [PATCH 2/3] Xfani: Update domain (#3) --- src/zh/xfani/build.gradle | 2 +- .../animeextension/zh/xfani/Xfani.kt | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/zh/xfani/build.gradle b/src/zh/xfani/build.gradle index 89b38f74..937a972a 100644 --- a/src/zh/xfani/build.gradle +++ b/src/zh/xfani/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Xfani' extClass = '.Xfani' - extVersionCode = 5 + extVersionCode = 6 } apply from: "$rootDir/common.gradle" diff --git a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt index 92583f32..eeb802e9 100644 --- a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt +++ b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt @@ -53,7 +53,7 @@ enum class FilterUpdateState { class Xfani : AnimeHttpSource(), ConfigurableAnimeSource { override val baseUrl: String - get() = "https://dick.xfani.com" + get() = "https://dm.xifanacg.com" override val lang: String get() = "zh" override val name: String @@ -117,10 +117,16 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource { } override fun animeDetailsParse(response: Response): SAnime { - val jsoup = response.asJsoup() + val doc = response.asJsoup() return SAnime.create().apply { - description = jsoup.select("#height_limit.text").text() - title = jsoup.select(".slide-info-title").text() + description = doc.select("#height_limit.text").text() + title = doc.select(".slide-info-title").text() + author = doc.select(".slide-info:contains(导演 :)").text().removePrefix("导演 :") + .removeSuffix(",") + artist = doc.select(".slide-info:contains(演员 :)").text().removePrefix("演员 :") + .removeSuffix(",") + genre = doc.select(".slide-info:contains(类型 :)").text().removePrefix("类型 :") + .removeSuffix(",").replace(",", ", ") } } @@ -234,19 +240,13 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource { return vodListToAnimePageList(response) } val jsoup = response.asJsoup() - val items = jsoup.select("div.public-list-box.search-box.flex.rel") + val items = jsoup.select("div.search-list") val animeList = items.map { item -> SAnime.create().apply { - title = item.select(".thumb-txt").text() - url = item.select("div.left.public-list-bj a.public-list-exp").attr("href") + title = item.select("div.detail-info > a").text() + url = item.select("div.detail-info > a").attr("href") thumbnail_url = - item.select("div.left.public-list-bj img[data-src]").attr("data-src") - author = item.select("div.thumb-actor").text().removeSuffix("/") - artist = item.select("div.thumb-director").text().removeSuffix("/") - description = item.select(".thumb-blurb").text() - genre = item.select("div.thumb-else").text() - val statusString = item.select("div.left.public-list-bj .public-list-prb").text() - status = STATUS_STR_MAPPING.getOrElse(statusString) { SAnime.ONGOING } + item.select("div.detail-pic img[data-src]").attr("data-src") } } val tip = jsoup.select("div.pages div.page-tip").text() -- 2.47.2 From fc7e6bee6523c3f5266ce2b37104e758910adf56 Mon Sep 17 00:00:00 2001 From: ZhendongWu <30779939+AlphaBoom@users.noreply.github.com> Date: Fri, 6 Jun 2025 21:58:14 +0800 Subject: [PATCH 3/3] perf: remove custom coroutine scope --- .../tachiyomi/animeextension/zh/hanime1/Hanime1.kt | 6 ++++-- .../kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt | 10 ++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/zh/hanime1/src/eu/kanade/tachiyomi/animeextension/zh/hanime1/Hanime1.kt b/src/zh/hanime1/src/eu/kanade/tachiyomi/animeextension/zh/hanime1/Hanime1.kt index f3bdfdb9..311e7597 100644 --- a/src/zh/hanime1/src/eu/kanade/tachiyomi/animeextension/zh/hanime1/Hanime1.kt +++ b/src/zh/hanime1/src/eu/kanade/tachiyomi/animeextension/zh/hanime1/Hanime1.kt @@ -17,8 +17,9 @@ import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.util.asJsoup import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.serialization.encodeToString @@ -237,11 +238,12 @@ class Hanime1 : AnimeHttpSource(), ConfigurableAnimeSource { return chain.proceed(chain.request()) } + @OptIn(DelicateCoroutinesApi::class) private fun updateFilters() { filterUpdateState = FilterUpdateState.UPDATING val exceptionHandler = CoroutineExceptionHandler { _, _ -> filterUpdateState = FilterUpdateState.FAILED } - CoroutineScope(Dispatchers.IO + exceptionHandler).launch { + GlobalScope.launch(Dispatchers.IO + exceptionHandler) { val jsoup = client.newCall(GET("$baseUrl/search")).awaitSuccess().asJsoup() val genreList = jsoup.select("div.genre-option div.hentai-sort-options").eachText() val sortList = diff --git a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt index eeb802e9..add3a049 100644 --- a/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt +++ b/src/zh/xfani/src/eu/kanade/tachiyomi/animeextension/zh/xfani/Xfani.kt @@ -20,8 +20,9 @@ import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.util.asJsoup import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject @@ -259,12 +260,13 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource { return numbers.size == 2 && numbers[0] != numbers[1] } + @OptIn(DelicateCoroutinesApi::class) private fun updateFilter() { filterState = FilterUpdateState.UPDATING val handler = CoroutineExceptionHandler { _, _ -> filterState = FilterUpdateState.FAILED } - CoroutineScope(Dispatchers.IO + handler).launch { + GlobalScope.launch(Dispatchers.IO + handler) { val jsoup = client.newCall(GET("$baseUrl/show/1/html")).awaitSuccess().asJsoup() // update class and year filter type val classList = jsoup.select("li[data-type=class]").eachAttr("data-val") @@ -393,9 +395,5 @@ class Xfani : AnimeHttpSource(), ConfigurableAnimeSource { const val PREF_KEY_FILTER_YEAR = "PREF_KEY_FILTER_YEAR" const val DEFAULT_VIDEO_SOURCE = "0" - - val STATUS_STR_MAPPING = mapOf( - "已完结" to SAnime.COMPLETED, - ) } } -- 2.47.2