sora: fixed Crunchyroll and Zoro

This commit is contained in:
hexated 2023-05-21 20:20:19 +07:00
parent 92803be98d
commit 93c948bb8e
6 changed files with 298 additions and 461 deletions

View file

@ -46,12 +46,16 @@ jobs:
SORAHE: ${{ secrets.SORAHE }} SORAHE: ${{ secrets.SORAHE }}
SORAXA: ${{ secrets.SORAXA }} SORAXA: ${{ secrets.SORAXA }}
SORATED: ${{ secrets.SORATED }} SORATED: ${{ secrets.SORATED }}
CRUNCHYROLL_BASIC_TOKEN: ${{ secrets.CRUNCHYROLL_BASIC_TOKEN }}
CRUNCHYROLL_REFRESH_TOKEN: ${{ secrets.CRUNCHYROLL_REFRESH_TOKEN }}
run: | run: |
cd $GITHUB_WORKSPACE/src cd $GITHUB_WORKSPACE/src
echo SORA_API=$SORA_API >> local.properties echo SORA_API=$SORA_API >> local.properties
echo SORAHE=$SORAHE >> local.properties echo SORAHE=$SORAHE >> local.properties
echo SORAXA=$SORAXA >> local.properties echo SORAXA=$SORAXA >> local.properties
echo SORATED=$SORATED >> local.properties echo SORATED=$SORATED >> local.properties
echo CRUNCHYROLL_BASIC_TOKEN=$CRUNCHYROLL_BASIC_TOKEN >> local.properties
echo CRUNCHYROLL_REFRESH_TOKEN=$CRUNCHYROLL_REFRESH_TOKEN >> local.properties
- name: Build Plugins - name: Build Plugins
run: | run: |

View file

@ -12,6 +12,8 @@ android {
buildConfigField("String", "SORAHE", "\"${properties.getProperty("SORAHE")}\"") buildConfigField("String", "SORAHE", "\"${properties.getProperty("SORAHE")}\"")
buildConfigField("String", "SORAXA", "\"${properties.getProperty("SORAXA")}\"") buildConfigField("String", "SORAXA", "\"${properties.getProperty("SORAXA")}\"")
buildConfigField("String", "SORATED", "\"${properties.getProperty("SORATED")}\"") buildConfigField("String", "SORATED", "\"${properties.getProperty("SORATED")}\"")
buildConfigField("String", "CRUNCHYROLL_BASIC_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_BASIC_TOKEN")}\"")
buildConfigField("String", "CRUNCHYROLL_REFRESH_TOKEN", "\"${properties.getProperty("CRUNCHYROLL_REFRESH_TOKEN")}\"")
} }

View file

@ -45,7 +45,9 @@ object SoraExtractor : SoraStream() {
val link = source.link ?: return@let val link = source.link ?: return@let
if (link.contains("rabbitstream")) { if (link.contains("rabbitstream")) {
extractRabbitStream( extractRabbitStream(
"Vidcloud",
link, link,
"$twoEmbedAPI/",
subtitleCallback, subtitleCallback,
callback, callback,
false, false,
@ -188,21 +190,6 @@ object SoraExtractor : SoraStream() {
} }
} }
suspend fun invokeDatabaseGdrive(
imdbId: String? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val url = if (season == null) {
"$databaseGdriveAPI/player.php?imdb=$imdbId"
} else {
"$databaseGdriveAPI/player.php?type=series&imdb=$imdbId&season=$season&episode=$episode"
}
loadExtractor(url, databaseGdriveAPI, subtitleCallback, callback)
}
suspend fun invokeHDMovieBox( suspend fun invokeHDMovieBox(
title: String? = null, title: String? = null,
season: Int? = null, season: Int? = null,
@ -327,14 +314,13 @@ object SoraExtractor : SoraStream() {
}, { }, {
val iv = "9225679083961858" val iv = "9225679083961858"
val secretKey = "25742532592138496744665879883281" val secretKey = "25742532592138496744665879883281"
val secretDecryptKey = secretKey
GogoHelper.extractVidstream( GogoHelper.extractVidstream(
iframe.url, iframe.url,
"Vidstream", "Vidstream",
callback, callback,
iv, iv,
secretKey, secretKey,
secretDecryptKey, secretKey,
isUsingAdaptiveKeys = false, isUsingAdaptiveKeys = false,
isUsingAdaptiveData = true, isUsingAdaptiveData = true,
iframeDocument = iframeDoc iframeDocument = iframeDoc
@ -496,13 +482,12 @@ object SoraExtractor : SoraStream() {
} else { } else {
"${filmxyAPI}/tv/$imdbId" "${filmxyAPI}/tv/$imdbId"
} }
val filmxyCookies = val filmxyCookies = getFilmxyCookies(imdbId, season)
getFilmxyCookies(imdbId, season) ?: throw ErrorLoadingException("No Cookies Found")
val cookiesDoc = mapOf( val cookiesDoc = mapOf(
"G_ENABLED_IDPS" to "google", "G_ENABLED_IDPS" to "google",
"wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to "${filmxyCookies.wLog}", "wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to (filmxyCookies.wLog ?: return),
"PHPSESSID" to "${filmxyCookies.phpsessid}" "PHPSESSID" to (filmxyCookies.phpsessid ?: return)
) )
val doc = session.get(url, cookies = cookiesDoc).document val doc = session.get(url, cookies = cookiesDoc).document
@ -868,85 +853,10 @@ object SoraExtractor : SoraStream() {
{ {
invokeBiliBili(aniId, episode, subtitleCallback, callback) invokeBiliBili(aniId, episode, subtitleCallback, callback)
}, },
// { {
// if (season != null) invokeAllanime(aniId, title, jpTitle, episode, callback) if (season != null) invokeCrunchyroll(aniId, epsTitle, season, episode, subtitleCallback, callback)
// }
)
}
private suspend fun invokeAllanime(
aniId: String? = null,
title: String? = null,
jpTitle: String? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit
) {
val aniDetail =
app.get("$consumetAnilistAPI/info/$aniId").parsedSafe<ConsumetDetails>() ?: return
val edges = app.get(
allanimeQueries(
"""{"search":{"query":"$title","allowAdult":false,"allowUnknown":false},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}""",
allanimeSearchQuery
)
)
.parsedSafe<AllanimeResponses>()?.data?.shows?.edges
val id = edges?.let { edge ->
if (edges.size == 1) {
edge.firstOrNull()
} else {
edge.find {
(it.thumbnail == aniDetail.cover || it.thumbnail == aniDetail.image) || (
(it.name.equals(
aniDetail.title?.romaji,
true
) || it.name.equals(
jpTitle,
true
) || it.englishName.equals(aniDetail.title?.english, true))
&& it.airedStart?.year == aniDetail.releaseDate)
} ?: edge.find {
it.name.equals(
aniDetail.title?.romaji,
true
) || it.name.equals(
jpTitle,
true
) || it.englishName.equals(
aniDetail.title?.english,
true
) || it.englishName.equals(title, true)
}
} }
}?._id ?: return )
listOf(
"sub",
"dub"
).apmap { tl ->
val server = app.get(
allanimeQueries(
"""{"showId":"$id","translationType":"$tl","episodeString":"$episode"}""",
allanimeServerQuery
)
)
.parsedSafe<AllanimeResponses>()?.data?.episode?.sourceUrls?.find { it.sourceName == "Ac" }
val serverUrl = fixUrl(
server?.sourceUrl?.replace("/clock", "/clock.json") ?: return@apmap,
"https://blog.allanime.pro"
)
app.get(serverUrl)
.parsedSafe<AllanimeLinks>()?.links?.filter { it.resolutionStr == "RAW" && it.hls == true }
?.forEach { source ->
val translation = if (tl == "sub") "Raw" else "English Dub"
M3u8Helper.generateM3u8(
"Vrv [$translation]",
source.link ?: return@apmap,
"https://static.crunchyroll.com/",
).forEach(callback)
}
}
} }
private suspend fun invokeBiliBili( private suspend fun invokeBiliBili(
@ -999,41 +909,47 @@ object SoraExtractor : SoraStream() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
val episodeId = app.get("$consumetAnilistAPI/info/$aniId?provider=zoro") val animeId =
.parsedSafe<ConsumetDetails>()?.episodes?.find { app.get("https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/anilist/anime/$aniId.json")
it.number == (episode ?: 1) .parsedSafe<MALSyncResponses>()?.pages?.zoro?.keys?.firstOrNull()
}?.id?.substringBeforeLast("$") ?: return
listOf( val episodeId = app.get("$zoroAPI/ajax/v2/episode/list/${animeId ?: return}")
"$episodeId\$sub" to "Raw", .parsedSafe<ZoroResponses>()?.html?.let {
"$episodeId\$dub" to "English Dub", Jsoup.parse(it)
).apmap { (id, type) -> }?.select("div.ss-list a")?.find { it.attr("data-number") == "$episode" }
val sources = app.get("$consumetZoroAPI/watch?episodeId=$id") ?.attr("data-id")
.parsedSafe<ConsumetSourcesResponse>() ?: return@apmap null
sources.sources?.map sources@{ val servers = app.get("$zoroAPI/ajax/v2/episode/servers?episodeId=${episodeId ?: return}")
callback.invoke( .parsedSafe<ZoroResponses>()?.html?.let { Jsoup.parse(it) }
ExtractorLink( ?.select("div.item.server-item")?.map {
"Zoro [$type]", Triple(
"Zoro [$type]", it.text(),
it.url ?: return@sources null, it.attr("data-id"),
"", it.attr("data-type"),
getQualityFromName(it.quality),
it.isM3U8 ?: true
)
) )
} }
sources.subtitles?.map subtitles@{ servers?.apmap { server ->
subtitleCallback.invoke( val iframe = app.get("$zoroAPI/ajax/v2/episode/sources?id=${server.second ?: return@apmap}")
SubtitleFile( .parsedSafe<ZoroResponses>()?.link ?: return@apmap
it.lang ?: "", val audio = if(server.third == "sub") "Raw" else "English Dub"
it.url ?: return@subtitles null if(server.first == "Vidstreaming" || server.first == "Vidcloud") {
) extractRabbitStream(
) "${server.first} [$audio]",
iframe,
"$zoroAPI/",
subtitleCallback,
callback,
false,
decryptKey = RabbitStream.getZoroKey()
) { it }
} else {
loadExtractor(iframe,"$zoroAPI/", subtitleCallback, callback)
} }
} }
} }
private suspend fun invokeAnimeKaizoku( private suspend fun invokeAnimeKaizoku(
@ -1551,52 +1467,78 @@ object SoraExtractor : SoraStream() {
} }
suspend fun invokeCrunchyroll( suspend fun invokeCrunchyroll(
title: String? = null, aniId: String? = null,
epsTitle: String? = null, epsTitle: String? = null,
season: Int? = null, season: Int? = null,
episode: Int? = null, episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
val id = searchCrunchyrollAnimeId(title ?: return) ?: return val id = getCrunchyrollId(aniId) ?: return
val detail = val audioLocal = listOf(
app.get("$consumetCrunchyrollAPI/info/$id?fetchAllSeasons=true", timeout = 600L).text "ja-JP",
val epsId = tryParseJson<CrunchyrollDetails>(detail)?.findCrunchyrollId( "en-US",
season, "zh-CN",
episode,
epsTitle
) )
val headers = getCrunchyrollToken()
val seasonIdData = app.get("$crunchyrollAPI/content/v2/cms/series/$id/seasons", headers = headers)
.parsedSafe<CrunchyrollResponses>()?.data?.let { s ->
if (s.size == 1) {
s.firstOrNull()
} else {
s.find {
when (epsTitle) {
"One Piece" -> it.season_number == 13
"Hunter x Hunter" -> it.season_number == 5
else -> it.season_number == season
}
}
}
}
val seasonId = seasonIdData?.versions?.filter { it.audio_locale in audioLocal }
?.map { it.guid to it.audio_locale }
epsId?.apmap { seasonId?.apmap { (sId, audioL) ->
delay(2000) val streamsLink =
val json =
app.get( app.get(
"$consumetCrunchyrollAPI/watch/${it?.first?.id ?: return@apmap null}", "$crunchyrollAPI/content/v2/cms/seasons/${sId ?: return@apmap}/episodes",
timeout = 600L headers = headers
) ).parsedSafe<CrunchyrollResponses>()?.data?.find {
.parsedSafe<ConsumetSourcesResponse>() it.title.equals(epsTitle, true) || it.slug_title.equals(
epsTitle.createSlug(),
json?.sources?.map source@{ source ->
callback.invoke(
ExtractorLink(
"Crunchyroll",
"Crunchyroll [${it.second ?: ""}]",
source.url ?: return@source null,
"https://static.crunchyroll.com/",
source.quality?.removeSuffix("p")?.toIntOrNull() ?: return@source null,
true true
) ) || it.episode_number == episode
) }?.streams_link
val sources =
app.get(fixUrl(streamsLink ?: return@apmap, crunchyrollAPI), headers = headers)
.parsedSafe<CrunchyrollSourcesResponses>()
listOf(
"adaptive_hls",
"vo_adaptive_hls"
).map { hls ->
val name = if (hls == "adaptive_hls") "Crunchyroll" else "Vrv"
val audio = if (audioL == "en-US") "English Dub" else "Raw"
val source = sources?.data?.firstOrNull()?.let {
if (hls == "adaptive_hls") it.adaptive_hls else it.vo_adaptive_hls
}
M3u8Helper.generateM3u8(
"$name [$audio]",
source?.get("")?.get("url") ?: return@map,
"https://static.crunchyroll.com/"
).forEach(callback)
} }
json?.subtitles?.map subtitle@{ sub -> sources?.meta?.subtitles?.map { sub ->
subtitleCallback.invoke( subtitleCallback.invoke(
SubtitleFile( SubtitleFile(
"${fixCrunchyrollLang(sub.lang ?: return@subtitle null) ?: sub.lang} [ass]", "${fixCrunchyrollLang(sub.key) ?: sub.key} [ass]",
sub.url ?: return@subtitle null sub.value["url"] ?: return@map null
) )
) )
} }
} }
} }
@ -1900,6 +1842,7 @@ object SoraExtractor : SoraStream() {
imdbId: String? = null, imdbId: String? = null,
season: Int? = null, season: Int? = null,
episode: Int? = null, episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
) { ) {
val url = if (season == null) { val url = if (season == null) {
@ -1927,6 +1870,9 @@ object SoraExtractor : SoraStream() {
it.first.contains("/nflim") -> { it.first.contains("/nflim") -> {
invokeSmashyNflim(it.second, it.first, callback) invokeSmashyNflim(it.second, it.first, callback)
} }
it.first.contains("/rip") -> {
invokeSmashyRip(it.second, it.first, subtitleCallback, callback)
}
else -> return@apmap else -> return@apmap
} }
} }
@ -2121,46 +2067,6 @@ object SoraExtractor : SoraStream() {
) )
} }
suspend fun invokePapaonMovies1(
apiUrl: String,
api: String,
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
invokeIndex(
apiUrl,
api,
title,
year,
season,
episode,
callback,
)
}
suspend fun invokePapaonMovies2(
apiUrl: String,
api: String,
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
invokeIndex(
apiUrl,
api,
title,
year,
season,
episode,
callback,
)
}
suspend fun invokeJmdkhMovies( suspend fun invokeJmdkhMovies(
apiUrl: String, apiUrl: String,
api: String, api: String,
@ -2700,7 +2606,7 @@ object SoraExtractor : SoraStream() {
season: Int? = null, season: Int? = null,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
) { ) {
val monsterMainUrl = "https://freedoze.monster" val monsterMainUrl = "https://mobirs.monster"
val playSlug = if (season == null) { val playSlug = if (season == null) {
"movies/play/$urlSlug" "movies/play/$urlSlug"
} else { } else {
@ -2713,7 +2619,7 @@ object SoraExtractor : SoraStream() {
val res = app.get(url).document val res = app.get(url).document
val script = res.selectFirst("script:containsData(window['show_storage'])")?.data() val script = res.selectFirst("script:containsData(window['show_storage'])")?.data()
val hash = Regex("hash:\\s*['\"](\\S+)['\"],").find(script ?: return)?.groupValues?.get(1) val hash = Regex("hash:\\s*['\"](\\S+)['\"],").find(script ?: return)?.groupValues?.get(1)
val expires = Regex("expires:\\s*(\\d+),").find(script ?: return)?.groupValues?.get(1) val expires = Regex("expires:\\s*(\\d+),").find(script)?.groupValues?.get(1)
val videoUrl = if (season == null) { val videoUrl = if (season == null) {
"$monsterMainUrl/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires" "$monsterMainUrl/api/v1/security/movie-access?id_movie=$episodeId&hash=$hash&expires=$expires"
@ -3051,71 +2957,6 @@ data class Load(
@JsonProperty("data") val data: MediaDetail? = null, @JsonProperty("data") val data: MediaDetail? = null,
) )
data class ConsumetHeaders(
@JsonProperty("Referer") val referer: String? = null,
)
data class ConsumetSubtitles(
@JsonProperty("url") val url: String? = null,
@JsonProperty("lang") val lang: String? = null,
)
data class ConsumetSources(
@JsonProperty("url") val url: String? = null,
@JsonProperty("quality") val quality: String? = null,
@JsonProperty("isM3U8") val isM3U8: Boolean? = null,
)
data class ConsumetSourcesResponse(
@JsonProperty("headers") val headers: ConsumetHeaders? = null,
@JsonProperty("sources") val sources: ArrayList<ConsumetSources>? = arrayListOf(),
@JsonProperty("subtitles") val subtitles: ArrayList<ConsumetSubtitles>? = arrayListOf(),
)
data class ConsumetEpisodes(
@JsonProperty("id") val id: String? = null,
@JsonProperty("type") val type: String? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("number") val number: Int? = null,
@JsonProperty("season") val season: Int? = null,
)
data class ConsumetTitle(
@JsonProperty("romaji") val romaji: String? = null,
@JsonProperty("english") val english: String? = null
)
data class ConsumetDetails(
@JsonProperty("episodes") val episodes: ArrayList<ConsumetEpisodes>? = arrayListOf(),
@JsonProperty("image") val image: String? = null,
@JsonProperty("cover") val cover: String? = null,
@JsonProperty("title") val title: ConsumetTitle? = null,
@JsonProperty("releaseDate") val releaseDate: Int? = null
)
data class CrunchyrollEpisodes(
@JsonProperty("id") val id: String? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("episode_number") val episode_number: Int? = null,
@JsonProperty("season_number") val season_number: Int? = null,
)
data class CrunchyrollDetails(
@JsonProperty("episodes") val episodes: HashMap<String, List<CrunchyrollEpisodes>>? = hashMapOf(),
)
data class ConsumetResults(
@JsonProperty("id") val id: String? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("seasons") val seasons: Int? = null,
@JsonProperty("releaseDate") val releaseDate: String? = null,
@JsonProperty("type") val type: String? = null,
)
data class ConsumetSearchResponse(
@JsonProperty("results") val results: ArrayList<ConsumetResults>? = arrayListOf(),
)
data class KisskhSources( data class KisskhSources(
@JsonProperty("Video") val video: String?, @JsonProperty("Video") val video: String?,
@JsonProperty("ThirdParty") val thirdParty: String?, @JsonProperty("ThirdParty") val thirdParty: String?,
@ -3246,23 +3087,6 @@ data class SorastreamVideos(
@JsonProperty("currentDefinition") val currentDefinition: String? = null, @JsonProperty("currentDefinition") val currentDefinition: String? = null,
) )
data class SapphireSubtitles(
@JsonProperty("language") val language: String? = null,
@JsonProperty("url") val url: String? = null,
)
data class SapphireStreams(
@JsonProperty("format") val format: String? = null,
@JsonProperty("audio_lang") val audio_lang: String? = null,
@JsonProperty("hardsub_lang") val hardsub_lang: String? = null,
@JsonProperty("url") val url: String? = null,
)
data class SapphireSources(
@JsonProperty("streams") val streams: ArrayList<SapphireStreams>? = arrayListOf(),
@JsonProperty("subtitles") val subtitles: ArrayList<SapphireSubtitles>? = arrayListOf(),
)
data class BiliBiliEpisodes( data class BiliBiliEpisodes(
@JsonProperty("id") val id: Int? = null, @JsonProperty("id") val id: Int? = null,
@JsonProperty("sourceId") val sourceId: String? = null, @JsonProperty("sourceId") val sourceId: String? = null,
@ -3325,74 +3149,6 @@ data class PutlockerResponses(
@JsonProperty("backupLink") val backupLink: String? = null, @JsonProperty("backupLink") val backupLink: String? = null,
) )
data class AllanimeStreams(
@JsonProperty("format") val format: String? = null,
@JsonProperty("url") val url: String? = null,
@JsonProperty("audio_lang") val audio_lang: String? = null,
@JsonProperty("hardsub_lang") val hardsub_lang: String? = null,
)
data class AllanimePortData(
@JsonProperty("streams") val streams: ArrayList<AllanimeStreams>? = arrayListOf(),
)
data class AllanimeLink(
@JsonProperty("portData") val portData: AllanimePortData? = null,
@JsonProperty("resolutionStr") val resolutionStr: String? = null,
@JsonProperty("src") val src: String? = null,
@JsonProperty("link") val link: String? = null,
@JsonProperty("hls") val hls: Boolean? = null,
)
data class AllanimeLinks(
@JsonProperty("links") val links: ArrayList<AllanimeLink>? = arrayListOf(),
)
data class AllanimeSourceUrls(
@JsonProperty("sourceUrl") val sourceUrl: String? = null,
@JsonProperty("sourceName") val sourceName: String? = null,
)
data class AllanimeEpisode(
@JsonProperty("sourceUrls") val sourceUrls: ArrayList<AllanimeSourceUrls>? = arrayListOf(),
)
data class AllanimeAvailableEpisodesDetail(
@JsonProperty("sub") val sub: ArrayList<String>? = arrayListOf(),
@JsonProperty("dub") val dub: ArrayList<String>? = arrayListOf(),
)
data class AllanimeDetailShow(
@JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AllanimeAvailableEpisodesDetail? = null,
)
data class AllanimeAiredStart(
@JsonProperty("year") val year: Int? = null,
)
data class AllanimeEdges(
@JsonProperty("_id") val _id: String? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("englishName") val englishName: String? = null,
@JsonProperty("thumbnail") val thumbnail: String? = null,
@JsonProperty("type") val type: String? = null,
@JsonProperty("airedStart") val airedStart: AllanimeAiredStart? = null,
)
data class AllanimeShows(
@JsonProperty("edges") val edges: ArrayList<AllanimeEdges>? = arrayListOf(),
)
data class AllanimeData(
@JsonProperty("shows") val shows: AllanimeShows? = null,
@JsonProperty("show") val show: AllanimeDetailShow? = null,
@JsonProperty("episode") val episode: AllanimeEpisode? = null,
)
data class AllanimeResponses(
@JsonProperty("data") val data: AllanimeData? = null,
)
data class ShivamhwSources( data class ShivamhwSources(
@JsonProperty("id") val id: String? = null, @JsonProperty("id") val id: String? = null,
@JsonProperty("stream_link") val stream_link: String? = null, @JsonProperty("stream_link") val stream_link: String? = null,
@ -3448,4 +3204,74 @@ data class VizcloudData(
data class VizcloudResponses( data class VizcloudResponses(
@JsonProperty("data") val data: VizcloudData? = null, @JsonProperty("data") val data: VizcloudData? = 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,
)
data class AnilistMedia(
@JsonProperty("externalLinks") var externalLinks: ArrayList<AnilistExternalLinks> = arrayListOf()
)
data class AnilistData(
@JsonProperty("Media") var Media: AnilistMedia? = AnilistMedia()
)
data class AnilistResponses(
@JsonProperty("data") var data: AnilistData? = AnilistData()
)
data class CrunchyrollToken(
@JsonProperty("access_token") val accessToken: String? = null,
@JsonProperty("expires_in") val expiresIn: Int? = null,
@JsonProperty("token_type") val tokenType: String? = null,
@JsonProperty("scope") val scope: String? = null,
@JsonProperty("country") val country: String? = null
)
data class CrunchyrollVersions(
@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<CrunchyrollVersions>? = null,
@JsonProperty("streams_link") val streams_link: String? = null,
@JsonProperty("adaptive_hls") val adaptive_hls: HashMap<String, HashMap<String, String>>? = hashMapOf(),
@JsonProperty("vo_adaptive_hls") val vo_adaptive_hls: HashMap<String, HashMap<String, String>>? = hashMapOf(),
)
data class CrunchyrollResponses(
@JsonProperty("data") val data: ArrayList<CrunchyrollData>? = arrayListOf(),
)
data class CrunchyrollMeta(
@JsonProperty("subtitles") val subtitles: HashMap<String, HashMap<String, String>>? = hashMapOf(),
)
data class CrunchyrollSourcesResponses(
@JsonProperty("data") val data: ArrayList<CrunchyrollData>? = arrayListOf(),
@JsonProperty("meta") val meta: CrunchyrollMeta? = null,
)
data class MALSyncPages(
@JsonProperty("Zoro") val zoro: HashMap<String, HashMap<String, String>>? = hashMapOf(),
)
data class MALSyncResponses(
@JsonProperty("Pages") val pages: MALSyncPages? = null,
)
data class ZoroResponses(
@JsonProperty("html") val html: String? = null,
@JsonProperty("link") val link: String? = null,
) )

View file

@ -6,7 +6,6 @@ import com.hexated.SoraExtractor.invokeAsk4Movies
import com.hexated.SoraExtractor.invokeBlackmovies import com.hexated.SoraExtractor.invokeBlackmovies
import com.hexated.SoraExtractor.invokeBollyMaza import com.hexated.SoraExtractor.invokeBollyMaza
import com.hexated.SoraExtractor.invokeCodexmovies import com.hexated.SoraExtractor.invokeCodexmovies
import com.hexated.SoraExtractor.invokeCrunchyroll
import com.hexated.SoraExtractor.invokeCryMovies import com.hexated.SoraExtractor.invokeCryMovies
import com.hexated.SoraExtractor.invokeDbgo import com.hexated.SoraExtractor.invokeDbgo
import com.hexated.SoraExtractor.invokeFilmxy import com.hexated.SoraExtractor.invokeFilmxy
@ -17,7 +16,6 @@ import com.hexated.SoraExtractor.invokeMovieHab
import com.hexated.SoraExtractor.invokeNoverse import com.hexated.SoraExtractor.invokeNoverse
import com.hexated.SoraExtractor.invokeSeries9 import com.hexated.SoraExtractor.invokeSeries9
import com.hexated.SoraExtractor.invokeTwoEmbed import com.hexated.SoraExtractor.invokeTwoEmbed
import com.hexated.SoraExtractor.invokeUniqueStream
import com.hexated.SoraExtractor.invokeVidSrc import com.hexated.SoraExtractor.invokeVidSrc
import com.hexated.SoraExtractor.invokeXmovies import com.hexated.SoraExtractor.invokeXmovies
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
@ -41,8 +39,6 @@ import com.hexated.SoraExtractor.invokeMoviesbay
import com.hexated.SoraExtractor.invokeMoviezAdd import com.hexated.SoraExtractor.invokeMoviezAdd
import com.hexated.SoraExtractor.invokeNinetv import com.hexated.SoraExtractor.invokeNinetv
import com.hexated.SoraExtractor.invokeNowTv import com.hexated.SoraExtractor.invokeNowTv
import com.hexated.SoraExtractor.invokePapaonMovies1
import com.hexated.SoraExtractor.invokePapaonMovies2
import com.hexated.SoraExtractor.invokePutlocker import com.hexated.SoraExtractor.invokePutlocker
import com.hexated.SoraExtractor.invokeRStream import com.hexated.SoraExtractor.invokeRStream
import com.hexated.SoraExtractor.invokeRinzrymovies import com.hexated.SoraExtractor.invokeRinzrymovies
@ -51,7 +47,6 @@ import com.hexated.SoraExtractor.invokeShinobiMovies
import com.hexated.SoraExtractor.invokeShivamhw import com.hexated.SoraExtractor.invokeShivamhw
import com.hexated.SoraExtractor.invokeSmashyStream import com.hexated.SoraExtractor.invokeSmashyStream
import com.hexated.SoraExtractor.invokeSoraStream import com.hexated.SoraExtractor.invokeSoraStream
import com.hexated.SoraExtractor.invokeTgarMovies
import com.hexated.SoraExtractor.invokeTvMovies import com.hexated.SoraExtractor.invokeTvMovies
import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeUhdmovies
import com.hexated.SoraExtractor.invokeVitoenMovies import com.hexated.SoraExtractor.invokeVitoenMovies
@ -81,17 +76,14 @@ open class SoraStream : TmdbProvider() {
private const val tmdbAPI = "https://api.themoviedb.org/3" private const val tmdbAPI = "https://api.themoviedb.org/3"
const val tmdb2anilist = "https://tmdb2anilist.slidemovies.org" const val tmdb2anilist = "https://tmdb2anilist.slidemovies.org"
const val gdbot = "https://gdtot.pro" const val gdbot = "https://gdtot.pro"
const val consumetAnilistAPI = "https://api.consumet.org/meta/anilist"
private val apiKey = private val apiKey = base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL
base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL
/** ALL SOURCES */ /** ALL SOURCES */
const val twoEmbedAPI = "https://www.2embed.to" const val twoEmbedAPI = "https://www.2embed.to"
const val vidSrcAPI = "https://v2.vidsrc.me" const val vidSrcAPI = "https://v2.vidsrc.me"
const val dbgoAPI = "https://dbgo.fun" const val dbgoAPI = "https://dbgo.fun"
const val movieHabAPI = "https://moviehab.com" const val movieHabAPI = "https://moviehab.com"
const val databaseGdriveAPI = "https://databasegdriveplayer.co"
const val hdMovieBoxAPI = "https://hdmoviebox.net" const val hdMovieBoxAPI = "https://hdmoviebox.net"
const val series9API = "https://series9.sh" const val series9API = "https://series9.sh"
const val idlixAPI = "https://idlixian.com" const val idlixAPI = "https://idlixian.com"
@ -100,11 +92,11 @@ open class SoraStream : TmdbProvider() {
const val filmxyAPI = "https://www.filmxy.vip" const val filmxyAPI = "https://www.filmxy.vip"
const val kimcartoonAPI = "https://kimcartoon.li" const val kimcartoonAPI = "https://kimcartoon.li"
const val xMovieAPI = "https://xemovies.to" const val xMovieAPI = "https://xemovies.to"
const val consumetZoroAPI = "https://api.consumet.org/anime/zoro" const val zoroAPI = "https://sanji.to"
const val allanimeAPI = "https://api.allanime.to" const val crunchyrollAPI = "https://beta-api.crunchyroll.com"
const val kissKhAPI = "https://kisskh.co" const val kissKhAPI = "https://kisskh.co"
const val lingAPI = "https://ling-online.net" const val lingAPI = "https://ling-online.net"
const val uhdmoviesAPI = "https://uhdmovies.one" const val uhdmoviesAPI = "https://uhdmovies.bio"
const val fwatayakoAPI = "https://5100.svetacdn.in" const val fwatayakoAPI = "https://5100.svetacdn.in"
const val gMoviesAPI = "https://gdrivemovies.xyz" const val gMoviesAPI = "https://gdrivemovies.xyz"
const val fdMoviesAPI = "https://freedrivemovie.lol" const val fdMoviesAPI = "https://freedrivemovie.lol"
@ -600,7 +592,7 @@ open class SoraStream : TmdbProvider() {
invokeMovie123Net(res.title, res.season, res.episode, subtitleCallback, callback) invokeMovie123Net(res.title, res.season, res.episode, subtitleCallback, callback)
}, },
{ {
invokeSmashyStream(res.imdbId, res.season, res.episode, callback) invokeSmashyStream(res.imdbId, res.season, res.episode, subtitleCallback, callback)
}, },
{ {
invokeWatchsomuch( invokeWatchsomuch(

View file

@ -167,7 +167,7 @@ class SoraStreamLite : SoraStream() {
invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback)
}, },
{ {
invokeSmashyStream(res.imdbId, res.season, res.episode, callback) invokeSmashyStream(res.imdbId, res.season, res.episode, subtitleCallback, callback)
}, },
{ {
invokeXmovies( invokeXmovies(

View file

@ -2,10 +2,9 @@ package com.hexated
import android.util.Base64 import android.util.Base64
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.hexated.SoraStream.Companion.allanimeAPI
import com.hexated.SoraStream.Companion.base64DecodeAPI import com.hexated.SoraStream.Companion.base64DecodeAPI
import com.hexated.SoraStream.Companion.baymoviesAPI import com.hexated.SoraStream.Companion.baymoviesAPI
import com.hexated.SoraStream.Companion.consumetCrunchyrollAPI import com.hexated.SoraStream.Companion.crunchyrollAPI
import com.hexated.SoraStream.Companion.filmxyAPI import com.hexated.SoraStream.Companion.filmxyAPI
import com.hexated.SoraStream.Companion.gdbot import com.hexated.SoraStream.Companion.gdbot
import com.hexated.SoraStream.Companion.putlockerAPI import com.hexated.SoraStream.Companion.putlockerAPI
@ -17,10 +16,12 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.nicehttp.NiceResponse import com.lagradost.nicehttp.NiceResponse
import com.lagradost.nicehttp.RequestBodyTypes import com.lagradost.nicehttp.RequestBodyTypes
import com.lagradost.nicehttp.requestCreator
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Headers import okhttp3.Headers
@ -28,10 +29,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.net.URI import java.net.*
import java.net.URL
import java.net.URLDecoder
import java.net.URLEncoder
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.security.MessageDigest import java.security.MessageDigest
import java.security.SecureRandom import java.security.SecureRandom
@ -45,56 +43,13 @@ import kotlin.math.min
val soraAPI = base64DecodeAPI("cA==YXA=cy8=Y20=di8=LnQ=b2s=a2w=bG8=aS4=YXA=ZS0=aWw=b2I=LW0=Z2E=Ly8=czo=dHA=aHQ=") val soraAPI = base64DecodeAPI("cA==YXA=cy8=Y20=di8=LnQ=b2s=a2w=bG8=aS4=YXA=ZS0=aWw=b2I=LW0=Z2E=Ly8=czo=dHA=aHQ=")
val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=")
val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
val kaguyaBaseUrl = "https://kaguya.app/" const val kaguyaBaseUrl = "https://kaguya.app/"
val soraHeaders = mapOf( val soraHeaders = mapOf(
"lang" to "en", "lang" to "en",
"versioncode" to "33", "versioncode" to "33",
"clienttype" to "android_Official", "clienttype" to "android_Official",
"deviceid" to getDeviceId(), "deviceid" to getDeviceId(),
) )
val allanimeSearchQuery = """
query(
${'$'}search: SearchInput
${'$'}limit: Int
${'$'}page: Int
${'$'}translationType: VaildTranslationTypeEnumType
${'$'}countryOrigin: VaildCountryOriginEnumType
) {
shows(
search: ${'$'}search
limit: ${'$'}limit
page: ${'$'}page
translationType: ${'$'}translationType
countryOrigin: ${'$'}countryOrigin
) {
pageInfo {
total
}
edges {
_id
name
thumbnail
englishName
nativeName
}
}
}
""".trimIndent().trim()
val allanimeServerQuery = """
query(
${'$'}showId: String!,
${'$'}translationType: VaildTranslationTypeEnumType!,
${'$'}episodeString: String!
) {
episode(
showId: ${'$'}showId
translationType: ${'$'}translationType
episodeString: ${'$'}episodeString
) {
sourceUrls
}
}
""".trimIndent().trim()
val encodedIndex = arrayOf( val encodedIndex = arrayOf(
"GamMovies", "GamMovies",
"JSMovies", "JSMovies",
@ -574,6 +529,45 @@ suspend fun invokeSmashyNflim(
} }
suspend fun invokeSmashyRip(
name: String,
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val script = app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return
val source = Regex("file:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1)
val subtitle = Regex("subtitle:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1)
source?.split(",")?.map { links ->
val quality = Regex("\\[(\\d+)]").find(links)?.groupValues?.getOrNull(1)?.trim()
val link = links.removePrefix("[$quality]").substringAfter("dev/").trim()
callback.invoke(
ExtractorLink(
"Smashy [$name]",
"Smashy [$name]",
link,
"",
quality?.toIntOrNull() ?: return@map,
isM3u8 = true,
)
)
}
subtitle?.replace("<br>", "")?.split(",")?.map { sub ->
val lang = Regex("\\[(.*?)]").find(sub)?.groupValues?.getOrNull(1)?.trim()
val link = sub.removePrefix("[$lang]")
subtitleCallback.invoke(
SubtitleFile(
lang.orEmpty().ifEmpty { return@map },
link
)
)
}
}
suspend fun getSoraIdAndType(title: String?, year: Int?, season: Int?): Pair<String, String>? { suspend fun getSoraIdAndType(title: String?, year: Int?, season: Int?): Pair<String, String>? {
val doc = val doc =
app.get("${base64DecodeAPI("b20=LmM=b2s=a2w=bG8=Ly8=czo=dHA=aHQ=")}/search?keyword=$title").document app.get("${base64DecodeAPI("b20=LmM=b2s=a2w=bG8=Ly8=czo=dHA=aHQ=")}/search?keyword=$title").document
@ -825,7 +819,7 @@ suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair<St
} }
} }
suspend fun getFilmxyCookies(imdbId: String? = null, season: Int? = null): FilmxyCookies? { suspend fun getFilmxyCookies(imdbId: String? = null, season: Int? = null): FilmxyCookies {
val url = if (season == null) { val url = if (season == null) {
"${filmxyAPI}/movie/$imdbId" "${filmxyAPI}/movie/$imdbId"
@ -905,47 +899,68 @@ suspend fun searchWatchOnline(
) )
} }
suspend fun searchCrunchyrollAnimeId(title: String): String? { //modified code from https://github.com/jmir1/aniyomi-extensions/blob/master/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/AccessTokenInterceptor.kt
val res = app.get("${consumetCrunchyrollAPI}/search/$title", timeout = 600L) fun getCrunchyrollToken(): Map<String, String> {
.parsedSafe<ConsumetSearchResponse>()?.results val client = app.baseClient.newBuilder()
return (if (res?.size == 1) { .proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress("cr-unblocker.us.to", 1080)))
res.firstOrNull() .build()
} else {
res?.find { Authenticator.setDefault(object : Authenticator() {
(it.title?.contains( override fun getPasswordAuthentication(): PasswordAuthentication {
title, return PasswordAuthentication("crunblocker", "crunblocker".toCharArray())
true
) == true || it.title.createSlug()
?.contains("${title.createSlug()}", true) == true) && it.type.equals("series")
} }
})?.id })
val request = requestCreator(
method = "POST",
url = "$crunchyrollAPI/auth/v1/token",
headers = mapOf(
"User-Agent" to "Crunchyroll/3.26.1 Android/11 okhttp/4.9.2",
"Content-Type" to "application/x-www-form-urlencoded",
"Authorization" to "Basic ${BuildConfig.CRUNCHYROLL_BASIC_TOKEN}"
),
data = mapOf(
"refresh_token" to BuildConfig.CRUNCHYROLL_REFRESH_TOKEN,
"grant_type" to "refresh_token",
"scope" to "offline_access"
)
)
val response = tryParseJson<CrunchyrollToken>(client.newCall(request).execute().body.string())
return mapOf("Authorization" to "${response?.tokenType} ${response?.accessToken}")
} }
fun CrunchyrollDetails.findCrunchyrollId( suspend fun getCrunchyrollId(aniId: String?): String? {
season: Int?, val query = """
episode: Int?, query media(${'$'}id: Int, ${'$'}type: MediaType, ${'$'}isAdult: Boolean) {
epsTitle: String? Media(id: ${'$'}id, type: ${'$'}type, isAdult: ${'$'}isAdult) {
): List<Pair<CrunchyrollEpisodes?, String?>?> { id
val sub = this.episodes?.filterKeys { it.contains("subbed") } externalLinks {
.matchingEpisode(epsTitle, season, episode) to "Raw" id
val dub = this.episodes?.filterKeys { it.contains("English Dub") } site
.matchingEpisode(epsTitle, season, episode) to "English Dub" url
return listOf(sub, dub) type
} }
}
}
""".trimIndent().trim()
fun Map<String, List<CrunchyrollEpisodes>>?.matchingEpisode( val variables = mapOf(
epsTitle: String?, "id" to aniId,
season: Int?, "isAdult" to false,
episode: Int? "type" to "ANIME",
): CrunchyrollEpisodes? { )
return this?.mapNotNull { eps ->
eps.value.find { val data = mapOf(
(it.episode_number == episode && it.season_number == season) || it.title.equals( "query" to query,
epsTitle, "variables" to variables
true ).toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
)
} ?: eps.value.find { it.episode_number == episode } return app.post("https://graphql.anilist.co", requestBody = data)
}?.firstOrNull() .parsedSafe<AnilistResponses>()?.data?.Media?.externalLinks?.find { it.site == "VRV" }?.url?.let {
Regex("series/(\\w+)/?").find(it)?.groupValues?.get(1)
}
} }
suspend fun extractPutlockerSources(url: String?): NiceResponse? { suspend fun extractPutlockerSources(url: String?): NiceResponse? {
@ -1120,10 +1135,6 @@ fun String.decryptGomoviesJson(key: String = "123"): String {
return sb.toString() return sb.toString()
} }
fun allanimeQueries(variables: String, query: String) : String {
return "${allanimeAPI}/allanimeapi?variables=$variables&query=$query"
}
fun Headers.getGomoviesCookies(cookieKey: String = "set-cookie"): Map<String, String> { fun Headers.getGomoviesCookies(cookieKey: String = "set-cookie"): Map<String, String> {
val cookieList = val cookieList =
this.filter { it.first.equals(cookieKey, ignoreCase = true) }.mapNotNull { this.filter { it.first.equals(cookieKey, ignoreCase = true) }.mapNotNull {
@ -1292,7 +1303,7 @@ private fun decryptVrf(input: String, key: String): String {
val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) { val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) {
input.replace("""==?$""".toRegex(), "") input.replace("""==?$""".toRegex(), "")
} else input } else input
if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input") if (t.length % 4 == 1 || t.contains("""[^+/\dA-Za-z]""".toRegex())) throw Exception("bad input")
var i: Int var i: Int
var r = "" var r = ""
var e = 0 var e = 0
@ -1359,10 +1370,6 @@ fun getBaseUrl(url: String): String {
} }
} }
fun String.decodeBase64(): String {
return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8)
}
fun decode(input: String): String = URLDecoder.decode(input, "utf-8") fun decode(input: String): String = URLDecoder.decode(input, "utf-8")
fun encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20") fun encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20")
@ -1599,7 +1606,9 @@ object CryptoAES {
object RabbitStream { object RabbitStream {
suspend fun MainAPI.extractRabbitStream( suspend fun MainAPI.extractRabbitStream(
server: String,
url: String, url: String,
ref: String,
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit, callback: (ExtractorLink) -> Unit,
useSidAuthentication: Boolean, useSidAuthentication: Boolean,
@ -1681,8 +1690,8 @@ object RabbitStream {
list.forEach { subList -> list.forEach { subList ->
subList.first?.forEach { source -> subList.first?.forEach { source ->
source?.toExtractorLink( source?.toExtractorLink(
"Vidcloud", server,
"$twoEmbedAPI/", ref,
extractorData, extractorData,
) )
?.forEach { ?.forEach {
@ -1792,11 +1801,15 @@ object RabbitStream {
return code.reversed() return code.reversed()
} }
suspend fun getKey(): String? { suspend fun getKey(): String {
return app.get("https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt") return app.get("https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt")
.text .text
} }
suspend fun getZoroKey(): String {
return app.get("https://raw.githubusercontent.com/enimax-anime/key/e6/key.txt").text
}
private inline fun <reified T> decryptMapped(input: String, key: String): T? { private inline fun <reified T> decryptMapped(input: String, key: String): T? {
return tryParseJson(decrypt(input, key)) return tryParseJson(decrypt(input, key))
} }