refactor(it): refactoring of some italian extensions (#1776)

This commit is contained in:
Secozzi
2023-06-23 20:41:24 +02:00
committed by GitHub
parent fe4332fa74
commit 2bbd7d9358
6 changed files with 249 additions and 270 deletions

View File

@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
@ -46,15 +45,13 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun popularAnimeSelector(): String = "div.containerlista > div.row > div.col-6" override fun popularAnimeSelector(): String = "div.containerlista > div.row > div.col-6"
override fun popularAnimeNextPageSelector(): String = "div > ul.page-nav > li:last-child:not(:has(a.disabled))" override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
override fun popularAnimeFromElement(element: Element): SAnime {
return SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href").toHttpUrl().encodedPath)
thumbnail_url = element.selectFirst("img")?.attr("src") thumbnail_url = element.selectFirst("img")?.attr("src")
title = element.selectFirst("div.default-text")!!.text() title = element.selectFirst("div.default-text")!!.text()
} }
}
override fun popularAnimeNextPageSelector(): String = "div > ul.page-nav > li:last-child:not(:has(a.disabled))"
// =============================== Latest =============================== // =============================== Latest ===============================
@ -62,10 +59,10 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun latestUpdatesSelector(): String = popularAnimeSelector() override fun latestUpdatesSelector(): String = popularAnimeSelector()
override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector()
override fun latestUpdatesFromElement(element: Element): SAnime = popularAnimeFromElement(element) override fun latestUpdatesFromElement(element: Element): SAnime = popularAnimeFromElement(element)
override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector()
// =============================== Search =============================== // =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
@ -89,30 +86,26 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return super.searchAnimeParse(response) return super.searchAnimeParse(response)
} }
val document = response.asJsoup() val animeList = response.asJsoup()
.select(searchAnimeSelectorSearch())
.map(::searchAnimeFromElementSearch)
val animes = document.select(searchAnimeSelectorSearch()).map { element -> return AnimesPage(animeList, false)
searchAnimeFromElementSearch(element)
}
return AnimesPage(animes, false)
} }
override fun searchAnimeSelector(): String = popularAnimeSelector() override fun searchAnimeSelector(): String = popularAnimeSelector()
private fun searchAnimeSelectorSearch(): String = "div.col-md-8 > div.card > div.card-body > div.row > div.col-6" private fun searchAnimeSelectorSearch(): String = "div.col-md-8 > div.card > div.card-body > div.row > div.col-6"
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector() override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
private fun searchAnimeFromElementSearch(element: Element): SAnime { private fun searchAnimeFromElementSearch(element: Element): SAnime = SAnime.create().apply {
return SAnime.create().apply { setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href").toHttpUrl().encodedPath)
thumbnail_url = element.selectFirst("img")?.attr("src") thumbnail_url = element.selectFirst("img")?.attr("src")
title = element.selectFirst("p.card-text")!!.text() title = element.selectFirst("p.card-text")!!.text()
} }
}
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element) override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
// ============================== Filters =============================== // ============================== Filters ===============================
@ -238,37 +231,33 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// =========================== Anime Details ============================ // =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime { override fun animeDetailsParse(document: Document): SAnime = SAnime.create().apply {
val moreInfo = (document.selectFirst("div.card-body > p:contains(TIPO:)")?.text() ?: "") +
"\n" +
(document.selectFirst("div.card-body > p:contains(ANNO)")?.text() ?: "")
return SAnime.create().apply {
title = document.selectFirst("div.card-body > p:contains(TITOLO:)")?.ownText() ?: "" title = document.selectFirst("div.card-body > p:contains(TITOLO:)")?.ownText() ?: ""
thumbnail_url = document.selectFirst("div.card-body > div > img")?.attr("src") ?: "" thumbnail_url = document.selectFirst("div.card-body > div > img")?.attr("src") ?: ""
author = document.selectFirst("div.card-body > p:contains(STUDIO:)")?.ownText() ?: "" author = document.selectFirst("div.card-body > p:contains(STUDIO:)")?.ownText() ?: ""
status = document.selectFirst("div.card-body > p:contains(STATO:)")?.let { status = document.selectFirst("div.card-body > p:contains(STATO:)")?.let {
parseStatus(it.ownText()) parseStatus(it.ownText())
} ?: SAnime.UNKNOWN } ?: SAnime.UNKNOWN
description = (document.selectFirst("div.card-body > p:contains(TRAMA:) ~ p")?.text() ?: "") + "\n\n$moreInfo"
genre = document.selectFirst("div.card-body > p:contains(GENERI:)")?.ownText() ?: "" genre = document.selectFirst("div.card-body > p:contains(GENERI:)")?.ownText() ?: ""
description = buildString {
append(document.selectFirst("div.card-body > p:contains(TRAMA:) ~ p")?.text() ?: "")
append("\n\n")
append(document.selectFirst("div.card-body > p:contains(TIPO:)")?.text() ?: "")
append("\n")
append(document.selectFirst("div.card-body > p:contains(ANNO)")?.text() ?: "")
} }
} }
// ============================== Episodes ============================== // ============================== Episodes ==============================
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> = super.episodeListParse(response).reversed()
return super.episodeListParse(response).reversed()
}
override fun episodeListSelector(): String = "div.card ul.page-nav-list-episodi > li" override fun episodeListSelector(): String = "div.card ul.page-nav-list-episodi > li"
override fun episodeFromElement(element: Element): SEpisode { override fun episodeFromElement(element: Element): SEpisode = SEpisode.create().apply {
return SEpisode.create().apply {
name = "Episodi ${element.text()}" name = "Episodi ${element.text()}"
episode_number = element.selectFirst("span")?.text()?.toFloatOrNull() ?: 0F episode_number = element.selectFirst("span")?.text()?.toFloatOrNull() ?: 0F
setUrlWithoutDomain(element.selectFirst("a[href]")!!.attr("href").toHttpUrl().encodedPath) setUrlWithoutDomain(element.selectFirst("a[href]")!!.attr("href"))
}
} }
// ============================ Video Links ============================= // ============================ Video Links =============================
@ -301,13 +290,15 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
} }
require(videoList.isNotEmpty()) { "Failed to fetch videos" }
return videoList return videoList
} }
override fun videoFromElement(element: Element): Video = throw Exception("Not Used")
override fun videoListSelector(): String = throw Exception("Not Used") override fun videoListSelector(): String = throw Exception("Not Used")
override fun videoFromElement(element: Element): Video = throw Exception("Not Used")
override fun videoUrlParse(document: Document): String = throw Exception("Not Used") override fun videoUrlParse(document: Document): String = throw Exception("Not Used")
// ============================= Utilities ============================== // ============================= Utilities ==============================
@ -328,7 +319,7 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
override fun List<Video>.sort(): List<Video> { override fun List<Video>.sort(): List<Video> {
val server = preferences.getString("preferred_server", "AnimeLove")!! val server = preferences.getString(PREF_SERVER_KEY, PREF_SERVER_DEFAULT)!!
return this.sortedWith( return this.sortedWith(
compareBy { it.quality.contains(server, true) }, compareBy { it.quality.contains(server, true) },
@ -343,13 +334,20 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} }
} }
companion object {
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "AnimeLove"
}
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoServerPref = ListPreference(screen.context).apply { ListPreference(screen.context).apply {
key = "preferred_server" key = PREF_SERVER_KEY
title = "Preferred server" title = "Preferred server"
entries = arrayOf("AnimeLove", "StreamTape") entries = arrayOf("AnimeLove", "StreamTape")
entryValues = arrayOf("AnimeLove", "StreamTape") entryValues = arrayOf("AnimeLove", "StreamTape")
setDefaultValue("AnimeLove") setDefaultValue(PREF_SERVER_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -358,7 +356,6 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val entry = entryValues[index] as String val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit() preferences.edit().putString(key, entry).commit()
} }
} }.also(screen::addPreference)
screen.addPreference(videoServerPref)
} }
} }

