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.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -46,15 +45,13 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
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 {
return SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href").toHttpUrl().encodedPath)
override fun popularAnimeFromElement(element: Element): SAnime = SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
thumbnail_url = element.selectFirst("img")?.attr("src")
title = element.selectFirst("div.default-text")!!.text()
}
}
override fun popularAnimeNextPageSelector(): String = "div > ul.page-nav > li:last-child:not(:has(a.disabled))"
// =============================== Latest ===============================
@ -62,10 +59,10 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun latestUpdatesSelector(): String = popularAnimeSelector()
override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector()
override fun latestUpdatesFromElement(element: Element): SAnime = popularAnimeFromElement(element)
override fun latestUpdatesNextPageSelector(): String = popularAnimeNextPageSelector()
// =============================== Search ===============================
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
@ -89,30 +86,26 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
return super.searchAnimeParse(response)
}
val document = response.asJsoup()
val animeList = response.asJsoup()
.select(searchAnimeSelectorSearch())
.map(::searchAnimeFromElementSearch)
val animes = document.select(searchAnimeSelectorSearch()).map { element ->
searchAnimeFromElementSearch(element)
}
return AnimesPage(animes, false)
return AnimesPage(animeList, false)
}
override fun searchAnimeSelector(): String = popularAnimeSelector()
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 {
return SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href").toHttpUrl().encodedPath)
private fun searchAnimeFromElementSearch(element: Element): SAnime = SAnime.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
thumbnail_url = element.selectFirst("img")?.attr("src")
title = element.selectFirst("p.card-text")!!.text()
}
}
override fun searchAnimeFromElement(element: Element): SAnime = popularAnimeFromElement(element)
override fun searchAnimeNextPageSelector(): String = popularAnimeNextPageSelector()
// ============================== Filters ===============================
@ -238,37 +231,33 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// =========================== Anime Details ============================
override fun animeDetailsParse(document: Document): SAnime {
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 {
override fun animeDetailsParse(document: Document): SAnime = SAnime.create().apply {
title = document.selectFirst("div.card-body > p:contains(TITOLO:)")?.ownText() ?: ""
thumbnail_url = document.selectFirst("div.card-body > div > img")?.attr("src") ?: ""
author = document.selectFirst("div.card-body > p:contains(STUDIO:)")?.ownText() ?: ""
status = document.selectFirst("div.card-body > p:contains(STATO:)")?.let {
parseStatus(it.ownText())
} ?: 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() ?: ""
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 ==============================
override fun episodeListParse(response: Response): List<SEpisode> {
return super.episodeListParse(response).reversed()
}
override fun episodeListParse(response: Response): List<SEpisode> = super.episodeListParse(response).reversed()
override fun episodeListSelector(): String = "div.card ul.page-nav-list-episodi > li"
override fun episodeFromElement(element: Element): SEpisode {
return SEpisode.create().apply {
override fun episodeFromElement(element: Element): SEpisode = SEpisode.create().apply {
name = "Episodi ${element.text()}"
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 =============================
@ -301,13 +290,15 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}
require(videoList.isNotEmpty()) { "Failed to fetch videos" }
return videoList
}
override fun videoFromElement(element: Element): Video = 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")
// ============================= Utilities ==============================
@ -328,7 +319,7 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
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(
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) {
val videoServerPref = ListPreference(screen.context).apply {
key = "preferred_server"
ListPreference(screen.context).apply {
key = PREF_SERVER_KEY
title = "Preferred server"
entries = arrayOf("AnimeLove", "StreamTape")
entryValues = arrayOf("AnimeLove", "StreamTape")
setDefaultValue("AnimeLove")
setDefaultValue(PREF_SERVER_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
@ -358,7 +356,6 @@ class AnimeLove : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
screen.addPreference(videoServerPref)
}.also(screen::addPreference)
}
}

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.AnimeFilterList
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
object AnimeUnityFilters {
@ -28,7 +30,7 @@ object AnimeUnityFilters {
class GenreFilter : CheckBoxFilterList(
"Genere",
AnimeUnityFiltersData.GENERE.map { CheckBoxVal(it.first, false) },
AnimeUnityFiltersData.GENRE.map { CheckBoxVal(it.first, false) },
)
class YearFilter : QueryPartFilter("Anno", AnimeUnityFiltersData.YEAR)
@ -73,15 +75,12 @@ object AnimeUnityFilters {
val genre: String = filters.filterIsInstance<GenreFilter>()
.first()
.state.mapNotNull { format ->
if (format.state) {
"{\"id\":" +
AnimeUnityFiltersData.GENERE.find { it.first == format.name }!!.second +
",\"name\":\"" +
AnimeUnityFiltersData.GENERE.find { it.first == format.name }!!.first +
"\"}"
} else { null }
}.joinToString(",")
.state.filter { it.state }.joinToString(",") { format ->
buildJsonObject {
put("id", AnimeUnityFiltersData.GENRE.find { it.first == format.name }!!.second.toInt())
put("name", AnimeUnityFiltersData.GENRE.find { it.first == format.name }!!.first)
}.toString()
}
return FilterSearchParams(
filters.asQueryPart<TopFilter>(),
@ -113,7 +112,7 @@ object AnimeUnityFilters {
Pair("Più visti", "top-anime?order=most_viewed"),
)
val GENERE = arrayOf(
val GENRE = arrayOf(
Pair("Action", "51"),
Pair("Adventure", "21"),
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")
private fun searchAnimeRequest(page: Int, query: String, filters: AniPlayFilters.FilterSearchParams): Request {
if ((filters.anni.isNotEmpty() && filters.stagione.isEmpty()) ||
(filters.anni.isEmpty() && filters.stagione.isNotEmpty())
if ((filters.year.isNotEmpty() && filters.season.isEmpty()) ||
(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()
.addPathSegment("${filters.stagione}-${filters.anni}")
.addPathSegment("${filters.season}-${filters.year}")
.addQueryParameter("page", (page - 1).toString())
.addQueryParameter("size", "36")
.addQueryParameter("sort", filters.ordina)
.addQueryParameter("sort", filters.order)
.addQueryParameter("sort", "id")
} else {
"$baseUrl/api/anime/advanced-similar-search".toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("page", (page - 1).toString())
.addQueryParameter("size", "36")
.addQueryParameter("sort", filters.ordina)
.addQueryParameter("sort", filters.order)
.addQueryParameter("sort", "id")
.addIfNotBlank("query", query)
.addIfNotBlank("genreIds", filters.genere)
.addIfNotBlank("typeIds", filters.tipologia)
.addIfNotBlank("statusIds", filters.stato)
.addIfNotBlank("originIds", filters.origine)
.addIfNotBlank("genreIds", filters.genre)
.addIfNotBlank("typeIds", filters.type)
.addIfNotBlank("statusIds", filters.status)
.addIfNotBlank("originIds", filters.origin)
.addIfNotBlank("studioIds", filters.studio)
.addIfNotBlank("startYear", filters.inizio)
.addIfNotBlank("endYear", filters.fine)
.addIfNotBlank("startYear", filters.start)
.addIfNotBlank("endYear", filters.end)
}
return GET(url.build().toString())

View File

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

View File

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