AquaStream/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt

904 lines
32 KiB
Kotlin
Raw Normal View History

2021-11-12 16:55:54 +00:00
package com.lagradost.cloudstream3.syncproviders.providers
2021-11-07 22:10:19 +00:00
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
2022-04-03 01:13:02 +00:00
import com.lagradost.cloudstream3.ErrorLoadingException
2021-11-12 16:55:54 +00:00
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.app
2021-11-07 22:10:19 +00:00
import com.lagradost.cloudstream3.mvvm.logError
2021-11-12 16:55:54 +00:00
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.OAuth2API
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.appString
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.maxStale
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.unixTime
import com.lagradost.cloudstream3.syncproviders.SyncAPI
2021-11-07 22:10:19 +00:00
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
2022-01-14 18:14:24 +00:00
import com.lagradost.cloudstream3.utils.AppUtils.toJson
2022-04-03 01:13:02 +00:00
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
2021-11-07 22:10:19 +00:00
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
import java.net.URL
import java.util.*
2021-11-12 16:55:54 +00:00
class AniListApi(index: Int) : AccountManager(index), SyncAPI {
2022-03-16 15:29:11 +00:00
override var name = "AniList"
override val key = "6871"
override val redirectUrl = "anilistlogin"
override val idPrefix = "anilist"
2022-03-16 15:29:11 +00:00
override var mainUrl = "https://anilist.co"
override val icon = R.drawable.ic_anilist_icon
2021-11-07 22:10:19 +00:00
override fun loginInfo(): OAuth2API.LoginInfo? {
2021-11-07 22:10:19 +00:00
// context.getUser(true)?.
getKey<AniListUser>(accountId, ANILIST_USER_KEY)?.let { user ->
2021-11-12 16:55:54 +00:00
return OAuth2API.LoginInfo(
profilePicture = user.picture,
name = user.name,
accountIndex = accountIndex
)
2021-11-07 22:10:19 +00:00
}
return null
}
override fun logOut() {
removeAccountKeys()
2021-11-08 18:13:39 +00:00
}
override fun authenticate() {
2021-11-07 22:10:19 +00:00
val request = "https://anilist.co/api/v2/oauth/authorize?client_id=$key&response_type=token"
openBrowser(request)
2021-11-07 22:10:19 +00:00
}
override fun handleRedirect(url: String) {
2022-04-01 20:05:34 +00:00
val sanitizer =
splitQuery(URL(url.replace(appString, "https").replace("/#", "?"))) // FIX ERROR
val token = sanitizer["access_token"]!!
val expiresIn = sanitizer["expires_in"]!!
val endTime = unixTime + expiresIn.toLong()
switchToNewAccount()
setKey(accountId, ANILIST_UNIXTIME_KEY, endTime)
setKey(accountId, ANILIST_TOKEN_KEY, token)
setKey(ANILIST_SHOULD_UPDATE_LIST, true)
ioSafe {
getUser()
2021-11-07 22:10:19 +00:00
}
}
override suspend fun search(name: String): List<SyncAPI.SyncSearchResult>? {
2021-11-12 16:55:54 +00:00
val data = searchShows(name) ?: return null
2022-04-02 22:06:35 +00:00
return data.data?.Page?.media?.map {
2021-11-12 16:55:54 +00:00
SyncAPI.SyncSearchResult(
2022-04-02 22:06:35 +00:00
it.title.romaji ?: return null,
2021-11-12 16:55:54 +00:00
this.name,
it.id.toString(),
"$mainUrl/anime/${it.id}",
it.bannerImage
)
}
2021-11-07 22:10:19 +00:00
}
override suspend fun getResult(id: String): SyncAPI.SyncResult? {
2021-11-12 16:55:54 +00:00
val internalId = id.toIntOrNull() ?: return null
2022-04-03 01:13:02 +00:00
val season = getSeason(internalId).data?.Media ?: throw ErrorLoadingException("No media")
2021-11-07 22:10:19 +00:00
2021-11-12 16:55:54 +00:00
return SyncAPI.SyncResult(
season.id.toString(),
nextAiring = season.nextAiringEpisode?.let {
SyncAPI.SyncNextAiring(
2022-04-02 22:06:35 +00:00
it.episode ?: return@let null,
(it.timeUntilAiring ?: return@let null) + unixTime
2021-11-12 16:55:54 +00:00
)
},
2022-04-02 22:06:35 +00:00
genres = season.genres,
synonyms = season.synonyms,
isAdult = season.isAdult,
totalEpisodes = season.episodes,
2022-04-03 01:13:02 +00:00
synopsis = season.description,
2021-11-12 16:55:54 +00:00
//TODO REST
)
}
2021-11-07 22:10:19 +00:00
override suspend fun getStatus(id: String): SyncAPI.SyncStatus? {
2021-11-12 16:55:54 +00:00
val internalId = id.toIntOrNull() ?: return null
val data = getDataAboutId(internalId) ?: return null
2021-11-07 22:10:19 +00:00
2021-11-12 16:55:54 +00:00
return SyncAPI.SyncStatus(
score = data.score,
watchedEpisodes = data.episodes,
2022-04-02 22:06:35 +00:00
status = data.type?.value ?: return null,
2021-11-12 16:55:54 +00:00
isFavorite = data.isFavourite,
)
2021-11-07 22:10:19 +00:00
}
override suspend fun score(id: String, status: SyncAPI.SyncStatus): Boolean {
return postDataAboutId(
2021-11-12 16:55:54 +00:00
id.toIntOrNull() ?: return false,
fromIntToAnimeStatus(status.status),
status.score,
status.watchedEpisodes
) ?: return false
2021-11-07 22:10:19 +00:00
}
2021-11-12 16:55:54 +00:00
companion object {
private val mapper = JsonMapper.builder().addModule(KotlinModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!!
2021-11-07 22:10:19 +00:00
2022-01-14 18:14:24 +00:00
private val aniListStatusString =
arrayOf("CURRENT", "COMPLETED", "PAUSED", "DROPPED", "PLANNING", "REPEATING")
2021-11-07 22:10:19 +00:00
2021-11-12 16:55:54 +00:00
const val ANILIST_UNIXTIME_KEY: String = "anilist_unixtime" // When token expires
const val ANILIST_TOKEN_KEY: String = "anilist_token" // anilist token for api
const val ANILIST_USER_KEY: String = "anilist_user" // user data like profile
const val ANILIST_CACHED_LIST: String = "anilist_cached_list"
const val ANILIST_SHOULD_UPDATE_LIST: String = "anilist_should_update_list"
2021-11-07 22:10:19 +00:00
2021-11-12 16:55:54 +00:00
private fun fixName(name: String): String {
2022-01-14 18:14:24 +00:00
return name.lowercase(Locale.ROOT).replace(" ", "")
.replace("[^a-zA-Z0-9]".toRegex(), "")
2021-11-07 22:10:19 +00:00
}
private suspend fun searchShows(name: String): GetSearchRoot? {
2021-11-12 16:55:54 +00:00
try {
val query = """
2021-11-07 22:10:19 +00:00
query (${"$"}id: Int, ${"$"}page: Int, ${"$"}search: String, ${"$"}type: MediaType) {
Page (page: ${"$"}page, perPage: 10) {
media (id: ${"$"}id, search: ${"$"}search, type: ${"$"}type) {
id
idMal
seasonYear
startDate { year month day }
title {
romaji
}
averageScore
meanScore
nextAiringEpisode {
timeUntilAiring
episode
}
trailer { id site thumbnail }
bannerImage
recommendations {
nodes {
id
mediaRecommendation {
id
title {
english
romaji
}
idMal
coverImage { medium large }
averageScore
}
}
}
relations {
edges {
id
relationType(version: 2)
node {
format
id
idMal
coverImage { medium large }
averageScore
title {
english
romaji
}
}
}
}
}
}
}
"""
2021-11-12 16:55:54 +00:00
val data =
mapOf(
"query" to query,
2022-01-14 18:14:24 +00:00
"variables" to
mapOf(
"search" to name,
"page" to 1,
"type" to "ANIME"
).toJson()
2021-11-12 16:55:54 +00:00
)
val res = app.post(
2021-11-12 16:55:54 +00:00
"https://graphql.anilist.co/",
//headers = mapOf(),
data = data,//(if (vars == null) mapOf("query" to q) else mapOf("query" to q, "variables" to vars))
timeout = 5000 // REASONABLE TIMEOUT
).text.replace("\\", "")
return res.toKotlinObject()
} catch (e: Exception) {
logError(e)
}
return null
2021-11-07 22:10:19 +00:00
}
2021-11-12 16:55:54 +00:00
// Should use https://gist.github.com/purplepinapples/5dc60f15f2837bf1cea71b089cfeaa0a
suspend fun getShowId(malId: String?, name: String, year: Int?): GetSearchMedia? {
2021-11-12 16:55:54 +00:00
// Strips these from the name
val blackList = listOf(
"TV Dubbed",
"(Dub)",
"Subbed",
"(TV)",
"(Uncensored)",
"(Censored)",
"(\\d+)" // year
)
val blackListRegex =
2022-01-14 18:14:24 +00:00
Regex(
""" (${
blackList.joinToString(separator = "|").replace("(", "\\(")
.replace(")", "\\)")
})"""
)
2021-11-12 16:55:54 +00:00
//println("NAME $name NEW NAME ${name.replace(blackListRegex, "")}")
val shows = searchShows(name.replace(blackListRegex, ""))
shows?.data?.Page?.media?.find {
malId ?: "NONE" == it.idMal.toString()
}?.let { return it }
val filtered =
shows?.data?.Page?.media?.filter {
(
it.startDate.year ?: year.toString() == year.toString()
|| year == null
)
}
filtered?.forEach {
2022-04-02 22:06:35 +00:00
it.title.romaji?.let { romaji ->
if (fixName(romaji) == fixName(name)) return it
}
2021-11-07 22:10:19 +00:00
}
2021-11-12 16:55:54 +00:00
return filtered?.firstOrNull()
2021-11-07 22:10:19 +00:00
}
2021-11-12 16:55:54 +00:00
// Changing names of these will show up in UI
enum class AniListStatusType(var value: Int) {
Watching(0),
Completed(1),
Paused(2),
Dropped(3),
Planning(4),
2022-04-02 22:06:35 +00:00
ReWatching(5),
2021-11-12 16:55:54 +00:00
None(-1)
}
2021-11-07 22:10:19 +00:00
2021-11-12 16:55:54 +00:00
fun fromIntToAnimeStatus(inp: Int): AniListStatusType {//= AniListStatusType.values().first { it.value == inp }
return when (inp) {
-1 -> AniListStatusType.None
0 -> AniListStatusType.Watching
1 -> AniListStatusType.Completed
2 -> AniListStatusType.Paused
3 -> AniListStatusType.Dropped
4 -> AniListStatusType.Planning
2022-04-02 22:06:35 +00:00
5 -> AniListStatusType.ReWatching
2021-11-12 16:55:54 +00:00
else -> AniListStatusType.None
}
}
fun convertAnilistStringToStatus(string: String): AniListStatusType {
return fromIntToAnimeStatus(aniListStatusString.indexOf(string))
}
2022-04-03 01:13:02 +00:00
private suspend fun getSeason(id: Int): SeasonResponse {
2022-04-02 22:06:35 +00:00
val q = """
2021-11-12 16:55:54 +00:00
query (${'$'}id: Int = $id) {
Media (id: ${'$'}id, type: ANIME) {
id
idMal
2022-04-03 01:13:02 +00:00
coverImage {
extraLarge
large
medium
color
}
2022-04-02 22:06:35 +00:00
duration
episodes
genres
synonyms
averageScore
isAdult
2022-04-03 01:13:02 +00:00
description(asHtml: false)
trailer {
id
site
thumbnail
}
2021-11-12 16:55:54 +00:00
relations {
edges {
id
relationType(version: 2)
node {
id
2022-04-03 01:13:02 +00:00
coverImage {
extraLarge
large
medium
color
}
2021-11-12 16:55:54 +00:00
}
}
}
nextAiringEpisode {
timeUntilAiring
episode
}
format
}
}
"""
val data = app.post(
2021-11-12 16:55:54 +00:00
"https://graphql.anilist.co",
data = mapOf("query" to q),
cacheTime = 0,
).text
2022-04-03 01:13:02 +00:00
return tryParseJson(data) ?: throw ErrorLoadingException("Error parsing $data")
2021-11-07 22:10:19 +00:00
}
}
fun initGetUser() {
2022-04-01 20:05:34 +00:00
if (getAuth() == null) return
2021-11-12 16:55:54 +00:00
ioSafe {
getUser()
}
}
private fun checkToken(): Boolean {
return unixTime > getKey(
accountId,
ANILIST_UNIXTIME_KEY, 0L
)!!
2021-11-12 16:55:54 +00:00
}
2021-11-07 22:10:19 +00:00
2022-04-01 20:05:34 +00:00
private suspend fun getDataAboutId(id: Int): AniListTitleHolder? {
2021-11-07 22:10:19 +00:00
val q =
"""query (${'$'}id: Int = $id) { # Define which variables will be used in the query (id)
Media (id: ${'$'}id, type: ANIME) { # Insert our variables into the query arguments (id) (type: ANIME is hard-coded in the query)
id
episodes
isFavourite
mediaListEntry {
progress
status
score (format: POINT_10)
}
title {
english
romaji
}
}
}"""
2022-04-01 20:05:34 +00:00
val data = postApi(q, true)
val d = mapper.readValue<GetDataRoot>(data ?: return null)
2022-04-02 22:06:35 +00:00
val main = d.data?.Media
if (main?.mediaListEntry != null) {
2022-04-01 20:05:34 +00:00
return AniListTitleHolder(
title = main.title,
id = id,
isFavourite = main.isFavourite,
progress = main.mediaListEntry.progress,
episodes = main.episodes,
score = main.mediaListEntry.score,
type = fromIntToAnimeStatus(aniListStatusString.indexOf(main.mediaListEntry.status)),
)
} else {
return AniListTitleHolder(
2022-04-02 22:06:35 +00:00
title = main?.title,
2022-04-01 20:05:34 +00:00
id = id,
2022-04-02 22:06:35 +00:00
isFavourite = main?.isFavourite,
2022-04-01 20:05:34 +00:00
progress = 0,
2022-04-02 22:06:35 +00:00
episodes = main?.episodes,
2022-04-01 20:05:34 +00:00
score = 0,
type = AniListStatusType.None,
)
2021-11-07 22:10:19 +00:00
}
2022-04-01 20:05:34 +00:00
2021-11-07 22:10:19 +00:00
}
2022-04-01 20:05:34 +00:00
private fun getAuth(): String? {
return getKey(
accountId,
ANILIST_TOKEN_KEY
)
}
private suspend fun postApi(q: String, cache: Boolean = false): String? {
return if (!checkToken()) {
app.post(
"https://graphql.anilist.co/",
headers = mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return null),
if (cache) "Cache-Control" to "max-stale=$maxStale" else "Cache-Control" to "no-cache"
),
cacheTime = 0,
data = mapOf("query" to q),//(if (vars == null) mapOf("query" to q) else mapOf("query" to q, "variables" to vars))
timeout = 5 // REASONABLE TIMEOUT
).text.replace("\\/", "/")
} else {
null
2021-11-12 16:55:54 +00:00
}
}
data class MediaRecommendation(
@JsonProperty("id") val id: Int,
2022-04-02 22:06:35 +00:00
@JsonProperty("title") val title: Title?,
2021-11-12 16:55:54 +00:00
@JsonProperty("idMal") val idMal: Int?,
2022-04-02 22:06:35 +00:00
@JsonProperty("coverImage") val coverImage: CoverImage?,
2021-11-12 16:55:54 +00:00
@JsonProperty("averageScore") val averageScore: Int?
)
2021-11-07 22:10:19 +00:00
data class FullAnilistList(
2022-04-02 22:06:35 +00:00
@JsonProperty("data") val data: Data?
2021-11-07 22:10:19 +00:00
)
data class CompletedAt(
@JsonProperty("year") val year: Int,
@JsonProperty("month") val month: Int,
@JsonProperty("day") val day: Int
)
data class StartedAt(
@JsonProperty("year") val year: String?,
@JsonProperty("month") val month: String?,
@JsonProperty("day") val day: String?
)
data class Title(
@JsonProperty("english") val english: String?,
@JsonProperty("romaji") val romaji: String?
)
data class CoverImage(
2022-04-02 22:06:35 +00:00
@JsonProperty("medium") val medium: String?,
2021-11-07 22:10:19 +00:00
@JsonProperty("large") val large: String?
)
data class Media(
@JsonProperty("id") val id: Int,
@JsonProperty("idMal") val idMal: Int?,
@JsonProperty("season") val season: String?,
@JsonProperty("seasonYear") val seasonYear: Int,
@JsonProperty("format") val format: String?,
//@JsonProperty("source") val source: String,
@JsonProperty("episodes") val episodes: Int,
@JsonProperty("title") val title: Title,
//@JsonProperty("description") val description: String,
@JsonProperty("coverImage") val coverImage: CoverImage,
@JsonProperty("synonyms") val synonyms: List<String>,
@JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?,
)
data class Entries(
@JsonProperty("status") val status: String?,
@JsonProperty("completedAt") val completedAt: CompletedAt,
@JsonProperty("startedAt") val startedAt: StartedAt,
@JsonProperty("updatedAt") val updatedAt: Int,
@JsonProperty("progress") val progress: Int,
@JsonProperty("score") val score: Int,
@JsonProperty("private") val private: Boolean,
@JsonProperty("media") val media: Media
)
data class Lists(
@JsonProperty("status") val status: String?,
@JsonProperty("entries") val entries: List<Entries>
)
data class MediaListCollection(
@JsonProperty("lists") val lists: List<Lists>
)
data class Data(
@JsonProperty("MediaListCollection") val MediaListCollection: MediaListCollection
)
fun getAnilistListCached(): Array<Lists>? {
2021-11-07 22:10:19 +00:00
return getKey(ANILIST_CACHED_LIST) as? Array<Lists>
}
suspend fun getAnilistAnimeListSmart(): Array<Lists>? {
2022-04-01 20:05:34 +00:00
if (getAuth() == null) return null
2021-11-07 22:10:19 +00:00
if (checkToken()) return null
return if (getKey(ANILIST_SHOULD_UPDATE_LIST, true) == true) {
val list = getFullAnilistList()?.data?.MediaListCollection?.lists?.toTypedArray()
if (list != null) {
setKey(ANILIST_CACHED_LIST, list)
setKey(ANILIST_SHOULD_UPDATE_LIST, false)
}
list
} else {
getAnilistListCached()
}
}
private suspend fun getFullAnilistList(): FullAnilistList? {
2022-04-01 20:05:34 +00:00
var userID: Int? = null
/** WARNING ASSUMES ONE USER! **/
getKeys(ANILIST_USER_KEY)?.forEach { key ->
getKey<AniListUser>(key, null)?.let {
userID = it.id
2021-11-07 22:10:19 +00:00
}
2022-04-01 20:05:34 +00:00
}
2021-11-07 22:10:19 +00:00
2022-04-01 20:05:34 +00:00
val fixedUserID = userID ?: return null
val mediaType = "ANIME"
2021-11-07 22:10:19 +00:00
2022-04-01 20:05:34 +00:00
val query = """
2021-11-07 22:10:19 +00:00
query (${'$'}userID: Int = $fixedUserID, ${'$'}MEDIA: MediaType = $mediaType) {
MediaListCollection (userId: ${'$'}userID, type: ${'$'}MEDIA) {
lists {
status
entries
{
status
completedAt { year month day }
startedAt { year month day }
updatedAt
progress
score
private
media
{
id
idMal
season
seasonYear
format
episodes
chapters
title
{
english
romaji
}
coverImage { medium }
synonyms
nextAiringEpisode {
timeUntilAiring
episode
}
}
}
}
}
}
"""
2022-04-01 20:05:34 +00:00
val text = postApi(query)
return text?.toKotlinObject()
2021-11-07 22:10:19 +00:00
}
suspend fun toggleLike(id: Int): Boolean {
2021-11-07 22:10:19 +00:00
val q = """mutation (${'$'}animeId: Int = $id) {
ToggleFavourite (animeId: ${'$'}animeId) {
anime {
nodes {
id
title {
romaji
}
}
}
}
}"""
2022-04-01 20:05:34 +00:00
val data = postApi(q)
2021-11-07 22:10:19 +00:00
return data != ""
}
private suspend fun postDataAboutId(
2022-01-14 18:14:24 +00:00
id: Int,
type: AniListStatusType,
score: Int?,
progress: Int?
): Boolean {
2022-04-01 20:05:34 +00:00
val q =
"""mutation (${'$'}id: Int = $id, ${'$'}status: MediaListStatus = ${
aniListStatusString[maxOf(
0,
type.value
)]
}, ${if (score != null) "${'$'}scoreRaw: Int = ${score * 10}" else ""} , ${if (progress != null) "${'$'}progress: Int = $progress" else ""}) {
2021-11-07 22:10:19 +00:00
SaveMediaListEntry (mediaId: ${'$'}id, status: ${'$'}status, scoreRaw: ${'$'}scoreRaw, progress: ${'$'}progress) {
id
status
progress
score
}
}"""
2022-04-01 20:05:34 +00:00
val data = postApi(q)
return data != ""
2021-11-07 22:10:19 +00:00
}
private suspend fun getUser(setSettings: Boolean = true): AniListUser? {
2021-11-07 22:10:19 +00:00
val q = """
{
Viewer {
id
name
avatar {
large
}
favourites {
anime {
nodes {
id
}
}
}
}
}"""
2022-04-01 20:05:34 +00:00
val data = postApi(q)
if (data == "") return null
val userData = mapper.readValue<AniListRoot>(data ?: return null)
2022-04-02 22:06:35 +00:00
val u = userData.data?.Viewer
2022-04-01 20:05:34 +00:00
val user = AniListUser(
2022-04-02 22:06:35 +00:00
u?.id,
u?.name,
u?.avatar?.large,
2022-04-01 20:05:34 +00:00
)
if (setSettings) {
setKey(accountId, ANILIST_USER_KEY, user)
registerAccount()
2021-11-07 22:10:19 +00:00
}
2022-04-01 20:05:34 +00:00
/* // TODO FIX FAVS
for(i in u.favourites.anime.nodes) {
println("FFAV:" + i.id)
}*/
return user
2021-11-07 22:10:19 +00:00
}
suspend fun getAllSeasons(id: Int): List<SeasonResponse?> {
2021-11-07 22:10:19 +00:00
val seasons = mutableListOf<SeasonResponse?>()
suspend fun getSeasonRecursive(id: Int) {
2021-11-07 22:10:19 +00:00
val season = getSeason(id)
2022-04-03 01:13:02 +00:00
seasons.add(season)
if (season.data?.Media?.format?.startsWith("TV") == true) {
season.data.Media.relations?.edges?.forEach {
if (it.node?.format != null) {
if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) {
getSeasonRecursive(it.node.id)
return@forEach
2021-11-07 22:10:19 +00:00
}
}
}
}
}
getSeasonRecursive(id)
return seasons.toList()
}
data class SeasonResponse(
2022-04-02 22:06:35 +00:00
@JsonProperty("data") val data: SeasonData?,
2021-11-07 22:10:19 +00:00
)
data class SeasonData(
2022-04-02 22:06:35 +00:00
@JsonProperty("Media") val Media: SeasonMedia?,
2021-11-07 22:10:19 +00:00
)
data class SeasonMedia(
2022-04-02 22:06:35 +00:00
@JsonProperty("id") val id: Int?,
2021-11-07 22:10:19 +00:00
@JsonProperty("idMal") val idMal: Int?,
@JsonProperty("format") val format: String?,
@JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?,
2022-04-02 22:06:35 +00:00
@JsonProperty("relations") val relations: SeasonEdges?,
@JsonProperty("coverImage") val coverImage: MediaCoverImage?,
@JsonProperty("duration") val duration: Int?,
@JsonProperty("episodes") val episodes: Int?,
@JsonProperty("genres") val genres: List<String>?,
@JsonProperty("synonyms") val synonyms: List<String>?,
@JsonProperty("averageScore") val averageScore: Int?,
@JsonProperty("isAdult") val isAdult: Boolean?,
@JsonProperty("trailer") val trailer: MediaTrailer?,
2022-04-03 01:13:02 +00:00
@JsonProperty("description") val description: String?,
)
2022-04-02 22:06:35 +00:00
data class MediaTrailer(
@JsonProperty("id") val id: String?,
@JsonProperty("site") val site: String?,
@JsonProperty("thumbnail") val thumbnail: String?,
)
data class MediaCoverImage(
@JsonProperty("extraLarge") val extraLarge: String?,
@JsonProperty("large") val large: String?,
@JsonProperty("medium") val medium: String?,
@JsonProperty("color") val color: String?,
2021-11-07 22:10:19 +00:00
)
data class SeasonNextAiringEpisode(
2022-04-02 22:06:35 +00:00
@JsonProperty("episode") val episode: Int?,
@JsonProperty("timeUntilAiring") val timeUntilAiring: Int?,
2021-11-07 22:10:19 +00:00
)
data class SeasonEdges(
2022-04-02 22:06:35 +00:00
@JsonProperty("edges") val edges: List<SeasonEdge>?,
2021-11-07 22:10:19 +00:00
)
data class SeasonEdge(
2022-04-02 22:06:35 +00:00
@JsonProperty("id") val id: Int?,
@JsonProperty("relationType") val relationType: String?,
@JsonProperty("node") val node: SeasonNode?,
2021-11-07 22:10:19 +00:00
)
data class AniListFavoritesMediaConnection(
@JsonProperty("nodes") val nodes: List<LikeNode>,
)
data class AniListFavourites(
@JsonProperty("anime") val anime: AniListFavoritesMediaConnection,
)
data class SeasonNode(
@JsonProperty("id") val id: Int,
@JsonProperty("format") val format: String?,
2022-04-02 22:06:35 +00:00
@JsonProperty("title") val title: Title?,
2021-11-07 22:10:19 +00:00
@JsonProperty("idMal") val idMal: Int?,
2022-04-02 22:06:35 +00:00
@JsonProperty("coverImage") val coverImage: CoverImage?,
2021-11-07 22:10:19 +00:00
@JsonProperty("averageScore") val averageScore: Int?
// @JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?,
)
data class AniListAvatar(
2022-04-02 22:06:35 +00:00
@JsonProperty("large") val large: String?,
2021-11-07 22:10:19 +00:00
)
data class AniListViewer(
2022-04-02 22:06:35 +00:00
@JsonProperty("id") val id: Int?,
@JsonProperty("name") val name: String?,
@JsonProperty("avatar") val avatar: AniListAvatar?,
@JsonProperty("favourites") val favourites: AniListFavourites?,
2021-11-07 22:10:19 +00:00
)
data class AniListData(
2022-04-02 22:06:35 +00:00
@JsonProperty("Viewer") val Viewer: AniListViewer?,
2021-11-07 22:10:19 +00:00
)
data class AniListRoot(
2022-04-02 22:06:35 +00:00
@JsonProperty("data") val data: AniListData?,
2021-11-07 22:10:19 +00:00
)
data class AniListUser(
2022-04-02 22:06:35 +00:00
@JsonProperty("id") val id: Int?,
@JsonProperty("name") val name: String?,
@JsonProperty("picture") val picture: String?,
2021-11-07 22:10:19 +00:00
)
data class LikeNode(
2022-04-02 22:06:35 +00:00
@JsonProperty("id") val id: Int?,
2021-11-07 22:10:19 +00:00
//@JsonProperty("idMal") public int idMal;
)
data class LikePageInfo(
2022-04-02 22:06:35 +00:00
@JsonProperty("total") val total: Int?,
@JsonProperty("currentPage") val currentPage: Int?,
@JsonProperty("lastPage") val lastPage: Int?,
@JsonProperty("perPage") val perPage: Int?,
@JsonProperty("hasNextPage") val hasNextPage: Boolean?,
2021-11-07 22:10:19 +00:00
)
data class LikeAnime(
2022-04-02 22:06:35 +00:00
@JsonProperty("nodes") val nodes: List<LikeNode>?,
@JsonProperty("pageInfo") val pageInfo: LikePageInfo?,
2021-11-07 22:10:19 +00:00
)
data class LikeFavourites(
2022-04-02 22:06:35 +00:00
@JsonProperty("anime") val anime: LikeAnime?,
2021-11-07 22:10:19 +00:00
)
data class LikeViewer(
2022-04-02 22:06:35 +00:00
@JsonProperty("favourites") val favourites: LikeFavourites?,
2021-11-07 22:10:19 +00:00
)
data class LikeData(
2022-04-02 22:06:35 +00:00
@JsonProperty("Viewer") val Viewer: LikeViewer?,
2021-11-07 22:10:19 +00:00
)
data class LikeRoot(
2022-04-02 22:06:35 +00:00
@JsonProperty("data") val data: LikeData?,
2021-11-07 22:10:19 +00:00
)
data class Recommendation(
2022-04-02 22:06:35 +00:00
@JsonProperty("title") val title: String?,
@JsonProperty("idMal") val idMal: Int?,
@JsonProperty("poster") val poster: String?,
2021-11-07 22:10:19 +00:00
@JsonProperty("averageScore") val averageScore: Int?
)
data class AniListTitleHolder(
2022-04-02 22:06:35 +00:00
@JsonProperty("title") val title: Title?,
@JsonProperty("isFavourite") val isFavourite: Boolean?,
@JsonProperty("id") val id: Int?,
@JsonProperty("progress") val progress: Int?,
@JsonProperty("episodes") val episodes: Int?,
@JsonProperty("score") val score: Int?,
@JsonProperty("type") val type: AniListStatusType?,
2021-11-07 22:10:19 +00:00
)
data class GetDataMediaListEntry(
2022-04-02 22:06:35 +00:00
@JsonProperty("progress") val progress: Int?,
@JsonProperty("status") val status: String?,
@JsonProperty("score") val score: Int?,
2021-11-07 22:10:19 +00:00
)
data class Nodes(
2022-04-02 22:06:35 +00:00
@JsonProperty("id") val id: Int?,
2021-11-07 22:10:19 +00:00
@JsonProperty("mediaRecommendation") val mediaRecommendation: MediaRecommendation?
)
data class GetDataMedia(
2022-04-02 22:06:35 +00:00
@JsonProperty("isFavourite") val isFavourite: Boolean?,
@JsonProperty("episodes") val episodes: Int?,
@JsonProperty("title") val title: Title?,
2021-11-07 22:10:19 +00:00
@JsonProperty("mediaListEntry") val mediaListEntry: GetDataMediaListEntry?
)
data class Recommendations(
2022-04-02 22:06:35 +00:00
@JsonProperty("nodes") val nodes: List<Nodes>?
2021-11-07 22:10:19 +00:00
)
data class GetDataData(
2022-04-02 22:06:35 +00:00
@JsonProperty("Media") val Media: GetDataMedia?,
2021-11-07 22:10:19 +00:00
)
data class GetDataRoot(
2022-04-02 22:06:35 +00:00
@JsonProperty("data") val data: GetDataData?,
2021-11-07 22:10:19 +00:00
)
data class GetSearchTitle(
2022-04-02 22:06:35 +00:00
@JsonProperty("romaji") val romaji: String?,
2021-11-07 22:10:19 +00:00
)
data class TrailerObject(
@JsonProperty("id") val id: String?,
@JsonProperty("thumbnail") val thumbnail: String?,
@JsonProperty("site") val site: String?,
)
data class GetSearchMedia(
@JsonProperty("id") val id: Int,
@JsonProperty("idMal") val idMal: Int?,
@JsonProperty("seasonYear") val seasonYear: Int,
@JsonProperty("title") val title: GetSearchTitle,
@JsonProperty("startDate") val startDate: StartedAt,
@JsonProperty("averageScore") val averageScore: Int?,
@JsonProperty("meanScore") val meanScore: Int?,
@JsonProperty("bannerImage") val bannerImage: String?,
@JsonProperty("trailer") val trailer: TrailerObject?,
@JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?,
@JsonProperty("recommendations") val recommendations: Recommendations?,
2022-04-02 22:06:35 +00:00
@JsonProperty("relations") val relations: SeasonEdges?
2021-11-07 22:10:19 +00:00
)
data class GetSearchPage(
2022-04-02 22:06:35 +00:00
@JsonProperty("Page") val Page: GetSearchData?,
2021-11-07 22:10:19 +00:00
)
data class GetSearchData(
2022-04-02 22:06:35 +00:00
@JsonProperty("media") val media: List<GetSearchMedia>?,
2021-11-07 22:10:19 +00:00
)
data class GetSearchRoot(
2022-04-02 22:06:35 +00:00
@JsonProperty("data") val data: GetSearchPage?,
2021-11-07 22:10:19 +00:00
)
}