refactor(multisrc/animestream): Make multisrc a bit more generic and customizable (#1775)

This commit is contained in:
Claudemirovsky
2023-06-23 13:07:58 +00:00
committed by GitHub
parent 82fc9a4b80
commit fe4332fa74
4 changed files with 69 additions and 60 deletions

View File

@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.animeextension.en.animenosub.extractors.StreamWishExt
import eu.kanade.tachiyomi.animeextension.en.animenosub.extractors.VidMolyExtractor
import eu.kanade.tachiyomi.animeextension.en.animenosub.extractors.VtubeExtractor
import eu.kanade.tachiyomi.animeextension.en.animenosub.extractors.WolfstreamExtractor
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.streamsbextractor.StreamSBExtractor
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
@ -18,23 +17,11 @@ class Animenosub : AnimeStream(
"Animenosub",
"https://animenosub.com",
) {
// ============================== Popular ===============================
override fun popularAnimeSelector() = "div.serieslist.wpop-weekly li"
// ============================== Episodes ==============================
override fun episodeFromElement(element: Element): SEpisode {
override fun getEpisodeName(element: Element, epNum: String): String {
val episodeTitle = element.selectFirst("div.epl-title")?.text() ?: ""
return SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
element.selectFirst("div.epl-num")!!.text().let {
name = "Ep. $it ${if (episodeTitle.contains("Episode $it", true)) "" else episodeTitle}"
episode_number = it.substringBefore(" ").toFloatOrNull() ?: 0F
}
element.selectFirst("div.epl-sub")?.text()?.let { scanlator = it }
date_upload = element.selectFirst("div.epl-date")?.text().toDate()
}
val complement = if (episodeTitle.contains("Episode $epNum", true)) "" else episodeTitle
return "Ep. $epNum $complement"
}
// ============================ Video Links =============================

View File

@ -85,21 +85,13 @@ abstract class AnimeStream(
return super.fetchPopularAnime(page)
}
override fun popularAnimeFromElement(element: Element): SAnime {
return SAnime.create().apply {
val ahref = element.selectFirst("h4 > a.series")!!
setUrlWithoutDomain(ahref.attr("href"))
title = ahref.text()
thumbnail_url = element.selectFirst("img")!!.getImageUrl()
}
}
override fun popularAnimeRequest(page: Int) = GET("$animeListUrl/?page=$page&order=popular")
override fun popularAnimeNextPageSelector() = null
override fun popularAnimeSelector() = searchAnimeSelector()
override fun popularAnimeRequest(page: Int) = GET(baseUrl)
override fun popularAnimeNextPageSelector() = searchAnimeNextPageSelector()
/* Possible classes: wpop-weekly, wpop-monthly, wpop-alltime */
override fun popularAnimeSelector() = "div.serieslist.wpop-alltime li"
override fun popularAnimeFromElement(element: Element) = searchAnimeFromElement(element)
// ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> {
@ -112,40 +104,67 @@ abstract class AnimeStream(
else -> "Episode"
}
@Suppress("unused_parameter")
protected open fun getEpisodeName(element: Element, epNum: String) = "$episodePrefix $epNum"
override fun episodeFromElement(element: Element): SEpisode {
return SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
element.selectFirst("div.epl-num")!!.text().let {
name = "$episodePrefix $it"
element.selectFirst(".epl-num")!!.text().let {
name = getEpisodeName(element, it)
episode_number = it.substringBefore(" ").toFloatOrNull() ?: 0F
}
element.selectFirst("div.epl-sub")?.text()?.let { scanlator = it }
date_upload = element.selectFirst("div.epl-date")?.text().toDate()
element.selectFirst(".epl-sub")?.text()?.let { scanlator = it }
date_upload = element.selectFirst(".epl-date")?.text().toDate()
}
}
override fun episodeListSelector() = "div.eplister > ul > li > a"
// =========================== Anime Details ============================
protected open val animeDetailsSelector = "div.info-content, div.right ul.data"
protected open val animeAltNameSelector = ".alter"
protected open val animeTitleSelector = "h1.entry-title"
protected open val animeThumbnailSelector = "div.thumb > img, div.limage > img"
protected open val animeGenresSelector = "div.genxed > a, li:contains(Genre:) a"
protected open val animeDescriptionSelector = ".entry-content[itemprop=description], .desc"
protected open val animeAdditionalInfoSelector = "div.spe > span, li:has(b)"
protected open val animeStatusText = "Status"
protected open val animeArtistText = "tudio"
protected open val animeAuthorText = "Fansub"
protected open val animeAltNamePrefix = when (lang) {
"pt-BR" -> "Nome(s) alternativo(s): "
else -> "Alternative name(s): "
}
protected open fun getAnimeDescription(document: Document) =
document.selectFirst(animeDescriptionSelector)?.text()
override fun animeDetailsParse(document: Document): SAnime {
return SAnime.create().apply {
setUrlWithoutDomain(document.location())
title = document.selectFirst("h1.entry-title")!!.text()
thumbnail_url = document.selectFirst("div.thumb > img")!!.getImageUrl()
title = document.selectFirst(animeTitleSelector)!!.text()
thumbnail_url = document.selectFirst(animeThumbnailSelector)!!.getImageUrl()
val infos = document.selectFirst("div.info-content")!!
genre = infos.select("div.genxed > a").eachText().joinToString()
val infos = document.selectFirst(animeDetailsSelector)!!
genre = infos.select(animeGenresSelector).eachText().joinToString()
status = parseStatus(infos.getInfo("Status"))
artist = infos.getInfo("tudio")
author = infos.getInfo("Fansub")
status = parseStatus(infos.getInfo(animeStatusText))
artist = infos.getInfo(animeArtistText)
author = infos.getInfo(animeAuthorText)
description = buildString {
document.selectFirst("div.entry-content")?.text()?.let {
getAnimeDescription(document)?.let {
append("$it\n\n")
}
infos.select("div.spe > span").eachText().forEach {
document.selectFirst(animeAltNameSelector)?.text()
?.takeIf(String::isNotBlank)
?.let { append("$animeAltNamePrefix$it\n") }
infos.select(animeAdditionalInfoSelector).eachText().forEach {
append("$it\n")
}
}
@ -153,7 +172,7 @@ abstract class AnimeStream(
}
// ============================ Video Links =============================
override fun videoListSelector() = "select.mirror > option[data-index]"
override fun videoListSelector() = "select.mirror > option[data-index], ul.mirror a[data-em]"
override fun videoListParse(response: Response): List<Video> {
val items = response.asJsoup().select(videoListSelector())
@ -167,7 +186,17 @@ abstract class AnimeStream(
}
protected open fun getHosterUrl(element: Element): String {
return Base64.decode(element.attr("value"), Base64.DEFAULT)
val encodedData = when (element.tagName()) {
"option" -> element.attr("value")
"a" -> element.attr("data-em")
else -> throw Exception()
}
return getHosterUrl(encodedData)
}
protected open fun getHosterUrl(encodedData: String): String {
return Base64.decode(encodedData, Base64.DEFAULT)
.let(::String) // bytearray -> string
.let(Jsoup::parse) // string -> document
.selectFirst("iframe[src~=.]")!!
@ -193,7 +222,7 @@ abstract class AnimeStream(
override fun searchAnimeFromElement(element: Element): SAnime {
return SAnime.create().apply {
setUrlWithoutDomain(element.attr("href"))
title = element.selectFirst("div.tt")!!.ownText()
title = element.selectFirst("div.tt, div.ttl")!!.ownText()
thumbnail_url = element.selectFirst("img")!!.getImageUrl()
}
}
@ -211,7 +240,7 @@ abstract class AnimeStream(
if (params.studios.isNotEmpty()) append(params.studios + "&")
}
GET("$baseUrl/anime/?page=$page&$multiString&status=${params.status}&type=${params.type}&sub=${params.sub}&order=${params.order}")
GET("$animeListUrl/?page=$page&$multiString&status=${params.status}&type=${params.type}&sub=${params.sub}&order=${params.order}")
}
}
@ -274,13 +303,15 @@ abstract class AnimeStream(
*/
protected open val fetchFilters = true
protected open val filtersSelector = "span.sec1 > div.filter > ul"
private fun fetchFilterList() {
if (fetchFilters && !AnimeStreamFilters.filterInitialized()) {
AnimeStreamFilters.filterElements = runBlocking {
withContext(Dispatchers.IO) {
client.newCall(GET(animeListUrl)).execute()
.asJsoup()
.select("span.sec1 > div.filter > ul")
.select(filtersSelector)
}
}
}

View File

@ -33,19 +33,10 @@ object AnimeStreamFilters {
name: String,
): String {
return (getFirst<R>() as CheckBoxFilterList).state
.mapNotNull { checkbox ->
when {
checkbox.state -> {
options.find { it.first == checkbox.name }!!.second
}
else -> null
}
}.joinToString("&$name[]=").let {
when {
it.isBlank() -> ""
else -> "$name[]=$it"
}
}
.filter { it.state }
.map { checkbox -> options.find { it.first == checkbox.name }!!.second }
.filter(String::isNotBlank)
.joinToString("&") { "$name[]=$it" }
}
class GenresFilter(name: String) : CheckBoxFilterList(name, GENRES_LIST)

View File

@ -8,7 +8,7 @@ class AnimeStreamGenerator : ThemeSourceGenerator {
override val themeClass = "AnimeStream"
override val baseVersionCode = 1
override val baseVersionCode = 2
override val sources = listOf(
SingleLang("AnimeXin", "https://animexin.vip", "all", isNsfw = false, overrideVersionCode = 4),