fix(it/aniplay): Rewrite extension for the new structure (#2871)

This commit is contained in:
Claudemirovsky
2024-02-05 19:24:03 -03:00
committed by GitHub
parent 8ffc21113b
commit 725454b8d5
8 changed files with 515 additions and 800 deletions

View File

@ -1,2 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest /> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".it.aniplay.AniPlayUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="aniplay.co"
android:pathPattern="/series/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,7 +1,11 @@
ext { ext {
extName = 'AniPlay' extName = 'AniPlay'
extClass = '.AniPlay' extClass = '.AniPlay'
extVersionCode = 6 extVersionCode = 7
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:playlist-utils"))
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,12 +1,14 @@
package eu.kanade.tachiyomi.animeextension.it.aniplay package eu.kanade.tachiyomi.animeextension.it.aniplay
import android.app.Application import android.app.Application
import android.content.SharedPreferences
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.BuildConfig import eu.kanade.tachiyomi.animeextension.it.aniplay.dto.AnimeInfoDto
import eu.kanade.tachiyomi.animeextension.it.aniplay.dto.EpisodeDto
import eu.kanade.tachiyomi.animeextension.it.aniplay.dto.LatestItemDto
import eu.kanade.tachiyomi.animeextension.it.aniplay.dto.PopularAnimeDto
import eu.kanade.tachiyomi.animeextension.it.aniplay.dto.PopularResponseDto
import eu.kanade.tachiyomi.animeextension.it.aniplay.dto.VideoDto
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage import eu.kanade.tachiyomi.animesource.model.AnimesPage
@ -14,17 +16,17 @@ import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.awaitSuccess
import kotlinx.serialization.json.Json import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers import eu.kanade.tachiyomi.util.parseAs
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -32,264 +34,158 @@ class AniPlay : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "AniPlay" override val name = "AniPlay"
override val baseUrl by lazy { preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! } override val baseUrl = "https://aniplay.co"
override val lang = "it" override val lang = "it"
override val supportsLatest = false override val supportsLatest = true
private val json: Json by injectLazy() override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
.add("Origin", baseUrl)
private val preferences: SharedPreferences by lazy { override val versionId = 2 // Source was rewritten in Svelte
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
// ============================== Popular =============================== // ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) =
GET("$API_URL/advancedSearch?sort=7&page=$page&origin=,,,,,,", headers)
override fun popularAnimeParse(response: Response): AnimesPage = searchAnimeParse(response) override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = response.parseAs<PopularResponseDto>()
override fun popularAnimeRequest(page: Int): Request = val animes = parsed.data.map(PopularAnimeDto::toSAnime)
GET("$baseUrl/api/anime/advanced-similar-search?page=${page - 1}&size=36&sort=views,desc&sort=id") return AnimesPage(animes, parsed.pagination.hasNextPage)
}
// =============================== Latest =============================== // =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) = GET("$API_URL/latest-episodes?page=$page&type=All")
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException() override fun latestUpdatesParse(response: Response): AnimesPage {
val items = response.parseAs<List<LatestItemDto>>()
override fun latestUpdatesParse(response: Response): AnimesPage = throw UnsupportedOperationException() val animes = items.mapNotNull { it.serie.firstOrNull()?.toSAnime() }
return AnimesPage(animes, items.size == 20)
}
// =============================== Search =============================== // =============================== Search ===============================
override fun getFilterList() = AniPlayFilters.FILTER_LIST
override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage { override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
val params = AniPlayFilters.getSearchParameters(filters) return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
return client.newCall(searchAnimeRequest(page, query, params)) val id = query.removePrefix(PREFIX_SEARCH)
.awaitSuccess() client.newCall(GET("$baseUrl/series/$id"))
.use(::searchAnimeParse) .awaitSuccess()
} .use(::searchAnimeByIdParse)
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw UnsupportedOperationException()
private fun searchAnimeRequest(page: Int, query: String, filters: AniPlayFilters.FilterSearchParams): Request {
if ((filters.year.isNotEmpty() && filters.season.isEmpty()) ||
(filters.year.isEmpty() && filters.season.isNotEmpty())
) {
error("Per gli anime stagionali, seleziona sia l'anno che la stagione")
}
val url = if (filters.year.isNotEmpty()) {
"$baseUrl/api/seasonal-view".toHttpUrlOrNull()!!.newBuilder()
.addPathSegment("${filters.season}-${filters.year}")
.addQueryParameter("page", (page - 1).toString())
.addQueryParameter("size", "36")
.addQueryParameter("sort", filters.order)
.addQueryParameter("sort", "id")
} else { } else {
"$baseUrl/api/anime/advanced-similar-search".toHttpUrlOrNull()!!.newBuilder() super.getSearchAnime(page, query, filters)
.addQueryParameter("page", (page - 1).toString())
.addQueryParameter("size", "36")
.addQueryParameter("sort", filters.order)
.addQueryParameter("sort", "id")
.addIfNotBlank("query", query)
.addIfNotBlank("genreIds", filters.genre)
.addIfNotBlank("typeIds", filters.type)
.addIfNotBlank("statusIds", filters.status)
.addIfNotBlank("originIds", filters.origin)
.addIfNotBlank("studioIds", filters.studio)
.addIfNotBlank("startYear", filters.start)
.addIfNotBlank("endYear", filters.end)
} }
return GET(url.build().toString())
} }
override fun searchAnimeParse(response: Response): AnimesPage { private fun searchAnimeByIdParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<List<SearchResult>>(response.body.string()) val details = animeDetailsParse(response)
.apply { setUrlWithoutDomain(response.request.url.toString()) }
val animeList = parsed.map { ani -> return AnimesPage(listOf(details), false)
SAnime.create().apply {
title = ani.title
if (ani.verticalImages.isNotEmpty()) {
thumbnail_url = ani.verticalImages.first().imageFull
}
url = ani.id.toString()
description = ani.storyline
}
}
return AnimesPage(animeList, animeList.size == 36)
} }
// ============================== Filters =============================== override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
val params = AniPlayFilters.getSearchParameters(filters)
val url = "$API_URL/advancedSearch".toHttpUrl().newBuilder()
.addQueryParameter("page", page.toString())
.addQueryParameter("origin", ",,,,,,")
.addQueryParameter("sort", params.order)
.addIfNotBlank("_q", query)
.addIfNotBlank("genres", params.genres)
.addIfNotBlank("country", params.countries)
.addIfNotBlank("types", params.types)
.addIfNotBlank("studios", params.studios)
.addIfNotBlank("status", params.status)
.addIfNotBlank("subbed", params.languages)
.build()
override fun getFilterList(): AnimeFilterList = AniPlayFilters.FILTER_LIST return GET(url, headers)
}
override fun searchAnimeParse(response: Response) = popularAnimeParse(response)
// =========================== Anime Details ============================ // =========================== Anime Details ============================
override fun getAnimeUrl(anime: SAnime) = "$baseUrl/anime/${anime.url}" override fun animeDetailsParse(response: Response) = SAnime.create().apply {
val script = response.getPageScript()
val jsonString = script.substringAfter("{serie:").substringBefore(",tags") + "}"
val parsed = jsonString.fixJsonString().parseAs<AnimeInfoDto>()
override fun animeDetailsRequest(anime: SAnime) = GET("$baseUrl/api/anime/${anime.url}") title = parsed.title
genre = parsed.genres.joinToString { it.name }
artist = parsed.studios.joinToString { it.name }
thumbnail_url = parsed.thumbnailUrl
status = when (parsed.status) {
"Completato" -> SAnime.COMPLETED
"In corso" -> SAnime.ONGOING
"Sospeso" -> SAnime.ON_HIATUS
else -> SAnime.UNKNOWN
}
override fun animeDetailsParse(response: Response): SAnime { description = buildString {
val detailsJson = json.decodeFromString<AnimeResult>(response.body.string()) parsed.description?.also {
append(it, "\n\n")
}
return SAnime.create().apply { listOf(
title = detailsJson.title "Titolo Alternativo" to parsed.alternative,
author = detailsJson.studio "Origine" to parsed.origin,
status = parseStatus(detailsJson.status) "Giorno di lancio" to parsed.release_day,
description = buildString { ).forEach { (title, value) ->
append(detailsJson.storyline) if (value != null) append(title, ": ", value, "\n")
append("\n\nTipologia: ${detailsJson.type}")
append("\nOrigine: ${detailsJson.origin}")
if (detailsJson.startDate != null) append("\nData di inizio: ${detailsJson.startDate}")
append("\nStato: ${detailsJson.status}")
} }
} }
} }
// ============================== Episodes ============================== // ============================== Episodes ==============================
override fun episodeListRequest(anime: SAnime): Request = GET("$baseUrl/api/anime/${anime.url}")
override fun episodeListParse(response: Response): List<SEpisode> { override fun episodeListParse(response: Response): List<SEpisode> {
val animeJson = json.decodeFromString<AnimeResult>(response.body.string()) val script = response.getPageScript()
val episodeList = mutableListOf<SEpisode>() val jsonString = script.substringAfter(",episodes:").substringBefore("]},") + "]"
val parsed = jsonString.fixJsonString().parseAs<List<EpisodeDto>>()
if (animeJson.seasons.isNotEmpty()) { return parsed.map {
for (season in animeJson.seasons) { SEpisode.create().apply {
val episodesResponse = client.newCall( episode_number = it.number?.toFloatOrNull() ?: 1F
GET("$baseUrl/api/anime/${animeJson.id}/season/${season.id}"), url = "/watch/${it.id}"
).execute() name = it.title ?: "Episodio ${it.number}"
val episodesJson = json.decodeFromString<List<AnimeResult.Episode>>(episodesResponse.body.string()) date_upload = it.release_date.toDate()
episodeList.addAll(
episodesJson.map { ep ->
SEpisode.create().apply {
name = "Episode ${ep.episodeNumber.toIntOrNull() ?: (ep.episodeNumber.toFloatOrNull() ?: 1)} ${ep.title ?: ""}"
episode_number = ep.episodeNumber.toFloatOrNull() ?: 0F
if (ep.airingDate != null) date_upload = SimpleDateFormat("yyyy-MM-dd", Locale.ITALY).parse(ep.airingDate)!!.time
url = ep.id.toString()
}
},
)
} }
} else if (animeJson.episodes.isNotEmpty()) { }.reversed()
episodeList.addAll(
animeJson.episodes.map { ep ->
SEpisode.create().apply {
name = "Episode ${ep.episodeNumber.toIntOrNull() ?: (ep.episodeNumber.toFloatOrNull() ?: 1)} ${ep.title ?: ""}"
episode_number = ep.episodeNumber.toFloatOrNull() ?: 0F
if (ep.airingDate != null) date_upload = SimpleDateFormat("yyyy-MM-dd", Locale.ITALY).parse(ep.airingDate)!!.time
url = ep.id.toString()
}
},
)
}
return episodeList.sortedBy { it.episode_number }.reversed()
} }
// ============================ Video Links ============================= // ============================ Video Links =============================
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
override fun videoListRequest(episode: SEpisode): Request = GET("$baseUrl/api/episode/${episode.url}")
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val videoList = mutableListOf<Video>() val script = response.getPageScript()
val videoJson = json.decodeFromString<VideoResult>(response.body.string()) val jsonString = script.substringAfter("{episode:").substringBefore(",views") + "}"
val videoUrl = videoJson.videoUrl val videoUrl = jsonString.fixJsonString().parseAs<VideoDto>().videoLink
if (videoUrl.contains(".mp4")) { return when {
videoList.add( videoUrl.contains(".m3u8") -> playlistUtils.extractFromHls(videoUrl)
Video( else -> listOf(Video(videoUrl, "Default", videoUrl, headers = headers))
videoUrl,
"Best (mp4)",
videoUrl,
headers = Headers.headersOf(
"Referer",
"$baseUrl/play/${videoJson.id}",
),
),
)
} else if (videoUrl.contains(".m3u8")) {
val masterPlaylist = client.newCall(
GET(videoUrl, headers = Headers.headersOf("Referer", "$baseUrl/play/${videoJson.id}")),
).execute().body.string()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:")
.forEach {
val quality = it.substringAfter("RESOLUTION=").substringAfter("x").substringBefore(",") + "p"
var newUrl = it.substringAfter("\n").substringBefore("\n")
if (!newUrl.startsWith("http")) {
newUrl = videoUrl.substringBeforeLast("/") + "/" + newUrl
}
videoList.add(Video(newUrl, quality, newUrl, headers = Headers.headersOf("Referer", "$baseUrl/")))
}
} }
require(videoList.isNotEmpty()) { "Failed to fetch videos" }
return videoList.sort()
}
// ============================= Utilities ==============================
private fun parseStatus(statusString: String): Int = when (statusString) {
"In corso" -> SAnime.ONGOING
"Completato" -> SAnime.COMPLETED
"Sospeso" -> SAnime.ON_HIATUS
else -> SAnime.UNKNOWN
}
private fun HttpUrl.Builder.addIfNotBlank(query: String, value: String): HttpUrl.Builder {
if (value.isNotBlank()) {
addQueryParameter(query, value)
}
return this
} }
override fun List<Video>.sort(): List<Video> { override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!! val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
return this.sortedWith( return sortedWith(
compareBy( compareBy { it.quality.contains(quality) },
{ it.quality.contains(quality) },
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
),
).reversed() ).reversed()
} }
companion object {
private val PREF_DOMAIN_KEY = "preferred_domain_name_v${BuildConfig.VERSION_CODE}"
private const val PREF_DOMAIN_TITLE = "Override BaseUrl"
private const val PREF_DOMAIN_DEFAULT = "https://aniplay.co"
private const val PREF_DOMAIN_SUMMARY = "For temporary uses. Updating the extension will erase this setting."
private const val PREF_QUALITY_KEY = "preferred_quality"
private const val PREF_QUALITY_DEFAULT = "1080"
}
// ============================== Settings ============================== // ============================== Settings ==============================
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
EditTextPreference(screen.context).apply {
key = PREF_DOMAIN_KEY
title = PREF_DOMAIN_TITLE
summary = PREF_DOMAIN_SUMMARY
dialogTitle = PREF_DOMAIN_TITLE
dialogMessage = "Default: $PREF_DOMAIN_DEFAULT"
setDefaultValue(PREF_DOMAIN_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
val newValueString = newValue as String
Toast.makeText(screen.context, "Restart Aniyomi to apply new setting.", Toast.LENGTH_LONG).show()
preferences.edit().putString(key, newValueString.trim()).commit()
}
}.also(screen::addPreference)
ListPreference(screen.context).apply { ListPreference(screen.context).apply {
key = PREF_QUALITY_KEY key = PREF_QUALITY_KEY
title = "Preferred quality" title = PREF_QUALITY_TITLE
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "80p") entries = PREF_QUALITY_ENTRIES
entryValues = arrayOf("1080", "720", "480", "360", "240", "80") entryValues = PREF_QUALITY_ENTRIES
setDefaultValue(PREF_QUALITY_DEFAULT) setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s" summary = "%s"
@ -301,4 +197,42 @@ class AniPlay : ConfigurableAnimeSource, AnimeHttpSource() {
} }
}.also(screen::addPreference) }.also(screen::addPreference)
} }
// ============================= Utilities ==============================
private fun HttpUrl.Builder.addIfNotBlank(query: String, value: String) = apply {
if (value.isNotBlank()) {
addQueryParameter(query, value)
}
}
// {key:"value"} -> {"key":"value"}
private fun String.fixJsonString() = replace(WRONG_KEY_REGEX) {
"\"${it.groupValues[1]}\":${it.groupValues[2]}"
}
private fun Response.getPageScript() =
asJsoup().selectFirst("script:containsData(const data = )")!!.data()
private fun String?.toDate(): Long {
if (this == null) return 0L
return runCatching { DATE_FORMATTER.parse(trim())?.time }
.getOrNull() ?: 0L
}
companion object {
const val PREFIX_SEARCH = "id:"
private const val API_URL = "https://api.aniplay.co/api/series"
private val WRONG_KEY_REGEX by lazy { Regex("([a-zA-Z_]+):\\s?([\"|0-9|f|t|n|\\[|\\{])") }
private val DATE_FORMATTER by lazy {
SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
}
private const val PREF_QUALITY_KEY = "pref_quality_key"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "720p"
private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "540p", "480p", "360p", "244p", "144p")
}
} }