View File

@ -2,6 +2,8 @@ package eu.kanade.tachiyomi.animeextension.it.animeunity
import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
object AnimeUnityFilters { object AnimeUnityFilters {
@ -28,7 +30,7 @@ object AnimeUnityFilters {
class GenreFilter : CheckBoxFilterList( class GenreFilter : CheckBoxFilterList(
"Genere", "Genere",
AnimeUnityFiltersData.GENERE.map { CheckBoxVal(it.first, false) }, AnimeUnityFiltersData.GENRE.map { CheckBoxVal(it.first, false) },
) )
class YearFilter : QueryPartFilter("Anno", AnimeUnityFiltersData.YEAR) class YearFilter : QueryPartFilter("Anno", AnimeUnityFiltersData.YEAR)
@ -73,15 +75,12 @@ object AnimeUnityFilters {
val genre: String = filters.filterIsInstance<GenreFilter>() val genre: String = filters.filterIsInstance<GenreFilter>()
.first() .first()
.state.mapNotNull { format -> .state.filter { it.state }.joinToString(",") { format ->
if (format.state) { buildJsonObject {
"{\"id\":" + put("id", AnimeUnityFiltersData.GENRE.find { it.first == format.name }!!.second.toInt())
AnimeUnityFiltersData.GENERE.find { it.first == format.name }!!.second + put("name", AnimeUnityFiltersData.GENRE.find { it.first == format.name }!!.first)
",\"name\":\"" + }.toString()
AnimeUnityFiltersData.GENERE.find { it.first == format.name }!!.first + }
"\"}"
} else { null }
}.joinToString(",")
return FilterSearchParams( return FilterSearchParams(
filters.asQueryPart<TopFilter>(), filters.asQueryPart<TopFilter>(),
@ -113,7 +112,7 @@ object AnimeUnityFilters {
Pair("Più visti", "top-anime?order=most_viewed"), Pair("Più visti", "top-anime?order=most_viewed"),
) )
val GENERE = arrayOf( val GENRE = arrayOf(
Pair("Action", "51"), Pair("Action", "51"),
Pair("Adventure", "21"), Pair("Adventure", "21"),
Pair("Cars", "29"), Pair("Cars", "29"),

View File

@ -76,33 +76,33 @@ class AniPlay : ConfigurableAnimeSource, AnimeHttpSource() {
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw Exception("Not used") override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw Exception("Not used")
private fun searchAnimeRequest(page: Int, query: String, filters: AniPlayFilters.FilterSearchParams): Request { private fun searchAnimeRequest(page: Int, query: String, filters: AniPlayFilters.FilterSearchParams): Request {
if ((filters.anni.isNotEmpty() && filters.stagione.isEmpty()) || if ((filters.year.isNotEmpty() && filters.season.isEmpty()) ||
(filters.anni.isEmpty() && filters.stagione.isNotEmpty()) (filters.year.isEmpty() && filters.season.isNotEmpty())
) { ) {
throw Exception("Per gli anime stagionali, seleziona sia l'anno che la stagione") error("Per gli anime stagionali, seleziona sia l'anno che la stagione")
} }
val url = if (filters.anni.isNotEmpty()) { val url = if (filters.year.isNotEmpty()) {
"$baseUrl/api/seasonal-view".toHttpUrlOrNull()!!.newBuilder() "$baseUrl/api/seasonal-view".toHttpUrlOrNull()!!.newBuilder()
.addPathSegment("${filters.stagione}-${filters.anni}") .addPathSegment("${filters.season}-${filters.year}")
.addQueryParameter("page", (page - 1).toString()) .addQueryParameter("page", (page - 1).toString())
.addQueryParameter("size", "36") .addQueryParameter("size", "36")
.addQueryParameter("sort", filters.ordina) .addQueryParameter("sort", filters.order)
.addQueryParameter("sort", "id") .addQueryParameter("sort", "id")
} else { } else {
"$baseUrl/api/anime/advanced-similar-search".toHttpUrlOrNull()!!.newBuilder() "$baseUrl/api/anime/advanced-similar-search".toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("page", (page - 1).toString()) .addQueryParameter("page", (page - 1).toString())
.addQueryParameter("size", "36") .addQueryParameter("size", "36")
.addQueryParameter("sort", filters.ordina) .addQueryParameter("sort", filters.order)
.addQueryParameter("sort", "id") .addQueryParameter("sort", "id")
.addIfNotBlank("query", query) .addIfNotBlank("query", query)
.addIfNotBlank("genreIds", filters.genere) .addIfNotBlank("genreIds", filters.genre)
.addIfNotBlank("typeIds", filters.tipologia) .addIfNotBlank("typeIds", filters.type)
.addIfNotBlank("statusIds", filters.stato) .addIfNotBlank("statusIds", filters.status)
.addIfNotBlank("originIds", filters.origine) .addIfNotBlank("originIds", filters.origin)
.addIfNotBlank("studioIds", filters.studio) .addIfNotBlank("studioIds", filters.studio)
.addIfNotBlank("startYear", filters.inizio) .addIfNotBlank("startYear", filters.start)
.addIfNotBlank("endYear", filters.fine) .addIfNotBlank("endYear", filters.end)
} }
return GET(url.build().toString()) return GET(url.build().toString())

View File

@ -24,24 +24,24 @@ object AniPlayFilters {
} }
} }
class GenereFilter : CheckBoxFilterList( class GenreFilter : CheckBoxFilterList(
"Genere", "Genere",
AniPlayFiltersData.GENERE.map { CheckBoxVal(it.first, false) }, AniPlayFiltersData.GENRE.map { CheckBoxVal(it.first, false) },
) )
class TipologiaFilter : CheckBoxFilterList( class TypeFilter : CheckBoxFilterList(
"Tipologia anime", "Tipologia anime",
AniPlayFiltersData.TIPOLOGIA.map { CheckBoxVal(it.first, false) }, AniPlayFiltersData.TYPE.map { CheckBoxVal(it.first, false) },
) )
class StatoFilter : CheckBoxFilterList( class StatusFilter : CheckBoxFilterList(
"Stato", "Stato",
AniPlayFiltersData.STATO.map { CheckBoxVal(it.first, false) }, AniPlayFiltersData.STATUS.map { CheckBoxVal(it.first, false) },
) )
class OrigineFilter : CheckBoxFilterList( class OriginFilter : CheckBoxFilterList(
"Origine", "Origine",
AniPlayFiltersData.ORIGINE.map { CheckBoxVal(it.first, false) }, AniPlayFiltersData.ORIGIN.map { CheckBoxVal(it.first, false) },
) )
class StudioFilter : CheckBoxFilterList( class StudioFilter : CheckBoxFilterList(
@ -49,110 +49,105 @@ object AniPlayFilters {
AniPlayFiltersData.STUDIO.map { CheckBoxVal(it.first, false) }, AniPlayFiltersData.STUDIO.map { CheckBoxVal(it.first, false) },
) )
class InizioFilter : QueryPartFilter("Inizio", AniPlayFiltersData.ANNI) class StartFilter : QueryPartFilter("Inizio", AniPlayFiltersData.YEAR)
class FineFilter : QueryPartFilter("Fine", AniPlayFiltersData.ANNI) class EndFilter : QueryPartFilter("Fine", AniPlayFiltersData.YEAR)
class OrdinaFilter : QueryPartFilter("Ordina per", AniPlayFiltersData.ORDINA) class OrderFilter : QueryPartFilter("Ordina per", AniPlayFiltersData.ORDER)
class AnniFilter : QueryPartFilter("Anni", AniPlayFiltersData.ANNI) class YearFilter : QueryPartFilter("Anni", AniPlayFiltersData.YEAR)
class StagioneFilter : QueryPartFilter("Stagione", AniPlayFiltersData.STAGIONE) class SeasonFilter : QueryPartFilter("Stagione", AniPlayFiltersData.SEASON)
val FILTER_LIST get() = AnimeFilterList( val FILTER_LIST get() = AnimeFilterList(
OrdinaFilter(), OrderFilter(),
AnimeFilter.Separator(), AnimeFilter.Separator(),
GenereFilter(), GenreFilter(),
TipologiaFilter(), TypeFilter(),
StatoFilter(), StatusFilter(),
OrigineFilter(), OriginFilter(),
StudioFilter(), StudioFilter(),
AnimeFilter.Separator(), AnimeFilter.Separator(),
AnimeFilter.Header("Anni"), AnimeFilter.Header("Anni"),
InizioFilter(), StartFilter(),
FineFilter(), EndFilter(),
AnimeFilter.Separator(), AnimeFilter.Separator(),
AnimeFilter.Header("Anime stagionali"), AnimeFilter.Header("Anime stagionali"),
AnimeFilter.Header("(ignora altri filtri tranne l'ordinamento per)"), AnimeFilter.Header("(ignora altri filtri tranne l'ordinamento per)"),
AnniFilter(), YearFilter(),
StagioneFilter(), SeasonFilter(),
) )
data class FilterSearchParams( data class FilterSearchParams(
val ordina: String = "views,desc", val order: String = "views,desc",
val genere: String = "", val genre: String = "",
val tipologia: String = "", val type: String = "",
val stato: String = "", val status: String = "",
val origine: String = "", val origin: String = "",
val studio: String = "", val studio: String = "",
val inizio: String = "", val start: String = "",
val fine: String = "", val end: String = "",
val anni: String = "", val year: String = "",
val stagione: String = "", val season: String = "",
) )
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams { internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams() if (filters.isEmpty()) return FilterSearchParams()
val genere: String = filters.filterIsInstance<GenereFilter>() val genre: String = filters.filterIsInstance<GenreFilter>()
.first() .first()
.state.mapNotNull { format -> .state.filter { it.state }
if (format.state) { .joinToString(",") { format ->
AniPlayFiltersData.GENERE.find { it.first == format.name }!!.second AniPlayFiltersData.GENRE.find { it.first == format.name }!!.second
} else { null } }
}.joinToString(",")
val tipologia: String = filters.filterIsInstance<TipologiaFilter>() val type: String = filters.filterIsInstance<TypeFilter>()
.first() .first()
.state.mapNotNull { format -> .state.filter { it.state }
if (format.state) { .joinToString(",") { format ->
AniPlayFiltersData.TIPOLOGIA.find { it.first == format.name }!!.second AniPlayFiltersData.TYPE.find { it.first == format.name }!!.second
} else { null } }
}.joinToString(",")
val stato: String = filters.filterIsInstance<StatoFilter>() val status: String = filters.filterIsInstance<StatusFilter>()
.first() .first()
.state.mapNotNull { format -> .state.filter { it.state }
if (format.state) { .joinToString(",") { format ->
AniPlayFiltersData.STATO.find { it.first == format.name }!!.second AniPlayFiltersData.STATUS.find { it.first == format.name }!!.second
} else { null } }
}.joinToString(",")
val origine: String = filters.filterIsInstance<OrigineFilter>() val origin: String = filters.filterIsInstance<OriginFilter>()
.first() .first()
.state.mapNotNull { format -> .state.filter { it.state }
if (format.state) { .joinToString(",") { format ->
AniPlayFiltersData.ORIGINE.find { it.first == format.name }!!.second AniPlayFiltersData.ORIGIN.find { it.first == format.name }!!.second
} else { null } }
}.joinToString(",")
val studio: String = filters.filterIsInstance<StudioFilter>() val studio: String = filters.filterIsInstance<StudioFilter>()
.first() .first()
.state.mapNotNull { format -> .state.filter { it.state }
if (format.state) { .joinToString(",") { format ->
AniPlayFiltersData.STUDIO.find { it.first == format.name }!!.second AniPlayFiltersData.STUDIO.find { it.first == format.name }!!.second
} else { null } }
}.joinToString(",")
return FilterSearchParams( return FilterSearchParams(
filters.asQueryPart<OrdinaFilter>(), filters.asQueryPart<OrderFilter>(),
genere, genre,
tipologia, type,
stato, status,
origine, origin,
studio, studio,
filters.asQueryPart<InizioFilter>(), filters.asQueryPart<StartFilter>(),
filters.asQueryPart<FineFilter>(), filters.asQueryPart<EndFilter>(),
filters.asQueryPart<AnniFilter>(), filters.asQueryPart<YearFilter>(),
filters.asQueryPart<StagioneFilter>(), filters.asQueryPart<SeasonFilter>(),
) )
} }
private object AniPlayFiltersData { private object AniPlayFiltersData {
val ALL = Pair("All", "") val ALL = Pair("All", "")
val ORDINA = arrayOf( val ORDER = arrayOf(
Pair("Popolarità decrescente", "views,desc"), Pair("Popolarità decrescente", "views,desc"),
Pair("Popolarità crescente", "views,asc"), Pair("Popolarità crescente", "views,asc"),
Pair("Titolo decrescente", "title,desc"), Pair("Titolo decrescente", "title,desc"),
@ -167,7 +162,7 @@ object AniPlayFilters {
Pair("Data di fine aggiunta", "createdDate,asc"), Pair("Data di fine aggiunta", "createdDate,asc"),
) )
val GENERE = arrayOf( val GENRE = arrayOf(
Pair("Arti marziali", "68"), Pair("Arti marziali", "68"),
Pair("Automobilismo", "90"), Pair("Automobilismo", "90"),
Pair("Avventura", "25"), Pair("Avventura", "25"),
@ -222,7 +217,7 @@ object AniPlayFilters {
Pair("Yuri", "50"), Pair("Yuri", "50"),
) )
val TIPOLOGIA = arrayOf( val TYPE = arrayOf(
Pair("Movie", "2"), Pair("Movie", "2"),
Pair("ONA", "4"), Pair("ONA", "4"),
Pair("OVA", "3"), Pair("OVA", "3"),
@ -230,7 +225,7 @@ object AniPlayFilters {
Pair("Special", "5"), Pair("Special", "5"),
) )
val STATO = arrayOf( val STATUS = arrayOf(
Pair("Annunciato", "4"), Pair("Annunciato", "4"),
Pair("Completato", "1"), Pair("Completato", "1"),
Pair("In corso", "2"), Pair("In corso", "2"),
@ -238,7 +233,7 @@ object AniPlayFilters {
Pair("Sospeso", "3"), Pair("Sospeso", "3"),
) )
val ORIGINE = arrayOf( val ORIGIN = arrayOf(
Pair("Gioco di carte", "1"), Pair("Gioco di carte", "1"),
Pair("Light novel", "2"), Pair("Light novel", "2"),
Pair("Mange", "3"), Pair("Mange", "3"),
@ -568,7 +563,7 @@ object AniPlayFilters {
Pair("Zexcs", "259"), Pair("Zexcs", "259"),
) )
val STAGIONE = arrayOf( val SEASON = arrayOf(
ALL, ALL,
Pair("Inverno", "winter"), Pair("Inverno", "winter"),
Pair("Primavera", "spring"), Pair("Primavera", "spring"),
@ -576,7 +571,7 @@ object AniPlayFilters {
Pair("Autunno", "fall"), Pair("Autunno", "fall"),
) )
val ANNI = arrayOf(ALL) + (1984..2023).map { val YEAR = arrayOf(ALL) + (1984..2023).map {
Pair(it.toString(), it.toString()) Pair(it.toString(), it.toString())
}.reversed().toTypedArray() }.reversed().toTypedArray()
} }

View File

@ -19,7 +19,6 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -58,15 +57,14 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
} }
private fun getConnId() { private fun getConnId() {
val headers = Headers.headersOf( val headers = headers.newBuilder()
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", .add("Accept", "application/json, text/javascript, */*; q=0.01")
"Accept", "application/json, text/javascript, */*; q=0.01", .add("Accept-Language", "en-US,en;q=0.5")
"Accept-Language", "en-US,en;q=0.5", .add("Content-Type", "application/json")
"Content-Type", "application/json", .add("Origin", baseUrl)
"Origin", "https://www.vvvvid.it", .add("Referer", "$baseUrl/channel/0/you")
"Referer", "https://www.vvvvid.it/channel/0/you", .add("X-Requested-With", "XMLHttpRequest")
"X-Requested-With", "XMLHttpRequest", .build()
)
val body = """ val body = """
{ {
@ -92,7 +90,7 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
val response = client.newCall( val response = client.newCall(
POST("$baseUrl/user/login", body = body, headers = headers), POST("$baseUrl/user/login", body = body, headers = headers),
).execute() ).execute()
if (response.code != 200) throw Exception("Failed to log in") if (response.code != 200) error("Failed to log in")
val parsed = json.decodeFromString<LoginResponse>(response.body.string()) val parsed = json.decodeFromString<LoginResponse>(response.body.string())
connId = parsed.data.conn_id connId = parsed.data.conn_id
@ -106,14 +104,13 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
getConnId() getConnId()
} }
val headers = Headers.headersOf( val headers = headers.newBuilder()
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", .add("Accept", "application/json, text/javascript, */*; q=0.01")
"Accept", "application/json, text/javascript, */*; q=0.01", .add("Accept-Language", "en-US,en;q=0.5")
"Accept-Language", "en-US,en;q=0.5", .add("Cookie", "JSESSIONID=$sessionId")
"Cookie", "JSESSIONID=$sessionId", .add("Referer", "$baseUrl/")
"Referer", "https://www.vvvvid.it/", .add("X-Requested-With", "XMLHttpRequest")
"X-Requested-With", "XMLHttpRequest", .build()
)
if (page == 1) { if (page == 1) {
updateFilters("anime", "Popolari") updateFilters("anime", "Popolari")
@ -143,14 +140,13 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
getConnId() getConnId()
} }
val headers = Headers.headersOf( val headers = headers.newBuilder()
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", .add("Accept", "application/json, text/javascript, */*; q=0.01")
"Accept", "application/json, text/javascript, */*; q=0.01", .add("Accept-Language", "en-US,en;q=0.5")
"Accept-Language", "en-US,en;q=0.5", .add("Cookie", "JSESSIONID=$sessionId")
"Cookie", "JSESSIONID=$sessionId", .add("Referer", "$baseUrl/")
"Referer", "https://www.vvvvid.it/", .add("X-Requested-With", "XMLHttpRequest")
"X-Requested-With", "XMLHttpRequest", .build()
)
if (page == 1) { if (page == 1) {
updateFilters("anime", "Nuove") updateFilters("anime", "Nuove")
@ -169,17 +165,16 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
} }
if (query.isNotEmpty()) { if (query.isNotEmpty()) {
throw Exception("Ricerca non disponibile") error("Ricerca non disponibile")
} }
val headers = Headers.headersOf( val headers = headers.newBuilder()
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", .add("Accept", "application/json, text/javascript, */*; q=0.01")
"Accept", "application/json, text/javascript, */*; q=0.01", .add("Accept-Language", "en-US,en;q=0.5")
"Accept-Language", "en-US,en;q=0.5", .add("Cookie", "JSESSIONID=$sessionId")
"Cookie", "JSESSIONID=$sessionId", .add("Referer", "$baseUrl/")
"Referer", "https://www.vvvvid.it/", .add("X-Requested-With", "XMLHttpRequest")
"X-Requested-With", "XMLHttpRequest", .build()
)
var filterStringFinal = "" var filterStringFinal = ""
var filterCounter = 0 var filterCounter = 0
@ -329,33 +324,30 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
getConnId() getConnId()
} }
val headers = Headers.headersOf( val headers = headers.newBuilder()
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", .add("Accept", "application/json, text/javascript, */*; q=0.01")
"Accept", "application/json, text/javascript, */*; q=0.01", .add("Accept-Language", "en-US,en;q=0.5")
"Accept-Language", "en-US,en;q=0.5", .add("Cookie", "JSESSIONID=$sessionId")
"Cookie", "JSESSIONID=$sessionId", .add("Referer", "$baseUrl/")
"Referer", "https://www.vvvvid.it/", .add("X-Requested-With", "XMLHttpRequest")
"X-Requested-With", "XMLHttpRequest", .build()
)
return GET("$baseUrl/vvvvid/ondemand/${anime.url}/info/?conn_id=$connId", headers = headers) return GET("$baseUrl/vvvvid/ondemand/${anime.url}/info/?conn_id=$connId", headers = headers)
} }
override fun animeDetailsParse(response: Response): SAnime { override fun animeDetailsParse(response: Response): SAnime {
val detailsJson = json.decodeFromString<InfoResponse>(response.body.string()).data val detailsJson = json.decodeFromString<InfoResponse>(response.body.string()).data
val anime = SAnime.create()
anime.title = detailsJson.title return SAnime.create().apply {
anime.status = SAnime.UNKNOWN title = detailsJson.title
status = SAnime.UNKNOWN
var description = detailsJson.description + "\n" genre = detailsJson.show_genres?.joinToString(", ") ?: ""
description += "\nAnno pubblicato: ${detailsJson.date_published}" description = buildString {
description += "\n${detailsJson.additional_info.split(" | ").joinToString("\n")}" append(detailsJson.description)
anime.description = description append("\n\nAnno pubblicato: ${detailsJson.date_published}")
append("\n${detailsJson.additional_info.split(" | ").joinToString("\n")}")
anime.genre = detailsJson.show_genres?.let { it.joinToString(", ") } ?: "" }
}
return anime
} }
// ============================== Episodes ============================== // ============================== Episodes ==============================
@ -365,14 +357,13 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
getConnId() getConnId()
} }
val headers = Headers.headersOf( val headers = headers.newBuilder()
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", .add("Accept", "application/json, text/javascript, */*; q=0.01")
"Accept", "application/json, text/javascript, */*; q=0.01", .add("Accept-Language", "en-US,en;q=0.5")
"Accept-Language", "en-US,en;q=0.5", .add("Cookie", "JSESSIONID=$sessionId")
"Cookie", "JSESSIONID=$sessionId", .add("Referer", "$baseUrl/")
"Referer", "https://www.vvvvid.it/", .add("X-Requested-With", "XMLHttpRequest")
"X-Requested-With", "XMLHttpRequest", .build()
)
return GET("$baseUrl/vvvvid/ondemand/${anime.url}/seasons/?conn_id=$connId", headers = headers) return GET("$baseUrl/vvvvid/ondemand/${anime.url}/seasons/?conn_id=$connId", headers = headers)
} }
@ -380,7 +371,7 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> {
val animeJson = json.decodeFromString<SeasonsResponse>(response.body.string()) val animeJson = json.decodeFromString<SeasonsResponse>(response.body.string())
val episodeList = mutableListOf<SEpisode>() val episodeList = mutableListOf<SEpisode>()
val subDub = preferences.getString("preferred_sub", "none")!! val subDub = preferences.getString(PREF_SUB_KEY, PREF_SUB_DEFAULT)!!
var counter = 1 var counter = 1
animeJson.data.forEach { animeJson.data.forEach {
@ -395,11 +386,13 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
} }
it.episodes.forEach { ep -> it.episodes.forEach { ep ->
val episode = SEpisode.create() episodeList.add(
episode.name = "$prefix${ep.number} ${ep.title}" SEpisode.create().apply {
episode.episode_number = counter.toFloat() name = "$prefix${ep.number} ${ep.title}"
episode.url = LinkData(it.show_id, ep.season_id, ep.video_id).toJsonString() episode_number = counter.toFloat()
episodeList.add(episode) url = LinkData(it.show_id, ep.season_id, ep.video_id).toJsonString()
},
)
counter++ counter++
} }
} }
@ -429,14 +422,13 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
val mediaId = json.decodeFromString<LinkData>(episode.url) val mediaId = json.decodeFromString<LinkData>(episode.url)
val headers = Headers.headersOf( val headers = headers.newBuilder()
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", .add("Accept", "application/json, text/javascript, */*; q=0.01")
"Accept", "application/json, text/javascript, */*; q=0.01", .add("Accept-Language", "en-US,en;q=0.5")
"Accept-Language", "en-US,en;q=0.5", .add("Cookie", "JSESSIONID=$sessionId")
"Cookie", "JSESSIONID=$sessionId", .add("Referer", "$baseUrl/")
"Referer", "https://www.vvvvid.it/", .add("X-Requested-With", "XMLHttpRequest")
"X-Requested-With", "XMLHttpRequest", .build()
)
return Pair( return Pair(
GET( GET(
@ -528,13 +520,12 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
} }
private fun videoFromDash(url: String, name: String): Video { private fun videoFromDash(url: String, name: String): Video {
val dashHeaders = Headers.headersOf( val dashHeaders = headers.newBuilder()
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", .add("Accept", "*/*")
"Accept", "*/*", .add("Accept-Language", "en-US,en;q=0.5")
"Accept-Language", "en-US,en;q=0.5", .add("Origin", baseUrl)
"Origin", "https://www.vvvvid.it", .add("Referer", "$baseUrl/")
"Referer", "https://www.vvvvid.it/", .build()
)
val dashContents = client.newCall( val dashContents = client.newCall(
GET(url, headers = dashHeaders), GET(url, headers = dashHeaders),
@ -545,24 +536,14 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
val audioUrl = dashContents.substringAfter("mimeType=\"audio").substringBefore("</BaseURL>").substringAfter("<BaseURL>") val audioUrl = dashContents.substringAfter("mimeType=\"audio").substringBefore("</BaseURL>").substringAfter("<BaseURL>")
val audioTracks = mutableListOf<Track>() val audioTracks = mutableListOf<Track>()
try {
audioTracks.add(Track("$baseVideoUrl/$audioUrl", "Audio")) audioTracks.add(Track("$baseVideoUrl/$audioUrl", "Audio"))
} catch (_: Error) { }
return try { return Video(
Video(
baseVideoUrl, baseVideoUrl,
name, name,
"$baseVideoUrl/$videoUrl", "$baseVideoUrl/$videoUrl",
audioTracks = audioTracks, audioTracks = audioTracks,
) )
} catch (_: Error) {
Video(
baseVideoUrl,
name,
"$baseVideoUrl/$videoUrl",
)
}
} }
private fun getRandomIntString(): String { private fun getRandomIntString(): String {
@ -577,7 +558,7 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
} }
override fun List<Video>.sort(): List<Video> { override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "HD")!! val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
return this.sortedWith( return this.sortedWith(
compareBy { it.quality.contains(quality) }, compareBy { it.quality.contains(quality) },
@ -656,13 +637,23 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
return d return d
} }
companion object {
private const val PREF_SUB_KEY = "preferred_sub"
private const val PREF_SUB_DEFAULT = "none"
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "HD"
}
// ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val subDubPref = ListPreference(screen.context).apply { ListPreference(screen.context).apply {
key = "preferred_sub" key = PREF_SUB_KEY
title = "Preferenza sub/dub" title = "Preferenza sub/dub"
entries = arrayOf("Nessuno", "Sub", "Dub") entries = arrayOf("Nessuno", "Sub", "Dub")
entryValues = arrayOf("none", "sub", "dub") entryValues = arrayOf("none", "sub", "dub")
setDefaultValue("none") setDefaultValue(PREF_SUB_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -671,14 +662,14 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
val entry = entryValues[index] as String val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit() preferences.edit().putString(key, entry).commit()
} }
} }.also(screen::addPreference)
val videoQualityPref = ListPreference(screen.context).apply { ListPreference(screen.context).apply {
key = "preferred_quality" key = PREF_QUALITY_KEY
title = "Qualità preferita" title = "Qualità preferita"
entries = arrayOf("HD", "SD") entries = arrayOf("HD", "SD")
entryValues = arrayOf("HD", "SD") entryValues = arrayOf("HD", "SD")
setDefaultValue("HD") setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s" summary = "%s"
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
@ -687,9 +678,6 @@ class VVVVID : ConfigurableAnimeSource, AnimeHttpSource() {
val entry = entryValues[index] as String val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit() preferences.edit().putString(key, entry).commit()
} }
} }.also(screen::addPreference)
screen.addPreference(subDubPref)
screen.addPreference(videoQualityPref)
} }
} }