diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index b5b9a14a..ccfdee88 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.konan.properties.Properties // use an integer for version numbers -version = 215 +version = 216 android { defaultConfig { diff --git a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt index 78d52668..b11a7d31 100644 --- a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt +++ b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt @@ -263,8 +263,7 @@ open class Streamruby : ExtractorApi() { } else { response.document.selectFirst("script:containsData(sources:)")?.data() } - val m3u8 = - Regex("file:\\s*\"(.*?m3u8.*?)\"").find(script ?: return)?.groupValues?.getOrNull(1) + val m3u8 = Regex("file:\\s*\"(.*?m3u8.*?)\"").find(script ?: return)?.groupValues?.getOrNull(1) M3u8Helper.generateM3u8( name, m3u8 ?: return, @@ -466,7 +465,7 @@ class Animefever : Filesim() { override var mainUrl = "https://animefever.fun" } -class Multimovies : Filesim() { +class Multimovies : Ridoo() { override val name = "Multimovies" override var mainUrl = "https://multimovies.cloud" } diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 25c6f27f..f848b653 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -492,7 +492,8 @@ object SoraExtractor : SoraStream() { val req = app.get(source, referer = "$host/") val server = getBaseUrl(req.url) val script = req.text.substringAfter("wc0 = '").substringBefore("'") - val video = tryParseJson>(base64Decode(script))?.get("file") + val video = + tryParseJson>(base64Decode(script))?.get("file") M3u8Helper.generateM3u8( "Voe", video ?: return@apmap, @@ -528,8 +529,8 @@ object SoraExtractor : SoraStream() { } else { doc.select("table.table-striped tbody tr") .find { it.text().contains("Episode $episode") }?.select("td")?.map { - it.select("a").attr("href") to it.select("a").text() - } + it.select("a").attr("href") to it.select("a").text() + } } ?: return delay(4000) @@ -781,9 +782,11 @@ object SoraExtractor : SoraStream() { val mediaId = app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return - app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/sources", headers = mapOf( - "X-Requested-With" to "XMLHttpRequest" - )).parsedSafe()?.result?.apmap { + app.get( + "$vidsrctoAPI/ajax/embed/episode/$mediaId/sources", headers = mapOf( + "X-Requested-With" to "XMLHttpRequest" + ) + ).parsedSafe()?.result?.apmap { val encUrl = app.get("$vidsrctoAPI/ajax/embed/source/${it.id}") .parsedSafe()?.result?.url loadExtractor( @@ -905,21 +908,68 @@ object SoraExtractor : SoraStream() { if (season == null) TvType.AnimeMovie else TvType.Anime ) - argamap({ - invokeAnimetosho(malId, season, episode, subtitleCallback, callback) - }, { - invokeAniwatch(malId, episode, subtitleCallback, callback) - }, { - if (season != null) invokeCrunchyroll( - aniId, - malId, - epsTitle, - season, - episode, + val malsync = app.get("$malsyncAPI/mal/anime/${malId ?: return}") + .parsedSafe()?.sites + + val zoroIds = malsync?.zoro?.keys?.map { it } + val aniwaveId = malsync?.nineAnime?.firstNotNullOf { it.value["url"] } + + argamap( + { + invokeAnimetosho(malId, season, episode, subtitleCallback, callback) + }, + { + invokeAniwatch(zoroIds, episode, subtitleCallback, callback) + }, + { + invokeAniwave(aniwaveId, episode, subtitleCallback, callback) + }, + { + if (season != null) invokeCrunchyroll( + aniId, + malId, + epsTitle, + season, + episode, + subtitleCallback, + callback + ) + } + ) + } + + private suspend fun invokeAniwave( + url: String? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val res = app.get(url ?: return).document + val id = res.select("div#watch-main").attr("data-id") + val episodeId = + app.get("$aniwaveAPI/ajax/episode/list/$id?vrf=${AniwaveUtils.encodeVrf(id)}") + .parsedSafe()?.asJsoup() + ?.selectFirst("ul.ep-range li a[data-num=${episode ?: 1}]")?.attr("data-ids") + ?: return + val servers = + app.get("$aniwaveAPI/ajax/server/list/$episodeId?vrf=${AniwaveUtils.encodeVrf(episodeId)}") + .parsedSafe()?.asJsoup() + ?.select("div.servers > div[data-type!=sub] ul li") ?: return + + servers.apmap { + val linkId = it.attr("data-link-id") + val iframe = + app.get("$aniwaveAPI/ajax/server/$linkId?vrf=${AniwaveUtils.encodeVrf(linkId)}") + .parsedSafe()?.result?.decrypt() + val audio = if (it.attr("data-cmid").endsWith("softsub")) "Raw" else "English Dub" + loadCustomExtractor( + "${it.text()} [$audio]", + iframe ?: return@apmap, + "$aniwaveAPI/", subtitleCallback, - callback + callback, ) - }) + } } private suspend fun invokeAnimetosho( @@ -968,7 +1018,7 @@ object SoraExtractor : SoraStream() { } private suspend fun invokeAniwatch( - malId: Int? = null, + animeIds: List? = null, episode: Int? = null, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit @@ -976,9 +1026,7 @@ object SoraExtractor : SoraStream() { val headers = mapOf( "X-Requested-With" to "XMLHttpRequest", ) - val animeId = app.get("$malsyncAPI/mal/anime/${malId ?: return}") - .parsedSafe()?.sites?.zoro?.keys?.map { it } - animeId?.apmap { id -> + animeIds?.apmap { id -> val episodeId = app.get( "$aniwatchAPI/ajax/v2/episode/list/${id ?: return@apmap}", headers = headers @@ -992,12 +1040,12 @@ object SoraExtractor : SoraStream() { headers = headers ).parsedSafe()?.html?.let { Jsoup.parse(it) } ?.select("div.item.server-item")?.map { - Triple( - it.text(), - it.attr("data-id"), - it.attr("data-type"), - ) - } + Triple( + it.text(), + it.attr("data-id"), + it.attr("data-type"), + ) + } servers?.apmap servers@{ server -> val iframe = app.get( @@ -1914,8 +1962,8 @@ object SoraExtractor : SoraStream() { timeout = 120L ).document.selectFirst("script:containsData(downloaddomain)")?.data() ?.substringAfter("\"downloaddomain\":\"")?.substringBefore("\",")?.let { - "$it/0:" - } + "$it/0:" + } fixUrl(path, worker ?: return@apmap null) } else { fixUrl(path, apiUrl) @@ -2372,14 +2420,14 @@ object SoraExtractor : SoraStream() { """postid\":\"""" ).substringBefore("""\"""") } ?: return - val url = if(season == null) { + val url = if (season == null) { "$ridomoviesAPI/core/api/movies/$slug/videos" } else { "$ridomoviesAPI/core/api/episodes/$slug/videos" } app.get(url).parsedSafe()?.data?.apmap { link -> val iframe = Jsoup.parse(link.url ?: return@apmap).select("iframe").attr("data-src") - if(iframe.startsWith("https://closeload.top")) { + if (iframe.startsWith("https://closeload.top")) { val unpacked = getAndUnpack( app.get( diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt index c4378663..fd388fbc 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt @@ -1,31 +1,56 @@ package com.hexated import com.fasterxml.jackson.annotation.JsonProperty +import org.jsoup.Jsoup +import org.jsoup.nodes.Document data class CrunchyrollAccessToken( - val accessToken: String? = null, - val tokenType: String? = null, - val bucket: String? = null, - val policy: String? = null, - val signature: String? = null, - val key_pair_id: String? = null, + val accessToken: String? = null, + val tokenType: String? = null, + val bucket: String? = null, + val policy: String? = null, + val signature: String? = null, + val key_pair_id: String? = null, ) data class FDMovieIFrame( - val link: String, - val quality: String, - val size: String, - val type: String, + val link: String, + val quality: String, + val size: String, + val type: String, ) data class AniIds(var id: Int? = null, var idMal: Int? = null) data class TmdbDate( - val today: String, - val nextWeek: String, + val today: String, + val nextWeek: String, ) -data class AniMedia(@JsonProperty("id") var id: Int? = null, @JsonProperty("idMal") var idMal: Int? = null) +data class AniwaveResponse( + val result: String +) { + fun asJsoup(): Document { + return Jsoup.parse(result) + } +} + +data class AniwaveServer( + val result: Result +) { + data class Result( + val url: String + ) { + fun decrypt(): String { + return AniwaveUtils.decodeVrf(url) + } + } +} + +data class AniMedia( + @JsonProperty("id") var id: Int? = null, + @JsonProperty("idMal") var idMal: Int? = null +) data class AniPage(@JsonProperty("media") var media: java.util.ArrayList = arrayListOf()) @@ -34,155 +59,155 @@ data class AniData(@JsonProperty("Page") var Page: AniPage? = AniPage()) data class AniSearch(@JsonProperty("data") var data: AniData? = AniData()) data class GpressSources( - @JsonProperty("src") val src: String, - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: Int? = null, - @JsonProperty("max") val max: String, + @JsonProperty("src") val src: String, + @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: Int? = null, + @JsonProperty("max") val max: String, ) data class UHDBackupUrl( - @JsonProperty("url") val url: String? = null, + @JsonProperty("url") val url: String? = null, ) data class ResponseHash( - @JsonProperty("embed_url") val embed_url: String, - @JsonProperty("key") val key: String? = null, - @JsonProperty("type") val type: String? = null, + @JsonProperty("embed_url") val embed_url: String, + @JsonProperty("key") val key: String? = null, + @JsonProperty("type") val type: String? = null, ) data class KisskhSources( - @JsonProperty("Video") val video: String?, - @JsonProperty("ThirdParty") val thirdParty: String?, + @JsonProperty("Video") val video: String?, + @JsonProperty("ThirdParty") val thirdParty: String?, ) data class KisskhSubtitle( - @JsonProperty("src") val src: String?, - @JsonProperty("label") val label: String?, + @JsonProperty("src") val src: String?, + @JsonProperty("label") val label: String?, ) data class KisskhEpisodes( - @JsonProperty("id") val id: Int?, - @JsonProperty("number") val number: Int?, + @JsonProperty("id") val id: Int?, + @JsonProperty("number") val number: Int?, ) data class KisskhDetail( - @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf(), + @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf(), ) data class KisskhResults( - @JsonProperty("id") val id: Int?, - @JsonProperty("title") val title: String?, + @JsonProperty("id") val id: Int?, + @JsonProperty("title") val title: String?, ) data class DriveBotLink( - @JsonProperty("url") val url: String? = null, + @JsonProperty("url") val url: String? = null, ) data class DirectDl( - @JsonProperty("download_url") val download_url: String? = null, + @JsonProperty("download_url") val download_url: String? = null, ) data class Safelink( - @JsonProperty("safelink") val safelink: String? = null, + @JsonProperty("safelink") val safelink: String? = null, ) data class FDAds( - @JsonProperty("linkr") val linkr: String? = null, + @JsonProperty("linkr") val linkr: String? = null, ) data class ZShowEmbed( - @JsonProperty("m") val meta: String? = null, + @JsonProperty("m") val meta: String? = null, ) data class WatchsomuchTorrents( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("movieId") val movieId: Int? = null, - @JsonProperty("season") val season: Int? = null, - @JsonProperty("episode") val episode: Int? = null, + @JsonProperty("id") val id: Int? = null, + @JsonProperty("movieId") val movieId: Int? = null, + @JsonProperty("season") val season: Int? = null, + @JsonProperty("episode") val episode: Int? = null, ) data class WatchsomuchMovies( - @JsonProperty("torrents") val torrents: ArrayList? = arrayListOf(), + @JsonProperty("torrents") val torrents: ArrayList? = arrayListOf(), ) data class WatchsomuchResponses( - @JsonProperty("movie") val movie: WatchsomuchMovies? = null, + @JsonProperty("movie") val movie: WatchsomuchMovies? = null, ) data class WatchsomuchSubtitles( - @JsonProperty("url") val url: String? = null, - @JsonProperty("label") val label: String? = null, + @JsonProperty("url") val url: String? = null, + @JsonProperty("label") val label: String? = null, ) data class WatchsomuchSubResponses( - @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), + @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), ) data class IndexMedia( - @JsonProperty("id") val id: String? = null, - @JsonProperty("driveId") val driveId: String? = null, - @JsonProperty("mimeType") val mimeType: String? = null, - @JsonProperty("size") val size: String? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("modifiedTime") val modifiedTime: String? = null, + @JsonProperty("id") val id: String? = null, + @JsonProperty("driveId") val driveId: String? = null, + @JsonProperty("mimeType") val mimeType: String? = null, + @JsonProperty("size") val size: String? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("modifiedTime") val modifiedTime: String? = null, ) data class IndexData( - @JsonProperty("files") val files: ArrayList? = arrayListOf(), + @JsonProperty("files") val files: ArrayList? = arrayListOf(), ) data class IndexSearch( - @JsonProperty("data") val data: IndexData? = null, + @JsonProperty("data") val data: IndexData? = null, ) data class JikanExternal( - @JsonProperty("name") val name: String? = null, - @JsonProperty("url") val url: String? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("url") val url: String? = null, ) data class JikanData( - @JsonProperty("title") val title: String? = null, - @JsonProperty("external") val external: ArrayList? = arrayListOf(), + @JsonProperty("title") val title: String? = null, + @JsonProperty("external") val external: ArrayList? = arrayListOf(), ) data class JikanResponse( - @JsonProperty("data") val data: JikanData? = null, + @JsonProperty("data") val data: JikanData? = null, ) data class CinemaTvSubtitles( - @JsonProperty("language") val language: String? = null, - @JsonProperty("file") val file: Any? = null, + @JsonProperty("language") val language: String? = null, + @JsonProperty("file") val file: Any? = null, ) data class CinemaTvResponse( - @JsonProperty("streams") val streams: HashMap? = null, - @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), + @JsonProperty("streams") val streams: HashMap? = null, + @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), ) data class VidsrctoResult( - @JsonProperty("id") val id: String? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("url") val url: String? = null, + @JsonProperty("id") val id: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("url") val url: String? = null, ) data class VidsrctoResponse( - @JsonProperty("result") val result: VidsrctoResult? = null, + @JsonProperty("result") val result: VidsrctoResult? = null, ) data class VidsrctoSources( - @JsonProperty("result") val result: ArrayList? = arrayListOf(), + @JsonProperty("result") val result: ArrayList? = arrayListOf(), ) data class VidsrctoSubtitles( - @JsonProperty("label") val label: String? = null, - @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: String? = null, + @JsonProperty("file") val file: String? = null, ) data class AnilistExternalLinks( - @JsonProperty("id") var id: Int? = null, - @JsonProperty("site") var site: String? = null, - @JsonProperty("url") var url: String? = null, - @JsonProperty("type") var type: String? = null, + @JsonProperty("id") var id: Int? = null, + @JsonProperty("site") var site: String? = null, + @JsonProperty("url") var url: String? = null, + @JsonProperty("type") var type: String? = null, ) data class AnilistMedia(@JsonProperty("externalLinks") var externalLinks: ArrayList = arrayListOf()) @@ -192,244 +217,248 @@ data class AnilistData(@JsonProperty("Media") var Media: AnilistMedia? = Anilist data class AnilistResponses(@JsonProperty("data") var data: AnilistData? = AnilistData()) data class CrunchyrollToken( - @JsonProperty("access_token") val accessToken: String? = null, - @JsonProperty("token_type") val tokenType: String? = null, - @JsonProperty("cms") val cms: Cms? = null, + @JsonProperty("access_token") val accessToken: String? = null, + @JsonProperty("token_type") val tokenType: String? = null, + @JsonProperty("cms") val cms: Cms? = null, ) { data class Cms( - @JsonProperty("bucket") var bucket: String? = null, - @JsonProperty("policy") var policy: String? = null, - @JsonProperty("signature") var signature: String? = null, - @JsonProperty("key_pair_id") var key_pair_id: String? = null, + @JsonProperty("bucket") var bucket: String? = null, + @JsonProperty("policy") var policy: String? = null, + @JsonProperty("signature") var signature: String? = null, + @JsonProperty("key_pair_id") var key_pair_id: String? = null, ) } data class CrunchyrollVersions( - @JsonProperty("audio_locale") val audio_locale: String? = null, - @JsonProperty("guid") val guid: String? = null, + @JsonProperty("audio_locale") val audio_locale: String? = null, + @JsonProperty("guid") val guid: String? = null, ) data class CrunchyrollData( - @JsonProperty("id") val id: String? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("slug_title") val slug_title: String? = null, - @JsonProperty("season_number") val season_number: Int? = null, - @JsonProperty("episode_number") val episode_number: Int? = null, - @JsonProperty("versions") val versions: ArrayList? = null, - @JsonProperty("streams_link") val streams_link: String? = null, + @JsonProperty("id") val id: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("slug_title") val slug_title: String? = null, + @JsonProperty("season_number") val season_number: Int? = null, + @JsonProperty("episode_number") val episode_number: Int? = null, + @JsonProperty("versions") val versions: ArrayList? = null, + @JsonProperty("streams_link") val streams_link: String? = null, ) data class CrunchyrollResponses( - @JsonProperty("data") val data: ArrayList? = arrayListOf(), + @JsonProperty("data") val data: ArrayList? = arrayListOf(), ) data class CrunchyrollSourcesResponses( - @JsonProperty("streams") val streams: Streams? = Streams(), - @JsonProperty("subtitles") val subtitles: HashMap>? = hashMapOf(), + @JsonProperty("streams") val streams: Streams? = Streams(), + @JsonProperty("subtitles") val subtitles: HashMap>? = hashMapOf(), ) { data class Streams( - @JsonProperty("adaptive_hls") val adaptive_hls: HashMap>? = hashMapOf(), - @JsonProperty("vo_adaptive_hls") val vo_adaptive_hls: HashMap>? = hashMapOf(), + @JsonProperty("adaptive_hls") val adaptive_hls: HashMap>? = hashMapOf(), + @JsonProperty("vo_adaptive_hls") val vo_adaptive_hls: HashMap>? = hashMapOf(), ) } data class MALSyncSites( - @JsonProperty("Zoro") val zoro: HashMap>? = hashMapOf(), + @JsonProperty("Zoro") val zoro: HashMap>? = hashMapOf(), + @JsonProperty("9anime") val nineAnime: HashMap>? = hashMapOf(), ) data class MALSyncResponses( - @JsonProperty("Sites") val sites: MALSyncSites? = null, + @JsonProperty("Sites") val sites: MALSyncSites? = null, ) data class AniwatchResponses( - @JsonProperty("html") val html: String? = null, - @JsonProperty("link") val link: String? = null, + @JsonProperty("html") val html: String? = null, + @JsonProperty("link") val link: String? = null, ) data class MalSyncRes( - @JsonProperty("Sites") val Sites: Map>>? = null, + @JsonProperty("Sites") val Sites: Map>>? = null, ) data class GokuData( - @JsonProperty("link") val link: String? = null, + @JsonProperty("link") val link: String? = null, ) data class GokuServer( - @JsonProperty("data") val data: GokuData? = GokuData(), + @JsonProperty("data") val data: GokuData? = GokuData(), ) data class AllMovielandEpisodeFolder( - @JsonProperty("title") val title: String? = null, - @JsonProperty("id") val id: String? = null, - @JsonProperty("file") val file: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("id") val id: String? = null, + @JsonProperty("file") val file: String? = null, ) data class AllMovielandSeasonFolder( - @JsonProperty("episode") val episode: String? = null, - @JsonProperty("id") val id: String? = null, - @JsonProperty("folder") val folder: ArrayList? = arrayListOf(), + @JsonProperty("episode") val episode: String? = null, + @JsonProperty("id") val id: String? = null, + @JsonProperty("folder") val folder: ArrayList? = arrayListOf(), ) data class AllMovielandServer( - @JsonProperty("title") val title: String? = null, - @JsonProperty("id") val id: String? = null, - @JsonProperty("file") val file: String? = null, - @JsonProperty("folder") val folder: ArrayList? = arrayListOf(), + @JsonProperty("title") val title: String? = null, + @JsonProperty("id") val id: String? = null, + @JsonProperty("file") val file: String? = null, + @JsonProperty("folder") val folder: ArrayList? = arrayListOf(), ) data class AllMovielandPlaylist( - @JsonProperty("file") val file: String? = null, - @JsonProperty("key") val key: String? = null, - @JsonProperty("href") val href: String? = null, + @JsonProperty("file") val file: String? = null, + @JsonProperty("key") val key: String? = null, + @JsonProperty("href") val href: String? = null, ) data class DumpMedia( - @JsonProperty("id") val id: String? = null, - @JsonProperty("domainType") val domainType: Int? = null, - @JsonProperty("name") val name: String? = null, - @JsonProperty("releaseTime") val releaseTime: String? = null, + @JsonProperty("id") val id: String? = null, + @JsonProperty("domainType") val domainType: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("releaseTime") val releaseTime: String? = null, ) data class DumpQuickSearchData( - @JsonProperty("searchResults") val searchResults: ArrayList? = arrayListOf(), + @JsonProperty("searchResults") val searchResults: ArrayList? = arrayListOf(), ) data class SubtitlingList( - @JsonProperty("languageAbbr") val languageAbbr: String? = null, - @JsonProperty("language") val language: String? = null, - @JsonProperty("subtitlingUrl") val subtitlingUrl: String? = null, + @JsonProperty("languageAbbr") val languageAbbr: String? = null, + @JsonProperty("language") val language: String? = null, + @JsonProperty("subtitlingUrl") val subtitlingUrl: String? = null, ) data class DefinitionList( - @JsonProperty("code") val code: String? = null, - @JsonProperty("description") val description: String? = null, + @JsonProperty("code") val code: String? = null, + @JsonProperty("description") val description: String? = null, ) data class EpisodeVo( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("seriesNo") val seriesNo: Int? = null, - @JsonProperty("definitionList") val definitionList: ArrayList? = arrayListOf(), - @JsonProperty("subtitlingList") val subtitlingList: ArrayList? = arrayListOf(), + @JsonProperty("id") val id: Int? = null, + @JsonProperty("seriesNo") val seriesNo: Int? = null, + @JsonProperty("definitionList") val definitionList: ArrayList? = arrayListOf(), + @JsonProperty("subtitlingList") val subtitlingList: ArrayList? = arrayListOf(), ) data class DumpMediaDetail( - @JsonProperty("episodeVo") val episodeVo: ArrayList? = arrayListOf(), + @JsonProperty("episodeVo") val episodeVo: ArrayList? = arrayListOf(), ) data class EMovieServer( - @JsonProperty("value") val value: String? = null, + @JsonProperty("value") val value: String? = null, ) data class EMovieSources( - @JsonProperty("file") val file: String? = null, + @JsonProperty("file") val file: String? = null, ) data class EMovieTraks( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, + @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: String? = null, ) data class ShowflixResultsMovies( - @JsonProperty("movieName") val movieName: String? = null, - @JsonProperty("streamwish") val streamwish: String? = null, - @JsonProperty("filelions") val filelions: String? = null, - @JsonProperty("streamruby") val streamruby: String? = null, + @JsonProperty("movieName") val movieName: String? = null, + @JsonProperty("streamwish") val streamwish: String? = null, + @JsonProperty("filelions") val filelions: String? = null, + @JsonProperty("streamruby") val streamruby: String? = null, ) data class ShowflixResultsSeries( - @JsonProperty("seriesName") val seriesName: String? = null, - @JsonProperty("streamwish") val streamwish: HashMap>? = hashMapOf(), - @JsonProperty("filelions") val filelions: HashMap>? = hashMapOf(), - @JsonProperty("streamruby") val streamruby: HashMap>? = hashMapOf(), + @JsonProperty("seriesName") val seriesName: String? = null, + @JsonProperty("streamwish") val streamwish: HashMap>? = hashMapOf(), + @JsonProperty("filelions") val filelions: HashMap>? = hashMapOf(), + @JsonProperty("streamruby") val streamruby: HashMap>? = hashMapOf(), ) data class ShowflixSearchMovies( - @JsonProperty("results") val resultsMovies: ArrayList? = arrayListOf(), + @JsonProperty("results") val resultsMovies: ArrayList? = arrayListOf(), ) data class ShowflixSearchSeries( - @JsonProperty("results") val resultsSeries: ArrayList? = arrayListOf(), + @JsonProperty("results") val resultsSeries: ArrayList? = arrayListOf(), ) data class SFMoviesSeriess( - @JsonProperty("title") var title: String? = null, - @JsonProperty("svideos") var svideos: String? = null, + @JsonProperty("title") var title: String? = null, + @JsonProperty("svideos") var svideos: String? = null, ) data class SFMoviesAttributes( - @JsonProperty("title") var title: String? = null, - @JsonProperty("video") var video: String? = null, - @JsonProperty("releaseDate") var releaseDate: String? = null, - @JsonProperty("seriess") var seriess: ArrayList>? = arrayListOf(), - @JsonProperty("contentId") var contentId: String? = null, + @JsonProperty("title") var title: String? = null, + @JsonProperty("video") var video: String? = null, + @JsonProperty("releaseDate") var releaseDate: String? = null, + @JsonProperty("seriess") var seriess: ArrayList>? = arrayListOf(), + @JsonProperty("contentId") var contentId: String? = null, ) -data class SFMoviesData(@JsonProperty("id") var id: Int? = null, @JsonProperty("attributes") var attributes: SFMoviesAttributes? = SFMoviesAttributes()) +data class SFMoviesData( + @JsonProperty("id") var id: Int? = null, + @JsonProperty("attributes") var attributes: SFMoviesAttributes? = SFMoviesAttributes() +) data class SFMoviesSearch( - @JsonProperty("data") var data: ArrayList? = arrayListOf(), + @JsonProperty("data") var data: ArrayList? = arrayListOf(), ) data class RidoContentable( - @JsonProperty("imdbId") var imdbId: String? = null, - @JsonProperty("tmdbId") var tmdbId: Int? = null, + @JsonProperty("imdbId") var imdbId: String? = null, + @JsonProperty("tmdbId") var tmdbId: Int? = null, ) data class RidoItems( - @JsonProperty("slug") var slug: String? = null, - @JsonProperty("contentable") var contentable: RidoContentable? = null, + @JsonProperty("slug") var slug: String? = null, + @JsonProperty("contentable") var contentable: RidoContentable? = null, ) data class RidoData( - @JsonProperty("url") var url: String? = null, - @JsonProperty("items") var items: ArrayList? = arrayListOf(), + @JsonProperty("url") var url: String? = null, + @JsonProperty("items") var items: ArrayList? = arrayListOf(), ) data class RidoResponses( - @JsonProperty("data") var data: ArrayList? = arrayListOf(), + @JsonProperty("data") var data: ArrayList? = arrayListOf(), ) data class RidoSearch( - @JsonProperty("data") var data: RidoData? = null, + @JsonProperty("data") var data: RidoData? = null, ) data class SmashySources( - @JsonProperty("sourceUrls") var sourceUrls: ArrayList? = arrayListOf(), - @JsonProperty("subtitleUrls") var subtitleUrls: String? = null, + @JsonProperty("sourceUrls") var sourceUrls: ArrayList? = arrayListOf(), + @JsonProperty("subtitleUrls") var subtitleUrls: String? = null, ) data class SmashyDSources( - @JsonProperty("sourceUrls") var sourceUrls: ArrayList? = arrayListOf(), + @JsonProperty("sourceUrls") var sourceUrls: ArrayList? = arrayListOf(), ) data class SmashyDSourcesUrls( - @JsonProperty("file") var file: String? = null, - @JsonProperty("title") var title: String? = null, + @JsonProperty("file") var file: String? = null, + @JsonProperty("title") var title: String? = null, ) data class AoneroomResponse( - @JsonProperty("data") val data: Data? = null, + @JsonProperty("data") val data: Data? = null, ) { data class Data( - @JsonProperty("items") val items: ArrayList? = arrayListOf(), - @JsonProperty("list") val list: ArrayList? = arrayListOf(), + @JsonProperty("items") val items: ArrayList? = arrayListOf(), + @JsonProperty("list") val list: ArrayList? = arrayListOf(), ) { data class Items( - @JsonProperty("subjectId") val subjectId: String? = null, - @JsonProperty("title") val title: String? = null, - @JsonProperty("releaseDate") val releaseDate: String? = null, + @JsonProperty("subjectId") val subjectId: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("releaseDate") val releaseDate: String? = null, ) data class List( - @JsonProperty("resourceLink") val resourceLink: String? = null, - @JsonProperty("extCaptions") val extCaptions: ArrayList? = arrayListOf(), - @JsonProperty("se") val se: Int? = null, - @JsonProperty("ep") val ep: Int? = null, - @JsonProperty("resolution") val resolution: Int? = null, + @JsonProperty("resourceLink") val resourceLink: String? = null, + @JsonProperty("extCaptions") val extCaptions: ArrayList? = arrayListOf(), + @JsonProperty("se") val se: Int? = null, + @JsonProperty("ep") val ep: Int? = null, + @JsonProperty("resolution") val resolution: Int? = null, ) { data class ExtCaptions( - @JsonProperty("lanName") val lanName: String? = null, - @JsonProperty("url") val url: String? = null, + @JsonProperty("lanName") val lanName: String? = null, + @JsonProperty("url") val url: String? = null, ) } } diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index bbcac45e..64e37094 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -91,6 +91,7 @@ open class SoraStream : TmdbProvider() { const val filmxyAPI = "https://www.filmxy.vip" const val kimcartoonAPI = "https://kimcartoon.li" const val aniwatchAPI = "https://aniwatch.to" + const val aniwaveAPI = "https://aniwave.to" const val crunchyrollAPI = "https://beta-api.crunchyroll.com" const val kissKhAPI = "https://kisskh.co" const val lingAPI = "https://ling-online.net" @@ -185,8 +186,8 @@ open class SoraStream : TmdbProvider() { val type = if (request.data.contains("/movie")) "movie" else "tv" val home = app.get("${request.data}$adultQuery&page=$page") .parsedSafe()?.results?.mapNotNull { media -> - media.toSearchResponse(type) - } ?: throw ErrorLoadingException("Invalid Json reponse") + media.toSearchResponse(type) + } ?: throw ErrorLoadingException("Invalid Json reponse") return newHomePageResponse(request.name, home) } @@ -205,8 +206,8 @@ open class SoraStream : TmdbProvider() { override suspend fun search(query: String): List? { return app.get("$tmdbAPI/search/multi?api_key=$apiKey&language=en-US&query=$query&page=1&include_adult=${settingsForProvider.enableAdult}") .parsedSafe()?.results?.mapNotNull { media -> - media.toSearchResponse() - } + media.toSearchResponse() + } } override suspend fun load(url: String): LoadResponse? { @@ -257,39 +258,39 @@ open class SoraStream : TmdbProvider() { val episodes = res.seasons?.mapNotNull { season -> app.get("$tmdbAPI/${data.type}/${data.id}/season/${season.seasonNumber}?api_key=$apiKey") .parsedSafe()?.episodes?.map { eps -> - Episode( - LinkData( - data.id, - res.external_ids?.imdb_id, - res.external_ids?.tvdb_id, - data.type, - eps.seasonNumber, - eps.episodeNumber, - title = title, - year = season.airDate?.split("-")?.first()?.toIntOrNull(), - orgTitle = orgTitle, - isAnime = isAnime, - airedYear = year, - lastSeason = lastSeason, - epsTitle = eps.name, - jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title, - date = season.airDate, - airedDate = res.releaseDate - ?: res.firstAirDate, - isAsian = isAsian, - isBollywood = isBollywood, - isCartoon = isCartoon - ).toJson(), - name = eps.name + if (isUpcoming(eps.airDate)) " - [UPCOMING]" else "", - season = eps.seasonNumber, - episode = eps.episodeNumber, - posterUrl = getImageUrl(eps.stillPath), - rating = eps.voteAverage?.times(10)?.roundToInt(), - description = eps.overview - ).apply { - this.addDate(eps.airDate) + Episode( + LinkData( + data.id, + res.external_ids?.imdb_id, + res.external_ids?.tvdb_id, + data.type, + eps.seasonNumber, + eps.episodeNumber, + title = title, + year = season.airDate?.split("-")?.first()?.toIntOrNull(), + orgTitle = orgTitle, + isAnime = isAnime, + airedYear = year, + lastSeason = lastSeason, + epsTitle = eps.name, + jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title, + date = season.airDate, + airedDate = res.releaseDate + ?: res.firstAirDate, + isAsian = isAsian, + isBollywood = isBollywood, + isCartoon = isCartoon + ).toJson(), + name = eps.name + if (isUpcoming(eps.airDate)) " - [UPCOMING]" else "", + season = eps.seasonNumber, + episode = eps.episodeNumber, + posterUrl = getImageUrl(eps.stillPath), + rating = eps.voteAverage?.times(10)?.roundToInt(), + description = eps.overview + ).apply { + this.addDate(eps.airDate) + } } - } }?.flatten() ?: listOf() newTvSeriesLoadResponse( title, @@ -584,7 +585,15 @@ open class SoraStream : TmdbProvider() { if (!res.isAnime) invokeNowTv(res.id, res.imdbId, res.season, res.episode, callback) }, { - if (!res.isAnime && res.season == null) invokeRidomovies(res.id, res.imdbId, res.title, res.season, res.episode, subtitleCallback, callback) + if (!res.isAnime) invokeRidomovies( + res.id, + res.imdbId, + res.title, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { if (!res.isAnime) invokeAllMovieland(res.imdbId, res.season, res.episode, callback) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index 5ab38d6c..8bd35537 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -223,7 +223,15 @@ class SoraStreamLite : SoraStream() { ) }, { - if (!res.isAnime && res.season == null) invokeRidomovies(res.id, res.imdbId, res.title, res.season, res.episode, subtitleCallback, callback) + if (!res.isAnime) invokeRidomovies( + res.id, + res.imdbId, + res.title, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { if (!res.isAnime) invokeEmovies( diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt index 9e2ec1d1..0b5a2b6c 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -1304,6 +1304,55 @@ suspend fun request( return client.newCall(request).await() } +// steal from https://github.com/aniyomiorg/aniyomi-extensions/blob/master/src/en/aniwave/src/eu/kanade/tachiyomi/animeextension/en/nineanime/AniwaveUtils.kt +// credits to @samfundev +object AniwaveUtils { + + fun encodeVrf(input: String): String { + val rc4Key = SecretKeySpec("ysJhV6U27FVIjjuk".toByteArray(), "RC4") + val cipher = Cipher.getInstance("RC4") + cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters) + var vrf = cipher.doFinal(input.toByteArray()) + vrf = Base64.encode(vrf, Base64.URL_SAFE or Base64.NO_WRAP) + vrf = Base64.encode(vrf, Base64.DEFAULT or Base64.NO_WRAP) + vrf = vrfShift(vrf) + vrf = Base64.encode(vrf, Base64.DEFAULT) + vrf = rot13(vrf) + val stringVrf = vrf.toString(Charsets.UTF_8) + return encode(stringVrf) + } + + fun decodeVrf(input: String): String { + var vrf = input.toByteArray() + vrf = Base64.decode(vrf, Base64.URL_SAFE) + val rc4Key = SecretKeySpec("hlPeNwkncH0fq9so".toByteArray(), "RC4") + val cipher = Cipher.getInstance("RC4") + cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters) + vrf = cipher.doFinal(vrf) + return decode(vrf.toString(Charsets.UTF_8)) + } + + private fun rot13(vrf: ByteArray): ByteArray { + for (i in vrf.indices) { + val byte = vrf[i] + if (byte in 'A'.code..'Z'.code) { + vrf[i] = ((byte - 'A'.code + 13) % 26 + 'A'.code).toByte() + } else if (byte in 'a'.code..'z'.code) { + vrf[i] = ((byte - 'a'.code + 13) % 26 + 'a'.code).toByte() + } + } + return vrf + } + + private fun vrfShift(vrf: ByteArray): ByteArray { + for (i in vrf.indices) { + val shift = arrayOf(-3, 3, -4, 2, -2, 5, 4, 5)[i % 8] + vrf[i] = vrf[i].plus(shift).toByte() + } + return vrf + } +} + object DumpUtils { private val deviceId = getDeviceId()