mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
sora: fixed Crunchyroll and Zoro
This commit is contained in:
parent
92803be98d
commit
93c948bb8e
6 changed files with 298 additions and 461 deletions
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
@ -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: |
|
||||||
|
|
|
@ -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")}\"")
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -3449,3 +3205,73 @@ 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,
|
||||||
|
)
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 {
|
|
||||||
(it.title?.contains(
|
|
||||||
title,
|
|
||||||
true
|
|
||||||
) == true || it.title.createSlug()
|
|
||||||
?.contains("${title.createSlug()}", true) == true) && it.type.equals("series")
|
|
||||||
}
|
|
||||||
})?.id
|
|
||||||
}
|
|
||||||
|
|
||||||
fun CrunchyrollDetails.findCrunchyrollId(
|
Authenticator.setDefault(object : Authenticator() {
|
||||||
season: Int?,
|
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||||
episode: Int?,
|
return PasswordAuthentication("crunblocker", "crunblocker".toCharArray())
|
||||||
epsTitle: String?
|
|
||||||
): List<Pair<CrunchyrollEpisodes?, String?>?> {
|
|
||||||
val sub = this.episodes?.filterKeys { it.contains("subbed") }
|
|
||||||
.matchingEpisode(epsTitle, season, episode) to "Raw"
|
|
||||||
val dub = this.episodes?.filterKeys { it.contains("English Dub") }
|
|
||||||
.matchingEpisode(epsTitle, season, episode) to "English Dub"
|
|
||||||
return listOf(sub, dub)
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
fun Map<String, List<CrunchyrollEpisodes>>?.matchingEpisode(
|
val request = requestCreator(
|
||||||
epsTitle: String?,
|
method = "POST",
|
||||||
season: Int?,
|
url = "$crunchyrollAPI/auth/v1/token",
|
||||||
episode: Int?
|
headers = mapOf(
|
||||||
): CrunchyrollEpisodes? {
|
"User-Agent" to "Crunchyroll/3.26.1 Android/11 okhttp/4.9.2",
|
||||||
return this?.mapNotNull { eps ->
|
"Content-Type" to "application/x-www-form-urlencoded",
|
||||||
eps.value.find {
|
"Authorization" to "Basic ${BuildConfig.CRUNCHYROLL_BASIC_TOKEN}"
|
||||||
(it.episode_number == episode && it.season_number == season) || it.title.equals(
|
),
|
||||||
epsTitle,
|
data = mapOf(
|
||||||
true
|
"refresh_token" to BuildConfig.CRUNCHYROLL_REFRESH_TOKEN,
|
||||||
|
"grant_type" to "refresh_token",
|
||||||
|
"scope" to "offline_access"
|
||||||
)
|
)
|
||||||
} ?: eps.value.find { it.episode_number == episode }
|
)
|
||||||
}?.firstOrNull()
|
|
||||||
|
val response = tryParseJson<CrunchyrollToken>(client.newCall(request).execute().body.string())
|
||||||
|
return mapOf("Authorization" to "${response?.tokenType} ${response?.accessToken}")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getCrunchyrollId(aniId: String?): String? {
|
||||||
|
val query = """
|
||||||
|
query media(${'$'}id: Int, ${'$'}type: MediaType, ${'$'}isAdult: Boolean) {
|
||||||
|
Media(id: ${'$'}id, type: ${'$'}type, isAdult: ${'$'}isAdult) {
|
||||||
|
id
|
||||||
|
externalLinks {
|
||||||
|
id
|
||||||
|
site
|
||||||
|
url
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent().trim()
|
||||||
|
|
||||||
|
val variables = mapOf(
|
||||||
|
"id" to aniId,
|
||||||
|
"isAdult" to false,
|
||||||
|
"type" to "ANIME",
|
||||||
|
)
|
||||||
|
|
||||||
|
val data = mapOf(
|
||||||
|
"query" to query,
|
||||||
|
"variables" to variables
|
||||||
|
).toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
|
||||||
|
|
||||||
|
return app.post("https://graphql.anilist.co", requestBody = data)
|
||||||
|
.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))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue