diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index af164cb7..a9ef66fb 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 = 158 +version = 159 android { defaultConfig { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 56aee7cd..18305310 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -17,6 +17,7 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody import org.jsoup.Jsoup import org.jsoup.nodes.Document +import org.jsoup.select.Elements val session = Session(Requests().baseClient) @@ -968,10 +969,10 @@ object SoraExtractor : SoraStream() { argamap( { - invokeAniwatch(malId, episode, subtitleCallback, callback) + invokeAnimetosho(malId, season, episode, subtitleCallback, callback) }, { - invokeBiliBili(aniId, episode, subtitleCallback, callback) + invokeAniwatch(malId, episode, subtitleCallback, callback) }, { if (season != null) invokeCrunchyroll( @@ -987,54 +988,55 @@ object SoraExtractor : SoraStream() { ) } - private suspend fun invokeBiliBili( - aniId: Int? = null, + private suspend fun invokeAnimetosho( + malId: Int? = null, + season: Int? = null, episode: Int? = null, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val res = app.get( - "$biliBiliAPI/anime/episodes?id=${aniId ?: return}&source_id=bilibili", - referer = otakuzBaseUrl - ) - .parsedSafe()?.episodes?.find { - it.episodeNumber == episode - } ?: return - - val sources = - app.get( - "$biliBiliAPI/source?episode_id=${res.sourceEpisodeId}&source_media_id=${res.sourceMediaId}&source_id=${res.sourceId}", - referer = otakuzBaseUrl - ) - .parsedSafe() - - sources?.sources?.apmap { source -> - val quality = - app.get( - source.file ?: return@apmap null, - referer = otakuzBaseUrl - ).document.selectFirst("Representation") - ?.attr("height") - callback.invoke( - ExtractorLink( - "BiliBili", - "BiliBili", - source.file, - "", - quality?.toIntOrNull() ?: Qualities.Unknown.value, - isDash = true - ) - ) + fun Elements.getLinks(): List> { + return this.flatMap { ele -> + ele.select("div.links a:matches(KrakenFiles|GoFile)").map { + Triple( + it.attr("href"), + ele.select("div.size").text(), + getIndexQuality(ele.select("div.link a").text()) + ) + } + } } - sources?.subtitles?.map { sub -> - subtitleCallback.invoke( - SubtitleFile( - SubtitleHelper.fromTwoLettersToLanguage(sub.lang ?: "") ?: sub.language - ?: return@map null, - sub.file ?: return@map null + val (seasonSLug, episodeSlug) = getEpisodeSlug(season, episode) + val jikan = app.get("$jikanAPI/anime/$malId/full").parsedSafe()?.data + val aniId = jikan?.external?.find { it.name == "AniDB" }?.url?.substringAfterLast("=") + val res = app.get("$animetoshoAPI/series/${jikan?.title?.createSlug()}.$aniId?filter[0][t]=nyaa_class&filter[0][v]=trusted").document + + val servers = if (season == null) { + res.select("div.home_list_entry:has(div.links)").getLinks() + } else { + res.select("div.home_list_entry:has(div.link a:matches([\\.\\s]$episodeSlug[\\.\\s]|S${seasonSLug}E$episodeSlug))") + .getLinks() + } + + servers.filter { it.third in arrayOf(Qualities.P1080.value,Qualities.P720.value) }.apmap { + loadExtractor(it.first, "$animetoshoAPI/", subtitleCallback) { link -> + callback.invoke( + ExtractorLink( + link.source, + "${link.name} [${it.second}]", + link.url, + link.referer, + when { + link.isM3u8 -> link.quality + else -> it.third + }, + link.isM3u8, + link.headers, + link.extractorData + ) ) - ) + } } } @@ -2209,7 +2211,7 @@ object SoraExtractor : SoraStream() { "$dahmerMoviesAPI/tvs/${title?.replace(":", " -")}/Season $season/" } - val request = app.get(url) + val request = app.get(url, timeout = 120L) if (!request.isSuccessful) return val paths = request.document.select("a").map { it.text() to it.attr("href") @@ -2239,6 +2241,33 @@ object SoraExtractor : SoraStream() { } + suspend fun invoke2embed( + imdbId: String?, + season: Int?, + episode: Int?, + callback: (ExtractorLink) -> Unit + ) { + val server = "https://stream.2embed.cc" + val url = if(season == null) { + "$twoEmbedAPI/embed/$imdbId" + } else { + "$twoEmbedAPI/embedtv/$imdbId&s=$season&e=$episode" + } + + val iframesrc = app.get(url).document.selectFirst("iframe#iframesrc")?.attr("src") + val framesrc = app.get(fixUrl(iframesrc ?: return, twoEmbedAPI)).document.selectFirst("iframe#framesrc")?.attr("src") + val video = app.get(fixUrl(framesrc ?: return, "$server/e/")).text.let { + Regex("file:\\s*\"(.*?m3u8.*?)\"").find(it)?.groupValues?.getOrNull(1) + } + + M3u8Helper.generateM3u8( + "2embed", + video ?: return, + "$server/", + ).forEach(callback) + + } + suspend fun invokePrimewire( title: String? = null, year: Int? = null, diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt index 8387ccb9..db94285e 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt @@ -177,32 +177,18 @@ data class IndexSearch( @JsonProperty("data") val data: IndexData? = null, ) -data class BiliBiliEpisodes( - @JsonProperty("id") val id: Int? = null, - @JsonProperty("sourceId") val sourceId: String? = null, - @JsonProperty("sourceEpisodeId") val sourceEpisodeId: String? = null, - @JsonProperty("sourceMediaId") val sourceMediaId: String? = null, - @JsonProperty("episodeNumber") val episodeNumber: Int? = null, +data class JikanExternal( + @JsonProperty("name") val name: String? = null, + @JsonProperty("url") val url: String? = null, ) -data class BiliBiliDetails( - @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf(), +data class JikanData( + @JsonProperty("title") val title: String? = null, + @JsonProperty("external") val external: ArrayList? = arrayListOf(), ) -data class BiliBiliSubtitles( - @JsonProperty("file") val file: String? = null, - @JsonProperty("lang") val lang: String? = null, - @JsonProperty("language") val language: String? = null, -) - -data class BiliBiliSources( - @JsonProperty("file") val file: String? = null, - @JsonProperty("type") val type: String? = null, -) - -data class BiliBiliSourcesResponse( - @JsonProperty("sources") val sources: ArrayList? = arrayListOf(), - @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), +data class JikanResponse( + @JsonProperty("data") val data: JikanData? = null, ) data class WatchOnlineItems( diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 36df7dc2..16c74135 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -1,6 +1,7 @@ package com.hexated import com.fasterxml.jackson.annotation.JsonProperty +import com.hexated.SoraExtractor.invoke2embed import com.hexated.SoraExtractor.invokeAnimes import com.hexated.SoraExtractor.invokeAsk4Movies import com.hexated.SoraExtractor.invokeBollyMaza @@ -80,11 +81,13 @@ open class SoraStream : TmdbProvider() { const val anilistAPI = "https://graphql.anilist.co" const val malsyncAPI = "https://api.malsync.moe" const val consumetHelper = "https://api.consumet.org/anime/9anime/helper" + const val jikanAPI = "https://api.jikan.moe/v4" private val apiKey = base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL /** ALL SOURCES */ + const val twoEmbedAPI = "https://www.2embed.cc" const val vidSrcAPI = "https://v2.vidsrc.me" const val dbgoAPI = "https://dbgo.fun" const val movieHabAPI = "https://moviehab.com" @@ -113,7 +116,6 @@ open class SoraStream : TmdbProvider() { const val smashyStreamAPI = "https://embed.smashystream.com" const val watchSomuchAPI = "https://watchsomuch.tv" // sub only const val ask4MoviesAPI = "https://ask4movie.nl" - const val biliBiliAPI = "https://api-vn.otakuz.live/server" const val watchOnlineAPI = "https://watchonline.ag" const val nineTvAPI = "https://moviesapi.club" const val putlockerAPI = "https://ww7.putlocker.vip" @@ -132,6 +134,7 @@ open class SoraStream : TmdbProvider() { const val primewireAPI = "https://real-primewire.club" const val vidsrctoAPI = "https://vidsrc.to" const val dramadayAPI = "https://dramaday.me" + const val animetoshoAPI = "https://animetosho.org" // INDEX SITE const val dahmerMoviesAPI = "https://edytjedhgmdhm.abfhaqrhbnf.workers.dev" @@ -739,7 +742,10 @@ open class SoraStream : TmdbProvider() { subtitleCallback, callback ) - } + }, + { + if(!res.isAnime) invoke2embed(res.imdbId,res.season,res.episode,callback) + }, ) return true diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index 6b3a7818..c5bf1a6d 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -1,5 +1,6 @@ package com.hexated +import com.hexated.SoraExtractor.invoke2embed import com.hexated.SoraExtractor.invokeAnimes import com.hexated.SoraExtractor.invokeAsk4Movies import com.hexated.SoraExtractor.invokeDbgo @@ -355,7 +356,15 @@ class SoraStreamLite : SoraStream() { subtitleCallback, callback ) - } + }, + { + if(!res.isAnime) invoke2embed( + res.imdbId, + res.season, + res.episode, + callback + ) + }, ) return true