View File

@ -1,55 +0,0 @@
package eu.kanade.tachiyomi.animeextension.it.aniplay
import kotlinx.serialization.Serializable
@Serializable
data class VideoResult(
val id: Int,
val videoUrl: String,
)
@Serializable
data class SearchResult(
val id: Int,
val title: String,
val storyline: String,
val verticalImages: List<Image>,
) {
@Serializable
data class Image(
val imageFull: String,
)
}
@Serializable
data class AnimeResult(
val id: Int,
val title: String,
val startDate: String? = null,
val storyline: String,
val type: String,
val origin: String,
val status: String,
val studio: String,
val verticalImages: List<Image>,
val seasons: List<Season>,
val episodes: List<Episode>,
) {
@Serializable
data class Season(
val id: Int,
)
@Serializable
data class Episode(
val id: Int,
val episodeNumber: String,
val title: String? = null,
val airingDate: String? = null,
)
@Serializable
data class Image(
val imageFull: String,
)
}

View File

@ -4,575 +4,278 @@ import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object AniPlayFilters { object AniPlayFilters {
open class SelectFilter(
open class QueryPartFilter(
displayName: String, displayName: String,
val vals: Array<Pair<String, String>>, val vals: Array<Pair<String, String>>,
) : AnimeFilter.Select<String>( ) : AnimeFilter.Select<String>(
displayName, displayName,
vals.map { it.first }.toTypedArray(), vals.map { it.first }.toTypedArray(),
) { ) {
fun toQueryPart() = vals[state].second val selected get() = vals[state].second
} }
open class CheckBoxFilterList(name: String, values: List<CheckBox>) : AnimeFilter.Group<AnimeFilter.CheckBox>(name, values) private inline fun <reified R> AnimeFilterList.getSelected(): String {
private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state) return (first { it is R } as SelectFilter).selected
private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
return this.filterIsInstance<R>().joinToString("") {
(it as QueryPartFilter).toQueryPart()
}
} }
class GenreFilter : CheckBoxFilterList( open class CheckBoxFilterList(name: String, val pairs: Array<Pair<String, String>>) :
"Genere", AnimeFilter.Group<AnimeFilter.CheckBox>(name, pairs.map { CheckBoxVal(it.first) })
AniPlayFiltersData.GENRE.map { CheckBoxVal(it.first, false) },
)
class TypeFilter : CheckBoxFilterList( private class CheckBoxVal(name: String) : AnimeFilter.CheckBox(name, false)
"Tipologia anime",
AniPlayFiltersData.TYPE.map { CheckBoxVal(it.first, false) },
)
class StatusFilter : CheckBoxFilterList( private inline fun <reified R> AnimeFilterList.parseCheckbox(
"Stato", options: Array<Pair<String, String>>,
AniPlayFiltersData.STATUS.map { CheckBoxVal(it.first, false) }, ): String {
) return (first { it is R } as CheckBoxFilterList).state
.asSequence()
.filter { it.state }
.map { checkbox -> options.find { it.first == checkbox.name }!!.second }
.joinToString(",")
}
class OriginFilter : CheckBoxFilterList( internal class OrderFilter : SelectFilter("Ordina per", ORDER_LIST)
"Origine",
AniPlayFiltersData.ORIGIN.map { CheckBoxVal(it.first, false) },
)
class StudioFilter : CheckBoxFilterList( internal class GenreFilter : CheckBoxFilterList("Generi", GENRE_LIST)
"Studio", internal class CountryFilter : CheckBoxFilterList("Paesi", COUNTRY_LIST)
AniPlayFiltersData.STUDIO.map { CheckBoxVal(it.first, false) }, internal class TypeFilter : CheckBoxFilterList("Tipi", TYPE_LIST)
) internal class StudioFilter : CheckBoxFilterList("Studio", STUDIO_LIST)
internal class StatusFilter : CheckBoxFilterList("Stato", STATUS_LIST)
internal class LanguageFilter : CheckBoxFilterList("Lingua", LANGUAGE_LIST)
class StartFilter : QueryPartFilter("Inizio", AniPlayFiltersData.YEAR) internal val FILTER_LIST get() = AnimeFilterList(
class EndFilter : QueryPartFilter("Fine", AniPlayFiltersData.YEAR)
class OrderFilter : QueryPartFilter("Ordina per", AniPlayFiltersData.ORDER)
class YearFilter : QueryPartFilter("Anni", AniPlayFiltersData.YEAR)
class SeasonFilter : QueryPartFilter("Stagione", AniPlayFiltersData.SEASON)
val FILTER_LIST get() = AnimeFilterList(
OrderFilter(), OrderFilter(),
AnimeFilter.Separator(),
GenreFilter(), GenreFilter(),
CountryFilter(),
TypeFilter(), TypeFilter(),
StatusFilter(),
OriginFilter(),
StudioFilter(), StudioFilter(),
StatusFilter(),
AnimeFilter.Separator(), LanguageFilter(),
AnimeFilter.Header("Anni"),
StartFilter(),
EndFilter(),
AnimeFilter.Separator(),
AnimeFilter.Header("Anime stagionali"),
AnimeFilter.Header("(ignora altri filtri tranne l'ordinamento per)"),
YearFilter(),
SeasonFilter(),
) )
data class FilterSearchParams( internal data class FilterSearchParams(
val order: String = "views,desc", val order: String = "1",
val genre: String = "", val genres: String = "",
val type: String = "", val countries: String = "",
val types: String = "",
val studios: String = "",
val status: String = "", val status: String = "",
val origin: String = "", val languages: String = "",
val studio: String = "",
val start: String = "",
val end: String = "",
val year: 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 genre: String = filters.filterIsInstance<GenreFilter>()
.first()
.state.filter { it.state }
.joinToString(",") { format ->
AniPlayFiltersData.GENRE.find { it.first == format.name }!!.second
}
val type: String = filters.filterIsInstance<TypeFilter>()
.first()
.state.filter { it.state }
.joinToString(",") { format ->
AniPlayFiltersData.TYPE.find { it.first == format.name }!!.second
}
val status: String = filters.filterIsInstance<StatusFilter>()
.first()
.state.filter { it.state }
.joinToString(",") { format ->
AniPlayFiltersData.STATUS.find { it.first == format.name }!!.second
}
val origin: String = filters.filterIsInstance<OriginFilter>()
.first()
.state.filter { it.state }
.joinToString(",") { format ->
AniPlayFiltersData.ORIGIN.find { it.first == format.name }!!.second
}
val studio: String = filters.filterIsInstance<StudioFilter>()
.first()
.state.filter { it.state }
.joinToString(",") { format ->
AniPlayFiltersData.STUDIO.find { it.first == format.name }!!.second
}
return FilterSearchParams( return FilterSearchParams(
filters.asQueryPart<OrderFilter>(), filters.getSelected<OrderFilter>(),
genre, filters.parseCheckbox<GenreFilter>(GENRE_LIST),
type, filters.parseCheckbox<CountryFilter>(COUNTRY_LIST),
status, filters.parseCheckbox<TypeFilter>(TYPE_LIST),
origin, filters.parseCheckbox<StudioFilter>(STUDIO_LIST),
studio, filters.parseCheckbox<StatusFilter>(STATUS_LIST),
filters.asQueryPart<StartFilter>(), filters.parseCheckbox<LanguageFilter>(LANGUAGE_LIST),
filters.asQueryPart<EndFilter>(),
filters.asQueryPart<YearFilter>(),
filters.asQueryPart<SeasonFilter>(),
) )
} }
private object AniPlayFiltersData { private val GENRE_LIST = arrayOf(
val ALL = Pair("All", "") Pair("Arti marziali", "35"),
Pair("Automobilismo", "49"),
Pair("Avventura", "3"),
Pair("Azione", "7"),
Pair("Boys Love", "52"),
Pair("Combattimento", "27"),
Pair("Commedia", "13"),
Pair("Cucina", "38"),
Pair("Demenziale", "32"),
Pair("Demoni", "26"),
Pair("Drammatico", "2"),
Pair("Ecchi", "21"),
Pair("Fantasy", "1"),
Pair("Giallo", "34"),
Pair("Gioco", "31"),
Pair("Guerra", "39"),
Pair("Harem", "30"),
Pair("Horror", "14"),
Pair("Isekai", "43"),
Pair("Josei", "47"),
Pair("Magia", "18"),
Pair("Mecha", "25"),
Pair("Militare", "23"),
Pair("Mistero", "5"),
Pair("Musica", "40"),
Pair("Parodia", "42"),
Pair("Politica", "24"),
Pair("Poliziesco", "29"),
Pair("Psicologico", "6"),
Pair("Reverse-harem", "45"),
Pair("Romantico", "8"),
Pair("Samurai", "36"),
Pair("Sci-Fi", "20"),
Pair("Scolastico", "10"),
Pair("Seinen", "28"),
Pair("Sentimentale", "12"),
Pair("Shoujo", "11"),
Pair("Shoujo Ai", "37"),
Pair("Shounen", "16"),
Pair("Shounen Ai", "51"),
Pair("Slice of Life", "19"),
Pair("Sovrannaturale", "22"),
Pair("Spaziale", "48"),
Pair("Splatter", "15"),
Pair("Sport", "41"),
Pair("Storico", "17"),
Pair("Superpoteri", "9"),
Pair("Thriller", "4"),
Pair("Vampiri", "33"),
Pair("Videogame", "44"),
Pair("Yaoi", "50"),
Pair("Yuri", "46"),
)
val ORDER = arrayOf( private val COUNTRY_LIST = arrayOf(
Pair("Popolarità decrescente", "views,desc"), Pair("Corea del Sud", "KR"),
Pair("Popolarità crescente", "views,asc"), Pair("Cina", "CN"),
Pair("Titolo decrescente", "title,desc"), Pair("Hong Kong", "HK"),
Pair("Titolo crescente", "title,asc"), Pair("Filippine", "PH"),
Pair("Numero episodi decrescente", "episodeNumber,desc"), Pair("Giappone", "JP"),
Pair("Numero episodi crescente", "episodeNumber,asc"), Pair("Taiwan", "TW"),
Pair("Data di inizio decrescente", "startDate,desc"), Pair("Thailandia", "TH"),
Pair("Data di inizio crescente", "startDate,asc"), )
Pair("Data di fine decrescente", "endDate,desc"),
Pair("Data di fine crescente", "endDate,asc"),
Pair("Data di fine aggiunta", "createdDate,desc"),
Pair("Data di fine aggiunta", "createdDate,asc"),
)
val GENRE = arrayOf( private val TYPE_LIST = arrayOf(
Pair("Arti marziali", "68"), Pair("Serie", "1"),
Pair("Automobilismo", "90"), Pair("Movie", "2"),
Pair("Avventura", "25"), Pair("OVA", "3"),
Pair("Azione", "38"), Pair("ONA", "4"),
Pair("Boys Love", "104"), Pair("Special", "5"),
Pair("Combattimento", "64"), )
Pair("Commedia", "37"),
Pair("Cucina", "103"),
Pair("Demenziale", "66"),
Pair("Demoni", "53"),
Pair("Drammatico", "31"),
Pair("Ecchi", "47"),
Pair("Fantasy", "26"),
Pair("Giallo", "102"),
Pair("Gioco", "55"),
Pair("Guerra", "63"),
Pair("Harem", "46"),
Pair("Horror", "29"),
Pair("Isekai", "76"),
Pair("Josei", "101"),
Pair("Magia", "41"),
Pair("Mecha", "45"),
Pair("Militare", "84"),
Pair("Mistero", "27"),
Pair("Musica", "94"),
Pair("Parodia", "75"),
Pair("Politica", "59"),
Pair("Poliziesco", "33"),
Pair("Psicologico", "28"),
Pair("Reverse-harem", "80"),
Pair("Romantico", "43"),
Pair("Samurai", "95"),
Pair("Sci-Fi", "69"),
Pair("Scolastico", "32"),
Pair("Seinen", "49"),
Pair("Sentimentale", "36"),
Pair("Shoujo", "60"),
Pair("Shoujo Ai", "70"),
Pair("Shounen", "48"),
Pair("Shounen Ai", "99"),
Pair("Slice of Life", "30"),
Pair("Sovrannaturale", "34"),
Pair("Spaziale", "87"),
Pair("Splatter", "56"),
Pair("Sport", "74"),
Pair("Storico", "52"),
Pair("Superpoteri", "96"),
Pair("Thriller", "35"),
Pair("Vampiri", "67"),
Pair("Videogame", "85"),
Pair("Yaoi", "51"),
Pair("Yuri", "50"),
)
val TYPE = arrayOf( private val STUDIO_LIST = arrayOf(
Pair("Movie", "2"), Pair("2:10 AM Animation", "190"),
Pair("ONA", "4"), Pair("5 Inc.", "309"),
Pair("OVA", "3"), Pair("8bit", "17"),
Pair("Serie", "1"), Pair("A-1 Picture", "11"),
Pair("Special", "5"), Pair("Acca Effe", "180"),
) Pair("A.C.G.T.", "77"),
Pair("Actas", "153"),
Pair("AIC ASTA", "150"),
Pair("AIC Build", "46"),
Pair("AIC Classic", "99"),
Pair("AIC Plus+", "26"),
Pair("AIC Spirits", "128"),
Pair("Ajia-Do", "39"),
Pair("Akatsuki", "289"),
Pair("Albacrow", "229"),
Pair("Anima&Co", "161"),
Pair("Animation Planet", "224"),
Pair("Animax", "103"),
Pair("Anpro", "178"),
Pair("APPP", "220"),
Pair("AQUA ARIS", "245"),
Pair("A-Real", "211"),
Pair("ARECT", "273"),
Pair("Arms", "33"),
Pair("Artland", "81"),
Pair("Arvo Animation", "239"),
Pair("Asahi Production", "160"),
Pair("Ashi Production", "307"),
Pair("ASK Animation Studio", "296"),
Pair("Asread", "76"),
Pair("Atelier Pontdarc", "298"),
Pair("AtelierPontdarc", "271"),
Pair("AXsiZ", "70"),
Pair("Bakken Record", "195"),
Pair("Bandai Namco Pictures", "108"),
Pair("Barnum Studio", "191"),
Pair("B.CMAY PICTURES", "135"),
Pair("Bee Media", "262"),
Pair("Bee Train", "98"),
Pair("Bibury Animation Studios", "139"),
Pair("Big FireBird Animation", "141"),
Pair("blade", "212"),
Pair("Bones", "22"),
Pair("Bouncy", "174"),
Pair("Brain's Base", "18"),
Pair("Bridge", "88"),
Pair("B&T", "193"),
Pair("Buemon", "236"),
Pair("BUG FILMS", "314"),
Pair("Bushiroad", "249"),
Pair("C2C", "126"),
Pair("Chaos Project", "247"),
Pair("Charaction", "250"),
Pair("Children's Playground Entertainment", "184"),
Pair("CLAP", "292"),
Pair("CloverWorks", "51"),
Pair("Colored Pencil Animation", "268"),
Pair("CoMix Wave Films", "83"),
Pair("Connect", "185"),
Pair("Craftar Studios", "146"),
Pair("Creators in Pack", "84"),
Pair("C-Station", "72"),
Pair("CyberConnect2", "217"),
Pair("CygamesPictures", "233"),
Pair("DandeLion Animation Studio", "116"),
Pair("Daume", "102"),
Pair("David Production", "73"),
Pair("De Mas & Partners", "207"),
Pair("Diomedea", "21"),
Pair("DLE", "155"),
Pair("DMM.futureworks", "241"),
Pair("DMM pictures", "248"),
Pair("Doga Kobo", "50"),
Pair("domerica", "302"),
Pair("Drive", "226"),
Pair("DR Movie", "113"),
Pair("drop", "130"),
Pair("Dynamo Pictures", "231"),
Pair("E&H Production", "333"),
Pair("EKACHI EPILKA", "151"),
Pair("Emon Animation Company", "149"),
Pair("Emon, Blade", "123"),
Pair("EMT²", "90"),
Pair("Encourage Films", "100"),
Pair("ENGI", "158"),
Pair("evg", "322"),
Pair("EXNOA", "274"),
Pair("Ezo'la", "35"),
Pair("Fanworks", "121"),
Pair("feel.", "37"),
Pair("Felix Film", "163"),
Pair("Frederator Studios", "147"),
Pair("Fugaku", "326"),
Pair("Funimation", "106"),
Pair("Gainax", "43"),
Pair("Gainax Kyoto", "225"),
Pair("Gallop", "109"),
Pair("Gambit", "272"),
Pair("G-angle", "222"),
Pair("Garden Culture", "324"),
)
val STATUS = arrayOf( private val STATUS_LIST = arrayOf(
Pair("Annunciato", "4"), Pair("Completato", "1"),
Pair("Completato", "1"), Pair("In corso", "2"),
Pair("In corso", "2"), Pair("Sospeso", "3"),
Pair("Non rilasciato", "5"), Pair("Annunciato", "4"),
Pair("Sospeso", "3"), Pair("Non rilasciato", "5"),
) )
val ORIGIN = arrayOf( private val LANGUAGE_LIST = arrayOf(
Pair("Gioco di carte", "1"), Pair("Doppiato", "2"),
Pair("Light novel", "2"), Pair("RAW", "3"),
Pair("Mange", "3"), Pair("Sottotitolato", "1"),
Pair("Movie", "4"), )
Pair("Musicale", "10"),
Pair("Novel", "6"),
Pair("Storia originale", "9"),
Pair("Videogioco", "7"),
Pair("Visual novel", "8"),
Pair("Web Manga", "11"),
Pair("Web Novel", "12"),
)
val STUDIO = arrayOf( private val ORDER_LIST = arrayOf(
Pair("2:10 AM Animation", "435"), Pair("Rilevanza", "1"),
Pair("8bit", "183"), Pair("Modificato di recente", "2"),
Pair("A-1 Picture", "167"), Pair("Aggiunto di recente", "3"),
Pair("A-Real", "468"), Pair("Data di rilascio", "4"),
Pair("A.C.G.T.", "258"), Pair("Nome", "5"),
Pair("Acca Effe", "549"), Pair("Voto", "6"),
Pair("Actas", "367"), Pair("Visualizzazioni", "7"),
Pair("AIC", "267"), Pair("Episodi", "8"),
Pair("AIC ASTA", "381"), Pair("Casuale", "9"),
Pair("AIC Build", "220"), )
Pair("AIC Classic", "306"),
Pair("AIC Plus+", "198"),
Pair("AIC Spirits", "356"),
Pair("Ajia-Do", "212"),
Pair("Akatsuki", "582"),
Pair("Albacrow", "553"),
Pair("ANIK", "362"),
Pair("Anima&Co", "502"),
Pair("Animation Planet", "500"),
Pair("Animax", "318"),
Pair("Anpro", "423"),
Pair("APPP", "495"),
Pair("AQUA ARIS", "533"),
Pair("ARECT", "574"),
Pair("Arms", "205"),
Pair("Artland", "263"),
Pair("Arvo Animation", "521"),
Pair("Asahi Production", "394"),
Pair("Ashi Production", "604"),
Pair("ASK Animation Studio", "592"),
Pair("Asread", "257"),
Pair("Atelier Pontdarc", "595"),
Pair("AtelierPontdarc", "572"),
Pair("AXsiZ", "251"),
Pair("B&T", "441"),
Pair("B.CMAY PICTURES", "526"),
Pair("Bakken Record", "555"),
Pair("Bandai Namco Pictures", "324"),
Pair("Barnum Studio", "436"),
Pair("Bee Media", "567"),
Pair("Bee Train", "287"),
Pair("BeSTACK", "266"),
Pair("Bibury Animation Studios", "369"),
Pair("Big FireBird Animation", "557"),
Pair("blade", "347"),
Pair("Bones", "193"),
Pair("Bouncy", "416"),
Pair("Brain's Base", "189"),
Pair("Bridge", "268"),
Pair("Buemon", "518"),
Pair("Bushiroad", "539"),
Pair("C-Station", "253"),
Pair("C2C", "398"),
Pair("Chaos Project", "537"),
Pair("Charaction", "552"),
Pair("Children's Playground Entertainment", "509"),
Pair("Chippai", "483"),
Pair("CLAP", "588"),
Pair("CloverWorks", "237"),
Pair("Colored Pencil Animation", "570"),
Pair("CoMix Wave Films", "265"),
Pair("Connect", "430"),
Pair("Craftar Studios", "377"),
Pair("Creators in Pack", "269"),
Pair("CyberConnect2", "563"),
Pair("CygamesPictures", "511"),
Pair("DandeLion Animation Studio", "333"),
Pair("Daume", "316"),
Pair("David Production", "254"),
Pair("De Mas & Partners", "560"),
Pair("Diomedea", "192"),
Pair("DLE", "387"),
Pair("DMM pictures", "538"),
Pair("DMM.futureworks", "525"),
Pair("Doga Kobo", "225"),
Pair("domerica", "599"),
Pair("DR Movie", "328"),
Pair("Drive", "503"),
Pair("drop", "357"),
Pair("Dynamo Pictures", "507"),
Pair("EKACHI EPILKA", "382"),
Pair("Emon Animation Company", "380"),
Pair("Emon, Blade", "348"),
Pair("EMT²", "274"),
Pair("Encourage Films", "312"),
Pair("ENGI", "522"),
Pair("EXNOA", "575"),
Pair("Ezo'la", "207"),
Pair("Fanworks", "343"),
Pair("feel.", "247"),
Pair("Felix Film", "399"),
Pair("Foch", "586"),
Pair("Frederator Studios", "556"),
Pair("Funimation", "313"),
Pair("G&G Entertainment", "456"),
Pair("G-angle", "497"),
Pair("G.CMay Animation & Film", "365"),
Pair("Gaina", "336"),
Pair("Gainax", "216"),
Pair("Gainax Kyoto", "501"),
Pair("Gallop", "486"),
Pair("Gambit", "573"),
Pair("Gathering", "401"),
Pair("GEEK TOYS", "412"),
Pair("Genco", "418"),
Pair("Geno Studio", "223"),
Pair("GoHands", "213"),
Pair("Gonzo", "181"),
Pair("Graphinica", "255"),
Pair("Gravity Well", "603"),
Pair("GRIZZLY", "428"),
Pair("Group TAC", "335"),
Pair("Hal Film Maker", "323"),
Pair("Haoliners Animation League", "262"),
Pair("HMCH", "600"),
Pair("Hobi Animation", "417"),
Pair("Hoods Entertainment", "175"),
Pair("HOTZIPANG", "505"),
Pair("ILCA", "427"),
Pair("IMAGICA Lab.", "459"),
Pair("Imagin", "233"),
Pair("Imagineer", "564"),
Pair("Ishimori Entertainment", "485"),
Pair("ixtl", "479"),
Pair("J.C.Staff", "178"),
Pair("Jinnis Animation Studios", "482"),
Pair("Jumondo", "589"),
Pair("Jumondou", "591"),
Pair("Kachidoki Studio", "540"),
Pair("Kamikaze Douga", "383"),
Pair("Karaku", "349"),
Pair("Kazami Gakuen Koushiki Douga-bu", "488"),
Pair("Khara", "569"),
Pair("Kigumi", "587"),
Pair("Kinema Citrus", "185"),
Pair("Kitty Films", "530"),
Pair("KJJ Animation", "442"),
Pair("Knack Productions", "451"),
Pair("Kyoto Animation", "179"),
Pair("Kyotoma", "379"),
Pair("LandQ studios", "342"),
Pair("Lapin Track", "583"),
Pair("Larx Entertainment", "397"),
Pair("Lay-duce", "239"),
Pair("Lerche", "195"),
Pair("Lesprit", "462"),
Pair("LICO", "545"),
Pair("LIDENFILMS", "311"),
Pair("Lilix", "403"),
Pair("M.S.C", "390"),
Pair("Madhouse", "165"),
Pair("Magic Bus", "484"),
Pair("Maho Film", "375"),
Pair("Manglobe", "199"),
Pair("Manpuku Jinja", "331"),
Pair("MAPPA", "182"),
Pair("Marvy Jack", "597"),
Pair("MASTER LIGHTS", "469"),
Pair("Mili Pictures", "585"),
Pair("Millepensee", "321"),
Pair("monofilmo", "411"),
Pair("Movic", "338"),
Pair("Mushi Production", "506"),
Pair("Namu Animation", "314"),
Pair("NAZ", "206"),
Pair("Nexus", "359"),
Pair("Nippon Animation", "388"),
Pair("Nippon Columbia", "226"),
Pair("Nomad", "332"),
Pair("Non menzionato", "392"),
Pair("Nut", "235"),
Pair("Oddjob", "374"),
Pair("Office DCI", "425"),
Pair("Okuruto Noboru", "548"),
Pair("OLM", "320"),
Pair("Orange", "256"),
Pair("Ordet", "433"),
Pair("Oz", "580"),
Pair("P.A.Works", "169"),
Pair("Palm Studio", "494"),
Pair("Paper Plane Animation Studio", "571"),
Pair("Passione", "202"),
Pair("Pastel", "566"),
Pair("Pb Animation", "598"),
Pair("Pierrot Plus", "187"),
Pair("Pine Jam", "218"),
Pair("Planet", "447"),
Pair("Platinum Vision", "249"),
Pair("Plum", "368"),
Pair("Polygon Pictures", "186"),
Pair("PRA", "480"),
Pair("Primastea", "420"),
Pair("PrimeTime", "363"),
Pair("Production +h.", "584"),
Pair("production doA", "238"),
Pair("Production I.G", "191"),
Pair("Production IMS", "273"),
Pair("Production Reed", "211"),
Pair("Project No.9", "264"),
Pair("Quad", "581"),
Pair("Qualia Animation", "463"),
Pair("Qubic Pictures", "559"),
Pair("Radix", "371"),
Pair("Rainbow", "499"),
Pair("Revoroot", "431"),
Pair("Rising Force", "487"),
Pair("Rooster Teeth", "461"),
Pair("Saetta", "272"),
Pair("SANZIGEN", "340"),
Pair("Satelight", "197"),
Pair("Science SARU", "201"),
Pair("Scooter Films", "590"),
Pair("Sentai Filmworks", "408"),
Pair("Seven", "229"),
Pair("Seven Arcs", "351"),
Pair("Seven Arcs Pictures", "352"),
Pair("Seven Stone Entertainment", "422"),
Pair("Shaft", "203"),
Pair("Shanghai Foch Film Culture Investmen", "445"),
Pair("Shin-Ei Animation", "215"),
Pair("Shirogumi", "413"),
Pair("Shuka", "177"),
Pair("Signal.MD", "400"),
Pair("Silver Link", "194"),
Pair("Sola Digital Arts", "576"),
Pair("Square-Enix", "337"),
Pair("Staple Entertainment", "593"),
Pair("Studio 3Hz", "252"),
Pair("Studio 4°C", "344"),
Pair("Studio A-CAT", "373"),
Pair("Studio Animal", "271"),
Pair("Studio Bind", "547"),
Pair("Studio Blanc", "330"),
Pair("Studio Chizu", "424"),
Pair("Studio Colorido", "498"),
Pair("Studio Comet", "329"),
Pair("Studio Daisy", "578"),
Pair("Studio Deen", "204"),
Pair("Studio Elle", "520"),
Pair("Studio Fantasia", "407"),
Pair("Studio Flad", "327"),
Pair("Studio Gallop", "334"),
Pair("Studio Ghibli", "279"),
Pair("Studio Gokumi", "250"),
Pair("Studio Hibari", "492"),
Pair("Studio Hokiboshi", "524"),
Pair("Studio Kafka", "568"),
Pair("Studio Kai", "558"),
Pair("Studio LAN", "434"),
Pair("Studio Live", "542"),
Pair("Studio M2", "577"),
Pair("Studio Mir", "601"),
Pair("studio MOTHER", "596"),
Pair("Studio Pierrot", "168"),
Pair("Studio Ponoc", "546"),
Pair("Studio PuYUKAI", "244"),
Pair("Studio Rikka", "460"),
Pair("Studio Signpost", "510"),
Pair("Studio Voln", "240"),
Pair("Studio! Cucuri", "414"),
Pair("Sublimation", "532"),
Pair("Success", "602"),
Pair("Suiseisha", "457"),
Pair("Sunrise", "190"),
Pair("Sunrise Beyond", "515"),
Pair("SynergySP", "364"),
Pair("TAKI Corporation", "475"),
Pair("Tatsunoko Production", "227"),
Pair("Team TillDawn", "535"),
Pair("Team YokkyuFuman", "496"),
Pair("Tear Studio", "561"),
Pair("Telecom Animation Film", "228"),
Pair("Tencent Animation & Comics", "467"),
Pair("Tencent Penguin Pictures", "579"),
Pair("Tezuka Productions", "241"),
Pair("Think Corporation", "528"),
Pair("Thundray", "471"),
Pair("TMS Entertainment", "214"),
Pair("TNK", "208"),
Pair("Toei Animation", "224"),
Pair("Tokyo Kids", "366"),
Pair("Tokyo Movie Shinsha", "410"),
Pair("Tomason", "554"),
Pair("Topcraft", "565"),
Pair("Trans Arts", "432"),
Pair("Triangle Staff", "231"),
Pair("TriF Studio", "440"),
Pair("Trigger", "200"),
Pair("Trinet Entertainment", "421"),
Pair("TROYCA", "230"),
Pair("Tsuchida Productions", "562"),
Pair("Twilight Studio", "481"),
Pair("TYO Animations", "236"),
Pair("Typhoon Graphics", "465"),
Pair("ufotable", "166"),
Pair("Urban Production", "376"),
Pair("UWAN Pictures", "385"),
Pair("VAP", "319"),
Pair("Vega Entertainment", "361"),
Pair("Visual Flight", "550"),
Pair("W-Toon Studio", "402"),
Pair("Wao world", "284"),
Pair("Wawayu Animation", "358"),
Pair("White Fox", "161"),
Pair("Wit Studio", "176"),
Pair("WolfsBane", "523"),
Pair("Xebec", "196"),
Pair("Yaoyorozu", "396"),
Pair("Yokohama Animation Lab", "426"),
Pair("Yostar Pictures", "551"),
Pair("Yumeta Company", "222"),
Pair("Zero-G", "242"),
Pair("Zexcs", "259"),
)
val SEASON = arrayOf(
ALL,
Pair("Inverno", "winter"),
Pair("Primavera", "spring"),
Pair("Estate", "summer"),
Pair("Autunno", "fall"),
)
val YEAR = arrayOf(ALL) + (1984..2023).map {
Pair(it.toString(), it.toString())
}.reversed().toTypedArray()
}
} }

