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
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 515 additions and 800 deletions

View File

@ -1,2 +1,22 @@
<?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 {
extName = 'AniPlay'
extClass = '.AniPlay'
extVersionCode = 6
extVersionCode = 7
}
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
import android.app.Application
import android.content.SharedPreferences
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
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.model.AnimeFilterList
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.Video
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.awaitSuccess
import kotlinx.serialization.json.Json
import okhttp3.Headers
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
@ -32,264 +34,158 @@ class AniPlay : ConfigurableAnimeSource, AnimeHttpSource() {
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 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)
}
// ============================== 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 popularAnimeRequest(page: Int): Request =
GET("$baseUrl/api/anime/advanced-similar-search?page=${page - 1}&size=36&sort=views,desc&sort=id")
override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = response.parseAs<PopularResponseDto>()
val animes = parsed.data.map(PopularAnimeDto::toSAnime)
return AnimesPage(animes, parsed.pagination.hasNextPage)
}
// =============================== 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 = throw UnsupportedOperationException()
override fun latestUpdatesParse(response: Response): AnimesPage {
val items = response.parseAs<List<LatestItemDto>>()
val animes = items.mapNotNull { it.serie.firstOrNull()?.toSAnime() }
return AnimesPage(animes, items.size == 20)
}
// =============================== Search ===============================
override fun getFilterList() = AniPlayFilters.FILTER_LIST
override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
val params = AniPlayFilters.getSearchParameters(filters)
return client.newCall(searchAnimeRequest(page, query, params))
.awaitSuccess()
.use(::searchAnimeParse)
}
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")
return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
val id = query.removePrefix(PREFIX_SEARCH)
client.newCall(GET("$baseUrl/series/$id"))
.awaitSuccess()
.use(::searchAnimeByIdParse)
} else {
"$baseUrl/api/anime/advanced-similar-search".toHttpUrlOrNull()!!.newBuilder()
.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)
super.getSearchAnime(page, query, filters)
}
return GET(url.build().toString())
}
override fun searchAnimeParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<List<SearchResult>>(response.body.string())
val animeList = parsed.map { ani ->
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)
private fun searchAnimeByIdParse(response: Response): AnimesPage {
val details = animeDetailsParse(response)
.apply { setUrlWithoutDomain(response.request.url.toString()) }
return AnimesPage(listOf(details), false)
}
// ============================== 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 ============================
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 {
val detailsJson = json.decodeFromString<AnimeResult>(response.body.string())
description = buildString {
parsed.description?.also {
append(it, "\n\n")
}
return SAnime.create().apply {
title = detailsJson.title
author = detailsJson.studio
status = parseStatus(detailsJson.status)
description = buildString {
append(detailsJson.storyline)
append("\n\nTipologia: ${detailsJson.type}")
append("\nOrigine: ${detailsJson.origin}")
if (detailsJson.startDate != null) append("\nData di inizio: ${detailsJson.startDate}")
append("\nStato: ${detailsJson.status}")
listOf(
"Titolo Alternativo" to parsed.alternative,
"Origine" to parsed.origin,
"Giorno di lancio" to parsed.release_day,
).forEach { (title, value) ->
if (value != null) append(title, ": ", value, "\n")
}
}
}
// ============================== Episodes ==============================
override fun episodeListRequest(anime: SAnime): Request = GET("$baseUrl/api/anime/${anime.url}")
override fun episodeListParse(response: Response): List<SEpisode> {
val animeJson = json.decodeFromString<AnimeResult>(response.body.string())
val episodeList = mutableListOf<SEpisode>()
val script = response.getPageScript()
val jsonString = script.substringAfter(",episodes:").substringBefore("]},") + "]"
val parsed = jsonString.fixJsonString().parseAs<List<EpisodeDto>>()
if (animeJson.seasons.isNotEmpty()) {
for (season in animeJson.seasons) {
val episodesResponse = client.newCall(
GET("$baseUrl/api/anime/${animeJson.id}/season/${season.id}"),
).execute()
val episodesJson = json.decodeFromString<List<AnimeResult.Episode>>(episodesResponse.body.string())
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()
}
},
)
return parsed.map {
SEpisode.create().apply {
episode_number = it.number?.toFloatOrNull() ?: 1F
url = "/watch/${it.id}"
name = it.title ?: "Episodio ${it.number}"
date_upload = it.release_date.toDate()
}
} else if (animeJson.episodes.isNotEmpty()) {
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()
}.reversed()
}
// ============================ Video Links =============================
override fun videoListRequest(episode: SEpisode): Request = GET("$baseUrl/api/episode/${episode.url}")
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
override fun videoListParse(response: Response): List<Video> {
val videoList = mutableListOf<Video>()
val videoJson = json.decodeFromString<VideoResult>(response.body.string())
val videoUrl = videoJson.videoUrl
val script = response.getPageScript()
val jsonString = script.substringAfter("{episode:").substringBefore(",views") + "}"
val videoUrl = jsonString.fixJsonString().parseAs<VideoDto>().videoLink
if (videoUrl.contains(".mp4")) {
videoList.add(
Video(
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/")))
}
return when {
videoUrl.contains(".m3u8") -> playlistUtils.extractFromHls(videoUrl)
else -> listOf(Video(videoUrl, "Default", videoUrl, headers = headers))
}
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> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
return this.sortedWith(
compareBy(
{ it.quality.contains(quality) },
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
),
return sortedWith(
compareBy { it.quality.contains(quality) },
).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 ==============================
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 {
key = PREF_QUALITY_KEY
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "80p")
entryValues = arrayOf("1080", "720", "480", "360", "240", "80")
title = PREF_QUALITY_TITLE
entries = PREF_QUALITY_ENTRIES
entryValues = PREF_QUALITY_ENTRIES
setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s"
@ -301,4 +197,42 @@ class AniPlay : ConfigurableAnimeSource, AnimeHttpSource() {
}
}.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
object AniPlayFilters {
open class QueryPartFilter(
open class SelectFilter(
displayName: String,
val vals: Array<Pair<String, String>>,
) : AnimeFilter.Select<String>(
displayName,
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 class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
return this.filterIsInstance<R>().joinToString("") {
(it as QueryPartFilter).toQueryPart()
}
private inline fun <reified R> AnimeFilterList.getSelected(): String {
return (first { it is R } as SelectFilter).selected
}
class GenreFilter : CheckBoxFilterList(
"Genere",
AniPlayFiltersData.GENRE.map { CheckBoxVal(it.first, false) },
)
open class CheckBoxFilterList(name: String, val pairs: Array<Pair<String, String>>) :
AnimeFilter.Group<AnimeFilter.CheckBox>(name, pairs.map { CheckBoxVal(it.first) })
class TypeFilter : CheckBoxFilterList(
"Tipologia anime",
AniPlayFiltersData.TYPE.map { CheckBoxVal(it.first, false) },
)
private class CheckBoxVal(name: String) : AnimeFilter.CheckBox(name, false)
class StatusFilter : CheckBoxFilterList(
"Stato",
AniPlayFiltersData.STATUS.map { CheckBoxVal(it.first, false) },
)
private inline fun <reified R> AnimeFilterList.parseCheckbox(
options: Array<Pair<String, String>>,
): 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(
"Origine",
AniPlayFiltersData.ORIGIN.map { CheckBoxVal(it.first, false) },
)
internal class OrderFilter : SelectFilter("Ordina per", ORDER_LIST)
class StudioFilter : CheckBoxFilterList(
"Studio",
AniPlayFiltersData.STUDIO.map { CheckBoxVal(it.first, false) },
)
internal class GenreFilter : CheckBoxFilterList("Generi", GENRE_LIST)
internal class CountryFilter : CheckBoxFilterList("Paesi", COUNTRY_LIST)
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)
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(
internal val FILTER_LIST get() = AnimeFilterList(
OrderFilter(),
AnimeFilter.Separator(),
GenreFilter(),
CountryFilter(),
TypeFilter(),
StatusFilter(),
OriginFilter(),
StudioFilter(),
AnimeFilter.Separator(),
AnimeFilter.Header("Anni"),
StartFilter(),
EndFilter(),
AnimeFilter.Separator(),
AnimeFilter.Header("Anime stagionali"),
AnimeFilter.Header("(ignora altri filtri tranne l'ordinamento per)"),
YearFilter(),
SeasonFilter(),
StatusFilter(),
LanguageFilter(),
)
data class FilterSearchParams(
val order: String = "views,desc",
val genre: String = "",
val type: String = "",
internal data class FilterSearchParams(
val order: String = "1",
val genres: String = "",
val countries: String = "",
val types: String = "",
val studios: String = "",
val status: String = "",
val origin: String = "",
val studio: String = "",
val start: String = "",
val end: String = "",
val year: String = "",
val season: String = "",
val languages: String = "",
)
internal fun getSearchParameters(filters: AnimeFilterList): 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(
filters.asQueryPart<OrderFilter>(),
genre,
type,
status,
origin,
studio,
filters.asQueryPart<StartFilter>(),
filters.asQueryPart<EndFilter>(),
filters.asQueryPart<YearFilter>(),
filters.asQueryPart<SeasonFilter>(),
filters.getSelected<OrderFilter>(),
filters.parseCheckbox<GenreFilter>(GENRE_LIST),
filters.parseCheckbox<CountryFilter>(COUNTRY_LIST),
filters.parseCheckbox<TypeFilter>(TYPE_LIST),
filters.parseCheckbox<StudioFilter>(STUDIO_LIST),
filters.parseCheckbox<StatusFilter>(STATUS_LIST),
filters.parseCheckbox<LanguageFilter>(LANGUAGE_LIST),
)
}
private object AniPlayFiltersData {
val ALL = Pair("All", "")
private val GENRE_LIST = arrayOf(
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(
Pair("Popolarità decrescente", "views,desc"),
Pair("Popolarità crescente", "views,asc"),
Pair("Titolo decrescente", "title,desc"),
Pair("Titolo crescente", "title,asc"),
Pair("Numero episodi decrescente", "episodeNumber,desc"),
Pair("Numero episodi crescente", "episodeNumber,asc"),
Pair("Data di inizio decrescente", "startDate,desc"),
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"),
)
private val COUNTRY_LIST = arrayOf(
Pair("Corea del Sud", "KR"),
Pair("Cina", "CN"),
Pair("Hong Kong", "HK"),
Pair("Filippine", "PH"),
Pair("Giappone", "JP"),
Pair("Taiwan", "TW"),
Pair("Thailandia", "TH"),
)
val GENRE = arrayOf(
Pair("Arti marziali", "68"),
Pair("Automobilismo", "90"),
Pair("Avventura", "25"),
Pair("Azione", "38"),
Pair("Boys Love", "104"),
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"),
)
private val TYPE_LIST = arrayOf(
Pair("Serie", "1"),
Pair("Movie", "2"),
Pair("OVA", "3"),
Pair("ONA", "4"),
Pair("Special", "5"),
)
val TYPE = arrayOf(
Pair("Movie", "2"),
Pair("ONA", "4"),
Pair("OVA", "3"),
Pair("Serie", "1"),
Pair("Special", "5"),
)
private val STUDIO_LIST = arrayOf(
Pair("2:10 AM Animation", "190"),
Pair("5 Inc.", "309"),
Pair("8bit", "17"),
Pair("A-1 Picture", "11"),
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(
Pair("Annunciato", "4"),
Pair("Completato", "1"),
Pair("In corso", "2"),
Pair("Non rilasciato", "5"),
Pair("Sospeso", "3"),
)
private val STATUS_LIST = arrayOf(
Pair("Completato", "1"),
Pair("In corso", "2"),
Pair("Sospeso", "3"),
Pair("Annunciato", "4"),
Pair("Non rilasciato", "5"),
)
val ORIGIN = arrayOf(
Pair("Gioco di carte", "1"),
Pair("Light novel", "2"),
Pair("Mange", "3"),
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"),
)
private val LANGUAGE_LIST = arrayOf(
Pair("Doppiato", "2"),
Pair("RAW", "3"),
Pair("Sottotitolato", "1"),
)
val STUDIO = arrayOf(
Pair("2:10 AM Animation", "435"),
Pair("8bit", "183"),
Pair("A-1 Picture", "167"),
Pair("A-Real", "468"),
Pair("A.C.G.T.", "258"),
Pair("Acca Effe", "549"),
Pair("Actas", "367"),
Pair("AIC", "267"),
Pair("AIC ASTA", "381"),
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()
}
private val ORDER_LIST = arrayOf(
Pair("Rilevanza", "1"),
Pair("Modificato di recente", "2"),
Pair("Aggiunto di recente", "3"),
Pair("Data di rilascio", "4"),
Pair("Nome", "5"),
Pair("Voto", "6"),
Pair("Visualizzazioni", "7"),
Pair("Episodi", "8"),
Pair("Casuale", "9"),
)
}

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!!
}