diff --git a/SoraItalianStream/build.gradle.kts b/SoraItalianStream/build.gradle.kts index 8ed141f..0356d7f 100644 --- a/SoraItalianStream/build.gradle.kts +++ b/SoraItalianStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them description = "Provider che utilizza tmdb. Non tutti i links sono funzionanti" - authors = listOf("Adippe") + authors = listOf("Adippe", "Forthe") /** * Status int as the following: diff --git a/SoraItalianStream/src/main/kotlin/SoraItalianExtractor.kt b/SoraItalianStream/src/main/kotlin/SoraItalianExtractor.kt index 9d898ae..8d24f0d 100644 --- a/SoraItalianStream/src/main/kotlin/SoraItalianExtractor.kt +++ b/SoraItalianStream/src/main/kotlin/SoraItalianExtractor.kt @@ -4,6 +4,7 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ShortLink.unshorten import com.lagradost.nicehttp.Requests object SoraItalianExtractor : SoraItalianStream() { @@ -24,7 +25,7 @@ object SoraItalianExtractor : SoraItalianStream() { subtitleCallback, callback ) - println("LINK DI Guardare " + fixUrl(source.attr("data-link"))) + println("LINK DI Guardare " + fixUrl(source.attr("data-link"))) } } @@ -42,11 +43,12 @@ object SoraItalianExtractor : SoraItalianStream() { "story" to id!! ) ).document.selectFirst("h2>a")?.attr("href") ?: return + val document = app.get(url).document document.select("div.tab-content > div").mapIndexed { seasonData, data -> data.select("li").mapIndexed { epNum, epData -> if (season == seasonData + 1 && episode == epNum + 1) { - epData.select("div.mirrors > a").map { + epData.select("div.mirrors > a.mr").map { loadExtractor( fixUrl(it.attr("data-link")), "$/", @@ -79,121 +81,105 @@ object SoraItalianExtractor : SoraItalianStream() { .filter { it != filmpertuttiUrl } links.apmap { val doc = app.get(it).document - if (id == doc.selectFirst(" div.rating > p > a")?.attr("href") + if (id == doc.selectFirst("div.rating > p > a") //check if corresponds to the imdb id + ?.attr("href") ?.substringAfterLast("/") ) { if (type == "tv") { val seasonData = doc.select("div.accordion-item").filter { a -> - a.selectFirst("#season > ul > li.s_title > span")!!.text().isNotEmpty() + a.selectFirst("#season > ul > li.s_title > span")!! + .text() + .isNotEmpty() }.find { - season == it.selectFirst("#season > ul > li.s_title > span")!!.text() + season == it.selectFirst("#season > ul > li.s_title > span")!! + .text() .toInt() } val episodeData = seasonData?.select("div.episode-wrap")?.find { - episode == it.selectFirst("li.season-no")!!.text().substringAfter("x") + episode == it.selectFirst("li.season-no")!!.text() + .substringAfter("x") + .substringBefore(" ") .filter { it.isDigit() }.toIntOrNull() } episodeData?.select("#links > div > div > table > tbody:nth-child(2) > tr") ?.map { + val unshortenLink = unshorten(it.selectFirst("a")?.attr("href") ?: "") loadExtractor( - it.selectFirst("a")!!.attr("href") ?: "", + unshortenLink, filmpertuttiUrl, subtitleCallback, callback ) - println("FIlmpetutti " + it.selectFirst("a")!!.attr("href") ?: "") - } - } else { - val urls0 = doc.select("div.embed-player") - if (urls0.isNotEmpty()) { - urls0.map { - loadExtractor( - it.attr("data-id"), - filmpertuttiUrl, - subtitleCallback, - callback - - ) - println("LINK DI FIlmpetutti " + it.attr("data-id")) - } - } else { - doc.select("#info > ul > li ").mapNotNull { - val link = it.selectFirst("a")?.attr("href") ?: "" - loadExtractor( - ShortLink.unshorten(link).trim().replace("/v/", "/e/") - .replace("/f/", "/e/"), - "$/", - subtitleCallback, - callback - ) - println("LINK DI FIlmpetutti " + it.selectFirst("a")?.attr("href")) + println("LINK DI Filmpertutti Series $unshortenLink") } + } else { //movie + doc.select("#info > ul > li").mapNotNull { + val unshortenLink = unshorten(it.selectFirst("a")?.attr("href") ?: "") + loadExtractor( + unshortenLink, + "$/", + subtitleCallback, + callback + ) + println("LINK DI Filmpertutti Movies $unshortenLink") } } } } } - suspend fun invoAltadefinizione( - id: String? = null, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val url = app.get( - "$altadefinizioneUrl/index.php?story=$id&do=search&subaction=search" - ).document.selectFirst("div.cover_kapsul > a")?.attr("href") ?: return - val document = app.get(url).document - document.select("ul.host>a").map { - loadExtractor( - fixUrl(it.attr("data-link")), - altadefinizioneUrl, - subtitleCallback, - callback - ) - println("LINK DI altadefinizione " + fixUrl(it.attr("data-link"))) - } - - } - suspend fun invoCb01( title: String?, year: Int?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val res = app.get("$cb01Url/search/$title $year/feed").text + + val res = app.get("$cb01Url/search/$title%20$year/feed").text val links = Regex("(.*)").findAll(res).map { it.groupValues.last() }.toList() .filter { it != cb01Url && it != "$cb01Url/" } if (links.size != 1) return + links.apmap { val doc = app.get(it).document - doc.select("tr > td > a").mapNotNull { + doc.select("tr > td > a[href*='stayonline.pro']").mapNotNull { val link = it.selectFirst("a")?.attr("href") ?: "" - val url = ShortLink.unshorten(link).trim().replace("/v/", "/e/") - .replace("/f/", "/e/") - val processedUrl = if (url.contains("mixdrop.club")){ - fixUrl(app.get(url).document.selectFirst("iframe")?.attr("src")?:"") - } - else{url} + val idPost = link.substringAfter("/l/") + .substringBefore("/") //https://stayonline.pro/l/abcdef/ -> abcdef + val doc2 = app.post( + "https://stayonline.pro/ajax/linkView.php", + data = mapOf("id" to idPost) + ).text + var url2 = + doc2.substringAfter("\"value\": \"").substringBefore("\"") + .replace("\\", "") //bypass stayonline link + + if (url2.contains("mixdrop.club")) //https://mixdrop.club/f/lllllllll/2/abcdefghilmn.mp4 (fake mp4 url) -> https://mixdrop.ch/e/lllllllll + url2 = url2.replace("mixdrop.club", "mixdrop.ch") + .substringBeforeLast("/") + .substringBeforeLast("/") + .replace("/f/", "/e/") + else + url2 = unshorten(url2) loadExtractor( - processedUrl, + url2, "$/", subtitleCallback, callback ) - println("LINK DI CB01 " + url) + println("LINK DI CB01 $url2") } } } + suspend fun invoAnimeWorld( malId: String?, title: String?, episode: Int?, - subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { val pagedata = app.get("$animeworldUrl/search?keyword=$title").document @@ -202,9 +188,9 @@ object SoraItalianExtractor : SoraItalianStream() { fixUrl(it.select("a.name").firstOrNull()?.attr("href") ?: "", animeworldUrl) }.apmap { val document = app.get(it).document - val malID = document.select("#mal-button").attr("href") + val pageMalId = document.select("#mal-button").attr("href") .split('/').last().toString() - if (malId == malID) { + if (malId == pageMalId) { val servers = document.select(".widget.servers") servers.select(".server[data-name=\"9\"] .episode > a").toList() .filter { it.attr("data-episode-num").toIntOrNull()?.equals(episode) ?: false } @@ -229,12 +215,12 @@ object SoraItalianExtractor : SoraItalianStream() { ExtractorLink( "AnimeWorld", nameData, - url?:"", + url ?: "", referer = animeworldUrl, quality = Qualities.Unknown.value ) ) - println("LINK DI Animeworld " + url) + println("LINK DI Animeworld $url") } } } @@ -245,7 +231,6 @@ object SoraItalianExtractor : SoraItalianStream() { title: String?, episode: Int?, year: Int?, - subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { val response = @@ -266,7 +251,6 @@ object SoraItalianExtractor : SoraItalianStream() { val streamUrl = parseJson(app.get(episodeUrl).text).url callback.invoke( - ExtractorLink( name, AnimeName, @@ -276,16 +260,17 @@ object SoraItalianExtractor : SoraItalianStream() { isM3u8 = streamUrl.contains(".m3u8"), ) ) - } - else { - val seasonid = response.seasons.sortedBy { it.episodeStart }.last { it.episodeStart < episode!!} + println("LINK DI aniplay $streamUrl") + } else { + val seasonid = response.seasons.sortedBy { it.episodeStart } + .last { it.episodeStart < episode!! } val episodesData = tryParseJson>( app.get( "$url/season/${seasonid.id}" ).text ) - val episodeData = episodesData?.find { it.number == episode.toString() }?.id + val episodeData = episodesData?.find { it.number == episode.toString() }?.id if (episodeData != null) { val streamUrl = parseJson(app.get("$aniplayUrl/api/episode/${episodeData}").text).url @@ -299,7 +284,7 @@ object SoraItalianExtractor : SoraItalianStream() { isM3u8 = streamUrl.contains(".m3u8"), ) ) - println("LINK DI aniplay " + streamUrl) + println("LINK DI aniplay $streamUrl") } } } @@ -310,10 +295,11 @@ object SoraItalianExtractor : SoraItalianStream() { malId: String?, title: String?, episode: Int?, - subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val document = app.get("$animesaturnUrl/animelist?search=${title?.replace("-"," ")}").document + print("Animesaturn started to scrape") + val document = + app.get("$animesaturnUrl/animelist?search=${title?.replace("-", " ")}").document val links = document.select("div.item-archivio").map { it.select("a.badge-archivio").first()!!.attr("href") } @@ -324,53 +310,51 @@ object SoraItalianExtractor : SoraItalianStream() { } else { "AnimeSaturn SUB" } - var malID : String? = null + var malID: String? - response.select("[rel=\"noopener noreferrer\"]").forEach { - if(it.attr("href").contains("myanimelist")) - malID = it.attr("href").removeSuffix("/").split('/').last() - if (malId == malID){ - val link = response.select("a.bottone-ep").find { it.text().split(" ")[1] == episode.toString() }?.attr("href") - if (link != null) { - val page = app.get(link).document - val episodeLink = page.select("div.card-body > a[href]").find { it1 -> - it1.attr("href").contains("watch?") - }?.attr("href") ?: throw ErrorLoadingException("No link Found") + response.select("a[href*=myanimelist]").map { + malID = it.attr("href").substringBeforeLast("/") + .substringAfterLast("/") //https://myanimelist.net/anime/19/ -> 19 + if (malId == malID) { + val link = response.select("a.bottone-ep") + .find { it.text().substringAfter("Episodio ") == episode.toString() } + ?.attr("href") + if (link?.isBlank() == false) { //links exists + val page = app.get(link).document + val episodeLink = page.select("div.card-body > a[href*=watch]").attr("href") + ?: throw ErrorLoadingException("No link Found") - val episodePage = app.get(episodeLink).document - val episodeUrl: String? - var isM3U8 = false + val episodePage = app.get(episodeLink).document + var episodeUrl: String? + episodePage.select("video.afterglow > source") + .also { // Old player + episodeUrl = it.first()?.attr("src") + } + ?: run { //new player + episodeUrl = Regex("\"(https[A-z0-9\\/\\:\\.]*\\.m3u8)\",").find( + episodePage.select("script").find { + it.toString().contains("jwplayer('player_hls').setup({") + }.toString() + )?.value + } - if (episodePage.select("video.afterglow > source").isNotEmpty()) // Old player - episodeUrl = - episodePage.select("video.afterglow > source").first()!!.attr("src") - else { // New player - val script = episodePage.select("script").find { - it.toString().contains("jwplayer('player_hls').setup({") - }!!.toString() - episodeUrl = script.split(" ") - .find { it.contains(".m3u8") and !it.contains(".replace") }!! - .replace("\"", "").replace(",", "") - isM3U8 = true + callback.invoke( + ExtractorLink( + name, + AnimeName, + episodeUrl!!, + isM3u8 = episodeUrl!!.contains(".m3u8"), + referer = "https://www.animesaturn.io/", //Some servers need the old host as referer, and the new ones accept it too + quality = Qualities.Unknown.value + ) + ) + println("LINK DI animesaturn $episodeUrl") } - callback.invoke( - ExtractorLink( - name, - AnimeName, - episodeUrl!!, - isM3u8 = isM3U8, - referer = "https://www.animesaturn.io/", //Some servers need the old host as referer, and the new ones accept it too - quality = Qualities.Unknown.value - ) - ) - println("LINK DI animesaturn " + episodeUrl) } - } - } - } + } } } @@ -397,6 +381,5 @@ fun fixUrl(url: String, domain: String): String { return "$domain/$url" } - } diff --git a/SoraItalianStream/src/main/kotlin/SoraItalianStream.kt b/SoraItalianStream/src/main/kotlin/SoraItalianStream.kt index 0a258e7..461fad7 100644 --- a/SoraItalianStream/src/main/kotlin/SoraItalianStream.kt +++ b/SoraItalianStream/src/main/kotlin/SoraItalianStream.kt @@ -5,7 +5,6 @@ import com.lagradost.SoraItalianExtractor.invoGuardare import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.metaproviders.TmdbProvider -import com.lagradost.SoraItalianExtractor.invoAltadefinizione import com.lagradost.SoraItalianExtractor.invoAniPlay import com.lagradost.SoraItalianExtractor.invoAnimeSaturn import com.lagradost.SoraItalianExtractor.invoAnimeWorld @@ -37,8 +36,9 @@ open class SoraItalianStream : TmdbProvider() { private const val apiKey = "71f37e6dff3b879fa4656f19547c418c" // PLEASE DON'T STEAL const val guardaserieUrl = "https://guardaserie.app" const val filmpertuttiUrl = "https://www.filmpertutti.skin" - const val altadefinizioneUrl = "https://altadefinizione01.autos" - const val cb01Url = "https://cb01.delivery" + + // const val altadefinizioneUrl = "https://altadefinizione01.autos" + const val cb01Url = "https://cb01.tours/" const val animeworldUrl = "https://www.animeworld.tv" const val aniplayUrl = "https://aniplay.it" const val animesaturnUrl = "https://www.animesaturn.in" @@ -69,6 +69,12 @@ open class SoraItalianStream : TmdbProvider() { return api.chunked(4).map { base64Decode(it) }.reversed().joinToString("") } + fun containsJapaneseCharacters(str: String?): Boolean { //sometimes the it-It translation of names gives the japanese name of the anime + val japaneseCharactersRegex = + Regex("[\\u3000-\\u303f\\u3040-\\u309f\\u30a0-\\u30ff\\uff00-\\uff9f\\u4e00-\\u9faf\\u3400-\\u4dbf]") + return str?.let { japaneseCharactersRegex.containsMatchIn(it) } == true + } + } @@ -119,22 +125,13 @@ open class SoraItalianStream : TmdbProvider() { } - override suspend fun search(query: String): List { - val searchResponse = mutableListOf() - - val mainResponse = app.get( + override suspend fun search(query: String): List? { + return app.get( "$tmdbAPI/search/multi?api_key=$apiKey&language=it-IT&query=$query&page=1&include_adult=false" - ).parsedSafe()?.results?.mapNotNull { media -> - media.toSearchResponse() - } - if (mainResponse?.isNotEmpty() == true) searchResponse.addAll(mainResponse) - -// val animeResponse = -// app.get("$mainServerAPI/search/anime/$query?_data=routes/search/anime/\$animeKeyword") -// .parsedSafe()?.searchResults?.results?.mapNotNull { anime -> anime.toSearchResponse() } -// if (animeResponse?.isNotEmpty() == true) searchResponse.addAll(animeResponse) - - return searchResponse + ) + .parsedSafe()?.results?.mapNotNull { media -> + media.toSearchResponse() + } } @@ -143,16 +140,16 @@ open class SoraItalianStream : TmdbProvider() { val type = getType(data.type) - val typename = when(type){ - TvType.TvSeries-> "tv" - TvType.Movie -> "movie" - else -> "" - } + val typename = data.type - val res = + var res = app.get("$tmdbAPI/$typename/${data.id}?api_key=$apiKey&language=it-IT&append_to_response=external_ids,credits,recommendations,videos") .parsedSafe() ?: throw ErrorLoadingException("Invalid Json Response") - + if (containsJapaneseCharacters(res.name)) { + res = + app.get("$tmdbAPI/$typename/${data.id}?api_key=$apiKey&language=en-US&append_to_response=external_ids,credits,recommendations,videos") + .parsedSafe() ?: throw ErrorLoadingException("Invalid Json Response") + } val title = res.name ?: res.title ?: return null val orgTitle = res.originalName ?: res.originalTitle ?: return null @@ -175,81 +172,64 @@ open class SoraItalianStream : TmdbProvider() { res.videos?.results?.map { "https://www.youtube.com/watch?v=${it.key}" } ?.randomOrNull() - if (res.genres?.map { it.id }?.contains(16) == true && type == TvType.TvSeries) { + val isAnime = res.genres?.map { it.id }?.contains(16) == true - val episodes = mutableListOf() - res.seasons?.filter { it.seasonNumber != 0L }?.apmap { season -> - val seasonData = app.get("$tmdbAPI/${data.type}/${data.id}/season/${season.seasonNumber}?api_key=$apiKey&language=it-IT") - .parsedSafe()?.episodes - val seasonID = if (season.seasonNumber!! > 1 && seasonData?.isNotEmpty() == true && seasonData?.first()?.episodeNumber != 1){ 1 } else { season.seasonNumber }.toInt() - seasonData?.map { eps -> - episodes.add(Episode( - LinkData( - data.id, - res.externalIds?.imdbId, - data.type, - seasonID, - eps.episodeNumber, - title = title, - year = year, - orgTitle = orgTitle, - isAnime = true - ).toJson(), - name = eps.name, - season = eps.seasonNumber, - episode = eps.episodeNumber, - posterUrl = getImageUrl(eps.stillPath), - rating = eps.voteAverage?.times(10)?.roundToInt(), - description = eps.overview - ).apply { - this.addDate(eps.airDate) - }) - } - } - return newTvSeriesLoadResponse( + return if (type == TvType.Movie) { //can be a movie or a anime movie + newMovieLoadResponse( title, url, - TvType.TvSeries, - episodes + TvType.Movie, + LinkData( + data.id, + res.externalIds?.imdbId, + data.type, + title = title, + year = year, + orgTitle = orgTitle, + isAnime = isAnime + ).toJson(), ) { this.posterUrl = getOriImageUrl(res.backdropPath) this.year = year this.plot = res.overview this.tags = res.genres?.mapNotNull { it.name } - this.showStatus = getStatus(res.status) this.recommendations = recommendations this.actors = actors addTrailer(trailer) } - - } - return if (type == TvType.TvSeries) { + } else { //can be anime series or tv series val episodes = mutableListOf() - res.seasons?.filter { it.seasonNumber != 0L }?.apmap { season -> - app.get("$tmdbAPI/${data.type}/${data.id}/season/${season.seasonNumber}?api_key=$apiKey&language=it-IT") - .parsedSafe()?.episodes?.map { eps -> - episodes.add(Episode( - LinkData( - data.id, - res.externalIds?.imdbId, - data.type, - eps.seasonNumber, - eps.episodeNumber, - title = title, - year = season.airDate?.split("-")?.first()?.toIntOrNull(), - orgTitle = orgTitle, - isAnime = false - ).toJson(), - name = eps.name, - season = eps.seasonNumber, - episode = eps.episodeNumber, - posterUrl = getImageUrl(eps.stillPath), - rating = eps.voteAverage?.times(10)?.roundToInt(), - description = eps.overview - ).apply { - this.addDate(eps.airDate) - }) - } + var seasonNum = 0 + res.seasons?.filter { it.seasonNumber != 0 }?.map { season -> + val seasonData = + app.get("$tmdbAPI/${data.type}/${data.id}/season/${season.seasonNumber}?api_key=$apiKey&language=it-IT") + .parsedSafe()?.episodes + if (seasonData?.first()?.episodeNumber == 1) + seasonNum += 1 + + seasonData?.map { eps -> + episodes.add(Episode( + LinkData( + data.id, + res.externalIds?.imdbId, + data.type, + seasonNum, + eps.episodeNumber, + title = title, + year = year ?: season.airDate?.split("-")?.first()?.toIntOrNull(), + orgTitle = orgTitle, + isAnime = isAnime + ).toJson(), + name = eps.name, + season = seasonNum, + episode = eps.episodeNumber, + posterUrl = getImageUrl(eps.stillPath), + rating = eps.voteAverage?.times(10)?.roundToInt(), + description = eps.overview + ).apply { + this.addDate(eps.airDate) + }) + } } newTvSeriesLoadResponse( title, @@ -267,30 +247,6 @@ open class SoraItalianStream : TmdbProvider() { addTrailer(trailer) } } - else { - newMovieLoadResponse( - title, - url, - TvType.Movie, - LinkData( - data.id, - res.externalIds?.imdbId, - data.type, - title = title, - year = year, - orgTitle = orgTitle, - isAnime = res.genres?.map { it.id }?.contains(16)?:false - ).toJson(), - ) { - this.posterUrl = getOriImageUrl(res.backdropPath) - this.year = year - this.plot = res.overview - this.tags = res.genres?.mapNotNull { it.name } - this.recommendations = recommendations - this.actors = actors - addTrailer(trailer) - } - } } override suspend fun loadLinks( @@ -304,28 +260,60 @@ open class SoraItalianStream : TmdbProvider() { val malID = app.get("$tmdb2mal/?id=${res.id}&s=${res.season}").text.trim() argamap( { - if (res.isAnime ) invoAnimeWorld(malID, res.title, res.episode, subtitleCallback, callback) + if (res.isAnime) invoAnimeWorld( + malID, + res.title, + res.episode, + callback + ) }, { - if (res.isAnime) invoAniPlay(malID, res.title, res.episode, res.year, subtitleCallback, callback) + if (res.isAnime) invoAniPlay( + malID, + res.title, + res.episode, + res.year, + callback + ) }, { - if (res.isAnime) invoAnimeSaturn(malID, res.title, res.episode, subtitleCallback, callback) + if (res.isAnime) invoAnimeSaturn( + malID, + res.title, + res.episode, + callback + ) }, { - invoGuardare(res.imdbId, subtitleCallback, callback) + if (res.type == "movie") invoGuardare( + res.imdbId, + subtitleCallback, + callback + ) //just movies/anime movie }, { - if (malID == "") invoGuardaserie(res.imdbId, res.season, res.episode, subtitleCallback, callback) + if (res.type == "tv") invoGuardaserie( //has tv series and anime + res.imdbId, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { - if (!res.isAnime) invoFilmpertutti(res.imdbId,res.title, res.type, res.season, res.episode, res.year, subtitleCallback, callback) + invoFilmpertutti( //has tv series, film and some anime + res.imdbId, + res.title, + res.type, + res.season, + res.episode, + res.year, + subtitleCallback, + callback + ) }, { - if (!res.isAnime) invoAltadefinizione(res.imdbId, subtitleCallback, callback) - }, - { - if (!res.isAnime) invoCb01(res.title, res.year, subtitleCallback, callback) + if (res.type == "movie") invoCb01(res.title, res.year, subtitleCallback, callback) } ) @@ -335,7 +323,7 @@ open class SoraItalianStream : TmdbProvider() { private data class LinkData( val id: Int? = null, val imdbId: String? = null, - val type: String? = null, + val type: String? = null, //movie or tv val season: Int? = null, val episode: Int? = null, val aniId: String? = null, @@ -343,7 +331,7 @@ open class SoraItalianStream : TmdbProvider() { val title: String? = null, val year: Int? = null, val orgTitle: String? = null, - val isAnime : Boolean + val isAnime: Boolean ) data class Data( @@ -415,7 +403,7 @@ open class SoraItalianStream : TmdbProvider() { ) - data class MovieDetails ( + data class MovieDetails( val adult: Boolean? = null, @JsonProperty("first_air_date") val tvDate: String? = null, @JsonProperty("release_date") val movieDate: String? = null, @@ -442,19 +430,19 @@ open class SoraItalianStream : TmdbProvider() { @JsonProperty("external_ids") val externalIds: ExternalIds? = null ) - data class Recommendations ( + data class Recommendations( @JsonProperty("results") val results: List? = null, ) - data class Credits ( + data class Credits( val cast: List? = null, ) - data class ExternalIds ( + data class ExternalIds( @JsonProperty("imdb_id") val imdbId: String? = null ) - data class Videos ( + data class Videos( val results: List? = null, ) @@ -462,19 +450,19 @@ open class SoraItalianStream : TmdbProvider() { @JsonProperty("key") val key: String? = null, ) - data class Genre ( + data class Genre( val id: Long? = null, val name: String? = null ) - data class Season ( + data class Season( @JsonProperty("air_date") val airDate: String? = null, - @JsonProperty("episode_count") val episodeCount: Long? = null, + @JsonProperty("episode_count") val episodeCount: Int? = null, val id: Long? = null, val name: String? = null, val overview: String? = null, @JsonProperty("poster_path") val posterPath: String? = null, - @JsonProperty("season_number") val seasonNumber: Long? = null + @JsonProperty("season_number") val seasonNumber: Int? = null ) data class AnimeWorldJson( @@ -482,29 +470,35 @@ open class SoraItalianStream : TmdbProvider() { @JsonProperty("name") val name: String, @JsonProperty("target") val target: String, ) + data class AniPlayApiSearchResult( @JsonProperty("id") val id: Int, @JsonProperty("listWebsites") val websites: List ) + data class AniPlayWebsites( - @JsonProperty("url") val url: String? = null, - @JsonProperty("listWebsiteId") val websitesId: Int? = null + @JsonProperty("url") val url: String? = null, + @JsonProperty("listWebsiteId") val websitesId: Int? = null ) + data class AniplayApiAnime( @JsonProperty("episodes") val episodes: List, @JsonProperty("seasons") val seasons: List?, @JsonProperty("title") val title: String? ) + data class AniplayApiEpisode( @JsonProperty("id") val id: Int, @JsonProperty("title") val title: String?, @JsonProperty("episodeNumber") val number: String, ) + data class AniplayApiSeason( @JsonProperty("id") val id: Int, @JsonProperty("name") val name: String, @JsonProperty("episodeStart") val episodeStart: Int ) + data class AniPlayApiEpisodeUrl( @JsonProperty("videoUrl") val url: String )