Fix(en/AniPlay): Update source (#206)

This commit is contained in:
Dark25 2024-09-02 12:28:49 +01:00 committed by GitHub
parent 7a71a9d6fd
commit ffb39ec544
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 413 additions and 55 deletions

View file

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc")
}
baseVersionCode = 2
baseVersionCode = 3

View file

@ -8,6 +8,10 @@ import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.add
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray
import okhttp3.FormBody
import okhttp3.Request
import okhttp3.Response
@ -32,7 +36,7 @@ abstract class AniListAnimeHttpSource : AnimeHttpSource() {
query = ANIME_LIST_QUERY,
variables = AnimeListVariables(
page = page,
sort = AnimeListVariables.MediaSort.POPULARITY_DESC,
sort = AnimeListVariables.MediaSort.TRENDING_DESC,
),
)
}
@ -58,20 +62,58 @@ abstract class AniListAnimeHttpSource : AnimeHttpSource() {
/* ===================================== Search Anime ===================================== */
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
return buildAnimeListRequest(
query = ANIME_LIST_QUERY,
variables = AnimeListVariables(
page = page,
sort = AnimeListVariables.MediaSort.SEARCH_MATCH,
search = query.ifBlank { null },
),
)
val params = AniListFilters.getSearchParameters(filters)
val variablesObject = buildJsonObject {
put("page", page)
put("perPage", 30)
put("isAdult", false)
put("type", "ANIME")
put("sort", params.sort)
if (query.isNotBlank()) put("search", query)
if (params.genres.isNotEmpty()) {
putJsonArray("genres") {
params.genres.forEach { add(it) }
}
}
if (params.format.isNotEmpty()) {
putJsonArray("format") {
params.format.forEach { add(it) }
}
}
if (params.season.isBlank() && params.year.isNotBlank()) {
put("year", "${params.year}%")
}
if (params.season.isNotBlank() && params.year.isBlank()) {
throw Exception("Year cannot be blank if season is set")
}
if (params.season.isNotBlank() && params.year.isNotBlank()) {
put("season", params.season)
put("seasonYear", params.year)
}
if (params.status.isNotBlank()) {
put("status", params.status)
}
}
val variables = json.encodeToString(variablesObject)
return buildRequest(query = SORT_QUERY, variables = variables)
}
override fun searchAnimeParse(response: Response): AnimesPage {
return parseAnimeListResponse(response)
}
// ============================== Filters ===============================
override fun getFilterList(): AnimeFilterList = AniListFilters.FILTER_LIST
/* ===================================== Anime Details ===================================== */
override fun animeDetailsRequest(anime: SAnime): Request {
return buildRequest(
@ -145,6 +187,9 @@ abstract class AniListAnimeHttpSource : AnimeHttpSource() {
status = when (media.status) {
AniListMedia.Status.RELEASING -> SAnime.ONGOING
AniListMedia.Status.FINISHED -> SAnime.COMPLETED
AniListMedia.Status.NOT_YET_RELEASED -> SAnime.LICENSED
AniListMedia.Status.CANCELLED -> SAnime.CANCELLED
AniListMedia.Status.HIATUS -> SAnime.ON_HIATUS
}
thumbnail_url = media.coverImage.large
}

View file

@ -0,0 +1,236 @@
package eu.kanade.tachiyomi.multisrc.anilist
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object AniListFilters {
open class QueryPartFilter(
displayName: String,
val vals: Array<Pair<String, String>>,
) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray(),
) {
fun toQueryPart() = vals[state].second
}
open class CheckBoxFilterList(name: String, val pairs: Array<Pair<String, String>>) :
AnimeFilter.Group<AnimeFilter.CheckBox>(name, pairs.map { CheckBoxVal(it.first, false) })
private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
return (getFirst<R>() as QueryPartFilter).toQueryPart()
}
private inline fun <reified R> AnimeFilterList.getFirst(): R {
return first { it is R } as R
}
private inline fun <reified R> AnimeFilterList.parseCheckboxList(
options: Array<Pair<String, String>>,
): List<String> {
return (getFirst<R>() as CheckBoxFilterList).state
.filter { it.state }
.map { checkBox -> options.find { it.first == checkBox.name }!!.second }
.filter(String::isNotBlank)
}
private inline fun <reified R> AnimeFilterList.getSort(): String {
val state = (getFirst<R>() as AnimeFilter.Sort).state ?: return ""
val index = state.index
val suffix = if (state.ascending) "" else "_DESC"
return AniListFiltersData.SORT_LIST[index].second + suffix
}
class GenreFilter : CheckBoxFilterList("Genres", AniListFiltersData.GENRE_LIST)
class YearFilter : QueryPartFilter("Year", AniListFiltersData.YEAR_LIST)
class SeasonFilter : QueryPartFilter("Season", AniListFiltersData.SEASON_LIST)
class FormatFilter : CheckBoxFilterList("Format", AniListFiltersData.FORMAT_LIST)
class StatusFilter : QueryPartFilter("Airing Status", AniListFiltersData.STATUS_LIST)
class SortFilter : AnimeFilter.Sort(
"Sort",
AniListFiltersData.SORT_LIST.map { it.first }.toTypedArray(),
Selection(1, false),
)
val FILTER_LIST get() = AnimeFilterList(
GenreFilter(),
YearFilter(),
SeasonFilter(),
FormatFilter(),
StatusFilter(),
SortFilter(),
)
class FilterSearchParams(
val genres: List<String> = emptyList(),
val year: String = "",
val season: String = "",
val format: List<String> = emptyList(),
val status: String = "",
val sort: String = "",
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams()
return FilterSearchParams(
filters.parseCheckboxList<GenreFilter>(AniListFiltersData.GENRE_LIST),
filters.asQueryPart<YearFilter>(),
filters.asQueryPart<SeasonFilter>(),
filters.parseCheckboxList<FormatFilter>(AniListFiltersData.FORMAT_LIST),
filters.asQueryPart<StatusFilter>(),
filters.getSort<SortFilter>(),
)
}
private object AniListFiltersData {
val GENRE_LIST = arrayOf(
Pair("Action", "Action"),
Pair("Adventure", "Adventure"),
Pair("Comedy", "Comedy"),
Pair("Drama", "Drama"),
Pair("Ecchi", "Ecchi"),
Pair("Fantasy", "Fantasy"),
Pair("Horror", "Horror"),
Pair("Mahou Shoujo", "Mahou Shoujo"),
Pair("Mecha", "Mecha"),
Pair("Music", "Music"),
Pair("Mystery", "Mystery"),
Pair("Psychological", "Psychological"),
Pair("Romance", "Romance"),
Pair("Sci-Fi", "Sci-Fi"),
Pair("Slice of Life", "Slice of Life"),
Pair("Sports", "Sports"),
Pair("Supernatural", "Supernatural"),
Pair("Thriller", "Thriller"),
)
val YEAR_LIST = arrayOf(
Pair("<Select>", ""),
Pair("2024", "2024"),
Pair("2023", "2023"),
Pair("2022", "2022"),
Pair("2021", "2021"),
Pair("2020", "2020"),
Pair("2019", "2019"),
Pair("2018", "2018"),
Pair("2017", "2017"),
Pair("2016", "2016"),
Pair("2015", "2015"),
Pair("2014", "2014"),
Pair("2013", "2013"),
Pair("2012", "2012"),
Pair("2011", "2011"),
Pair("2010", "2010"),
Pair("2009", "2009"),
Pair("2008", "2008"),
Pair("2007", "2007"),
Pair("2006", "2006"),
Pair("2005", "2005"),
Pair("2004", "2004"),
Pair("2003", "2003"),
Pair("2002", "2002"),
Pair("2001", "2001"),
Pair("2000", "2000"),
Pair("1999", "1999"),
Pair("1998", "1998"),
Pair("1997", "1997"),
Pair("1996", "1996"),
Pair("1995", "1995"),
Pair("1994", "1994"),
Pair("1993", "1993"),
Pair("1992", "1992"),
Pair("1991", "1991"),
Pair("1990", "1990"),
Pair("1989", "1989"),
Pair("1988", "1988"),
Pair("1987", "1987"),
Pair("1986", "1986"),
Pair("1985", "1985"),
Pair("1984", "1984"),
Pair("1983", "1983"),
Pair("1982", "1982"),
Pair("1981", "1981"),
Pair("1980", "1980"),
Pair("1979", "1979"),
Pair("1978", "1978"),
Pair("1977", "1977"),
Pair("1976", "1976"),
Pair("1975", "1975"),
Pair("1974", "1974"),
Pair("1973", "1973"),
Pair("1972", "1972"),
Pair("1971", "1971"),
Pair("1970", "1970"),
Pair("1969", "1969"),
Pair("1968", "1968"),
Pair("1967", "1967"),
Pair("1966", "1966"),
Pair("1965", "1965"),
Pair("1964", "1964"),
Pair("1963", "1963"),
Pair("1962", "1962"),
Pair("1961", "1961"),
Pair("1960", "1960"),
Pair("1959", "1959"),
Pair("1958", "1958"),
Pair("1957", "1957"),
Pair("1956", "1956"),
Pair("1955", "1955"),
Pair("1954", "1954"),
Pair("1953", "1953"),
Pair("1952", "1952"),
Pair("1951", "1951"),
Pair("1950", "1950"),
Pair("1949", "1949"),
Pair("1948", "1948"),
Pair("1947", "1947"),
Pair("1946", "1946"),
Pair("1945", "1945"),
Pair("1944", "1944"),
Pair("1943", "1943"),
Pair("1942", "1942"),
Pair("1941", "1941"),
Pair("1940", "1940"),
)
val SEASON_LIST = arrayOf(
Pair("<Select>", ""),
Pair("Winter", "WINTER"),
Pair("Spring", "SPRING"),
Pair("Summer", "SUMMER"),
Pair("Fall", "FALL"),
)
val FORMAT_LIST = arrayOf(
Pair("TV Show", "TV"),
Pair("Movie", "MOVIE"),
Pair("TV Short", "TV_SHORT"),
Pair("Special", "SPECIAL"),
Pair("OVA", "OVA"),
Pair("ONA", "ONA"),
Pair("Music", "MUSIC"),
)
val STATUS_LIST = arrayOf(
Pair("<Select>", ""),
Pair("Airing", "RELEASING"),
Pair("Finished", "FINISHED"),
Pair("Not Yet Aired", "NOT_YET_RELEASED"),
Pair("Cancelled", "CANCELLED"),
)
val SORT_LIST = arrayOf(
Pair("Title", "TITLE_ENGLISH"),
Pair("Popularity", "POPULARITY"),
Pair("Average Score", "SCORE"),
Pair("Trending", "TRENDING"),
Pair("Favorites", "FAVOURITES"),
Pair("Date Added", "ID"),
Pair("Release Date", "START_DATE"),
)
}
}