View File

@ -0,0 +1,41 @@
package eu.kanade.tachiyomi.animeextension.it.aniplay
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
/**
* Springboard that accepts https://aniplay.co/series/<item> intents
* and redirects them to the main Aniyomi process.
*/
class AniPlayUrlActivity : Activity() {
private val tag = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
val item = pathSegments[1]
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.ANIMESEARCH"
putExtra("query", "${AniPlay.PREFIX_SEARCH}$item")
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e(tag, e.toString())
}
} else {
Log.e(tag, "could not parse uri from intent $intent")
}
finish()
exitProcess(0)
}
}

View File

@ -0,0 +1,68 @@
package eu.kanade.tachiyomi.animeextension.it.aniplay.dto
import eu.kanade.tachiyomi.animesource.model.SAnime
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class PopularResponseDto(
val data: List<PopularAnimeDto>,
val pagination: PaginationDto,
)
@Serializable
data class PopularAnimeDto(
val id: Int,
@SerialName("title") val name: String,
private val cover: String? = null,
private val main_image: String? = null,
) {
fun toSAnime() = SAnime.create().apply {
url = "/series/$id"
title = name
thumbnail_url = cover ?: main_image
}
}
@Serializable
data class PaginationDto(val page: Int, val pageCount: Int) {
val hasNextPage get() = page < pageCount
}
@Serializable
data class LatestItemDto(val serie: List<PopularAnimeDto>)
@Serializable
data class AnimeInfoDto(
val title: String,
val description: String? = null,
val alternative: String? = null,
val status: String? = null,
val origin: String? = null,
val release_day: String? = null,
val genres: List<NameDto> = emptyList(),
val studios: List<NameDto> = emptyList(),
private val cover: String? = null,
private val main_image: String? = null,
) {
val thumbnailUrl = cover ?: main_image
}
@Serializable
data class NameDto(val name: String)
@Serializable
data class EpisodeDto(
val id: Int,
val title: String? = null,
val number: String? = null,
val release_date: String? = null,
)
@Serializable
data class VideoDto(
private val download_link: String? = null,
private val streaming_link: String? = null,
) {
val videoLink = streaming_link ?: download_link!!
}