From 7ab9dc9a613eb958d30f12e9388238db1756d978 Mon Sep 17 00:00:00 2001 From: mklive <23458013+mklive@users.noreply.github.com> Date: Sat, 13 Jan 2024 11:36:10 +0100 Subject: [PATCH] fix(all/yomiroll): Fetch anime status from AniList (#2728) Co-authored-by: Samfun75 <38332931+Samfun75@users.noreply.github.com> Co-authored-by: jmir1 --- src/all/kamyroll/build.gradle | 2 +- .../animeextension/all/kamyroll/DataModel.kt | 38 ++-- .../animeextension/all/kamyroll/Yomiroll.kt | 205 ++++++++++++------ 3 files changed, 162 insertions(+), 83 deletions(-) diff --git a/src/all/kamyroll/build.gradle b/src/all/kamyroll/build.gradle index 2da771cb8..79df5b8d2 100644 --- a/src/all/kamyroll/build.gradle +++ b/src/all/kamyroll/build.gradle @@ -8,7 +8,7 @@ ext { extName = 'Yomiroll' pkgNameSuffix = 'all.kamyroll' extClass = '.Yomiroll' - extVersionCode = 26 + extVersionCode = 27 libVersion = '13' } diff --git a/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/DataModel.kt b/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/DataModel.kt index 256638a16..d894ba36b 100644 --- a/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/DataModel.kt +++ b/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/DataModel.kt @@ -59,30 +59,26 @@ data class Anime( val genres: ArrayList? = null, val series_metadata: Metadata? = null, @SerialName("movie_listing_metadata") - val movie_metadata: MovieMeta? = null, + val movie_metadata: Metadata? = null, val content_provider: String? = null, + val audio_locale: String? = null, + val audio_locales: ArrayList? = null, + val subtitle_locales: ArrayList? = null, + val maturity_ratings: ArrayList? = null, + val is_dubbed: Boolean? = null, + val is_subbed: Boolean? = null, ) { @Serializable data class Metadata( val maturity_ratings: ArrayList, - val is_simulcast: Boolean, - val audio_locales: ArrayList, + val is_simulcast: Boolean? = null, + val audio_locales: ArrayList? = null, val subtitle_locales: ArrayList, val is_dubbed: Boolean, val is_subbed: Boolean, @SerialName("tenant_categories") val genres: ArrayList? = null, ) - - @Serializable - data class MovieMeta( - val is_dubbed: Boolean, - val is_subbed: Boolean, - val maturity_ratings: ArrayList, - val subtitle_locales: ArrayList, - @SerialName("tenant_categories") - val genres: ArrayList? = null, - ) } @Serializable @@ -172,6 +168,22 @@ data class Subtitle( val url: String, ) +@Serializable +data class AnilistResult( + val data: AniData, +) { + @Serializable + data class AniData( + @SerialName("Media") + val media: Media? = null, + ) + + @Serializable + data class Media( + val status: String, + ) +} + fun List.thirdLast(): T? { if (size < 3) return null return this[size - 3] diff --git a/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/Yomiroll.kt b/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/Yomiroll.kt index dbb27656b..e603b5739 100644 --- a/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/Yomiroll.kt +++ b/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/Yomiroll.kt @@ -15,15 +15,19 @@ import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.MainScope import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject +import okhttp3.FormBody import okhttp3.Request import okhttp3.Response import rx.Observable @@ -53,6 +57,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { private val json: Json by injectLazy() + private val mainScope by lazy { MainScope() } + private val preferences: SharedPreferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } @@ -65,6 +71,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { super.client.newBuilder().addInterceptor(tokenInterceptor).build() } + private val noTokenClient = super.client + // ============================== Popular =============================== override fun popularAnimeRequest(page: Int): Request { @@ -73,7 +81,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { } override fun popularAnimeParse(response: Response): AnimesPage { - val parsed = json.decodeFromString(response.body.string()) + val parsed = json.decodeFromString(response.use { it.body.string() }) val animeList = parsed.data.mapNotNull { it.toSAnimeOrNull() } val position = response.request.url.queryParameter("start")?.toIntOrNull() ?: 0 return AnimesPage(animeList, position + 36 < parsed.total) @@ -103,7 +111,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { } override fun searchAnimeParse(response: Response): AnimesPage { - val bod = response.body.string() + val bod = response.use { it.body.string() } val total: Int val items = if (response.request.url.encodedPath.contains("search")) { @@ -125,6 +133,43 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { // =========================== Anime Details ============================ + // Function to fetch anime status using AniList GraphQL API ispired by OppaiStream.kt + private fun fetchStatusByTitle(title: String): Int { + val query = """ + query { + Media(search: "$title", isAdult: false, type: ANIME) { + id + idMal + title { + romaji + native + english + } + status + } + } + """.trimIndent() + + val requestBody = FormBody.Builder() + .add("query", query) + .build() + + val response = noTokenClient.newCall( + POST("https://graphql.anilist.co", body = requestBody), + ).execute().use { it.body.string() } + + val responseParsed = json.decodeFromString(response) + + return when (responseParsed.data.media?.status) { + "FINISHED" -> SAnime.COMPLETED + "RELEASING" -> SAnime.ONGOING + "NOT_YET_RELEASED" -> SAnime.LICENSED + "CANCELLED" -> SAnime.CANCELLED + "HIATUS" -> SAnime.ON_HIATUS + else -> SAnime.UNKNOWN + } + } + override fun fetchAnimeDetails(anime: SAnime): Observable { val mediaId = json.decodeFromString(anime.url) val resp = client.newCall( @@ -133,17 +178,10 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { } else { GET("$crApiUrl/cms/movie_listings/${mediaId.id}?locale=en-US") }, - ).execute() - val info = json.decodeFromString(resp.body.string()) + ).execute().use { it.body.string() } + val info = json.decodeFromString(resp) return Observable.just( - anime.apply { - author = info.data.first().content_provider - status = SAnime.COMPLETED - if (genre.isNullOrBlank()) { - genre = - info.data.first().genres?.joinToString { gen -> gen.replaceFirstChar { it.uppercase() } } - } - }, + info.data.first().toSAnimeOrNull(anime) ?: anime, ) } @@ -161,7 +199,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { } override fun episodeListParse(response: Response): List { - val seasons = json.decodeFromString(response.body.string()) + val seasons = json.decodeFromString(response.use { it.body.string() }) val series = response.request.url.encodedPath.contains("series/") val chunkSize = Runtime.getRuntime().availableProcessors() return if (series) { @@ -176,7 +214,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { seasons.data.mapIndexed { index, movie -> SEpisode.create().apply { url = EpisodeData(listOf(Pair(movie.id, ""))).toJsonString() - name = "Movie" + name = "Movie ${index + 1}" episode_number = (index + 1).toFloat() date_upload = movie.date?.let(::parseDate) ?: 0L } @@ -185,10 +223,9 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { } private fun getEpisodes(seasonData: SeasonResult.Season): List { - val episodeResp = + val body = client.newCall(GET("$crApiUrl/cms/seasons/${seasonData.id}/episodes")) - .execute() - val body = episodeResp.body.string() + .execute().use { it.body.string() } val episodes = json.decodeFromString(body) return episodes.data.sortedBy { it.episode_number }.mapNotNull EpisodeMap@{ ep -> @@ -246,8 +283,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() { private fun extractVideo(media: Pair): List