View file

@ -55,16 +55,76 @@ internal const val ANIME_DETAILS_QUERY = """
}
}
"""
private fun String.toQuery() = this.trimIndent().replace("%", "$")
internal val SORT_QUERY = """
query (
${"$"}page: Int,
${"$"}perPage: Int,
${"$"}isAdult: Boolean,
${"$"}type: MediaType,
${"$"}sort: [MediaSort],
${"$"}status: MediaStatus,
${"$"}search: String,
${"$"}genres: [String],
${"$"}year: String,
${"$"}seasonYear: Int,
${"$"}season: MediaSeason,
${"$"}format: [MediaFormat]
) {
Page (page: ${"$"}page, perPage: ${"$"}perPage) {
pageInfo {
hasNextPage
}
media (
isAdult: ${"$"}isAdult,
type: ${"$"}type,
sort: ${"$"}sort,
status: ${"$"}status,
search: ${"$"}search,
genre_in: ${"$"}genres,
startDate_like: ${"$"}year,
seasonYear: ${"$"}seasonYear,
season: ${"$"}season,
format_in: ${"$"}format
) {
id
title {
romaji
english
native
}
coverImage {
extraLarge
large
medium
}
status
genres
studios {
nodes {
name
}
}
}
}
}
""".toQuery()
@Serializable
internal data class AnimeListVariables(
val page: Int,
val sort: MediaSort,
val search: String? = null,
val genre: String? = null,
val year: String? = null,
val status: String? = null,
val format: String? = null,
val season: String? = null,
val seasonYear: String? = null,
val isAdult: Boolean = false,
) {
enum class MediaSort {
POPULARITY_DESC,
SEARCH_MATCH,
TRENDING_DESC,
START_DATE_DESC,
}
}

View file

@ -47,6 +47,9 @@ internal data class AniListMedia(
enum class Status {
RELEASING,
FINISHED,
NOT_YET_RELEASED,
CANCELLED,
HIATUS,
}
@Serializable