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 }}
|
||||
SORAXA: ${{ secrets.SORAXA }}
|
||||
SORATED: ${{ secrets.SORATED }}
|
||||
CRUNCHYROLL_BASIC_TOKEN: ${{ secrets.CRUNCHYROLL_BASIC_TOKEN }}
|
||||
CRUNCHYROLL_REFRESH_TOKEN: ${{ secrets.CRUNCHYROLL_REFRESH_TOKEN }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/src
|
||||
echo SORA_API=$SORA_API >> local.properties
|
||||
echo SORAHE=$SORAHE >> local.properties
|
||||
echo SORAXA=$SORAXA >> 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
|
||||
run: |
|
||||
|
|
|
@ -12,6 +12,8 @@ android {
|
|||
buildConfigField("String", "SORAHE", "\"${properties.getProperty("SORAHE")}\"")
|
||||
buildConfigField("String", "SORAXA", "\"${properties.getProperty("SORAXA")}\"")
|
||||
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
|
||||
if (link.contains("rabbitstream")) {
|
||||
extractRabbitStream(
|
||||
"Vidcloud",
|
||||
link,
|
||||
"$twoEmbedAPI/",
|
||||
subtitleCallback,
|
||||
callback,
|
||||
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(
|
||||
title: String? = null,
|
||||
season: Int? = null,
|
||||
|
@ -327,14 +314,13 @@ object SoraExtractor : SoraStream() {
|
|||
}, {
|
||||
val iv = "9225679083961858"
|
||||
val secretKey = "25742532592138496744665879883281"
|
||||
val secretDecryptKey = secretKey
|
||||
GogoHelper.extractVidstream(
|
||||
iframe.url,
|
||||
"Vidstream",
|
||||
callback,
|
||||
iv,
|
||||
secretKey,
|
||||
secretDecryptKey,
|
||||
secretKey,
|
||||
isUsingAdaptiveKeys = false,
|
||||
isUsingAdaptiveData = true,
|
||||
iframeDocument = iframeDoc
|
||||
|
@ -496,13 +482,12 @@ object SoraExtractor : SoraStream() {
|
|||
} else {
|
||||
"${filmxyAPI}/tv/$imdbId"
|
||||
}
|
||||
val filmxyCookies =
|
||||
getFilmxyCookies(imdbId, season) ?: throw ErrorLoadingException("No Cookies Found")
|
||||
val filmxyCookies = getFilmxyCookies(imdbId, season)
|
||||
|
||||
val cookiesDoc = mapOf(
|
||||
"G_ENABLED_IDPS" to "google",
|
||||
"wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to "${filmxyCookies.wLog}",
|
||||
"PHPSESSID" to "${filmxyCookies.phpsessid}"
|
||||
"wordpress_logged_in_8bf9d5433ac88cc9a3a396d6b154cd01" to (filmxyCookies.wLog ?: return),
|
||||
"PHPSESSID" to (filmxyCookies.phpsessid ?: return)
|
||||
)
|
||||
|
||||
val doc = session.get(url, cookies = cookiesDoc).document
|
||||
|
@ -868,85 +853,10 @@ object SoraExtractor : SoraStream() {
|
|||
{
|
||||
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(
|
||||
|
@ -999,41 +909,47 @@ object SoraExtractor : SoraStream() {
|
|||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val episodeId = app.get("$consumetAnilistAPI/info/$aniId?provider=zoro")
|
||||
.parsedSafe<ConsumetDetails>()?.episodes?.find {
|
||||
it.number == (episode ?: 1)
|
||||
}?.id?.substringBeforeLast("$") ?: return
|
||||
val animeId =
|
||||
app.get("https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/anilist/anime/$aniId.json")
|
||||
.parsedSafe<MALSyncResponses>()?.pages?.zoro?.keys?.firstOrNull()
|
||||
|
||||
listOf(
|
||||
"$episodeId\$sub" to "Raw",
|
||||
"$episodeId\$dub" to "English Dub",
|
||||
).apmap { (id, type) ->
|
||||
val sources = app.get("$consumetZoroAPI/watch?episodeId=$id")
|
||||
.parsedSafe<ConsumetSourcesResponse>() ?: return@apmap null
|
||||
val episodeId = app.get("$zoroAPI/ajax/v2/episode/list/${animeId ?: return}")
|
||||
.parsedSafe<ZoroResponses>()?.html?.let {
|
||||
Jsoup.parse(it)
|
||||
}?.select("div.ss-list a")?.find { it.attr("data-number") == "$episode" }
|
||||
?.attr("data-id")
|
||||
|
||||
sources.sources?.map sources@{
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
"Zoro [$type]",
|
||||
"Zoro [$type]",
|
||||
it.url ?: return@sources null,
|
||||
"",
|
||||
getQualityFromName(it.quality),
|
||||
it.isM3U8 ?: true
|
||||
)
|
||||
val servers = app.get("$zoroAPI/ajax/v2/episode/servers?episodeId=${episodeId ?: return}")
|
||||
.parsedSafe<ZoroResponses>()?.html?.let { Jsoup.parse(it) }
|
||||
?.select("div.item.server-item")?.map {
|
||||
Triple(
|
||||
it.text(),
|
||||
it.attr("data-id"),
|
||||
it.attr("data-type"),
|
||||
)
|
||||
}
|
||||
|
||||
sources.subtitles?.map subtitles@{
|
||||
subtitleCallback.invoke(
|
||||
SubtitleFile(
|
||||
it.lang ?: "",
|
||||
it.url ?: return@subtitles null
|
||||
)
|
||||
)
|
||||
servers?.apmap { server ->
|
||||
val iframe = app.get("$zoroAPI/ajax/v2/episode/sources?id=${server.second ?: return@apmap}")
|
||||
.parsedSafe<ZoroResponses>()?.link ?: return@apmap
|
||||
val audio = if(server.third == "sub") "Raw" else "English Dub"
|
||||
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(
|
||||
|
@ -1551,52 +1467,78 @@ object SoraExtractor : SoraStream() {
|
|||
}
|
||||
|
||||
suspend fun invokeCrunchyroll(
|
||||
title: String? = null,
|
||||
aniId: String? = null,
|
||||
epsTitle: String? = null,
|
||||
season: Int? = null,
|
||||
episode: Int? = null,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val id = searchCrunchyrollAnimeId(title ?: return) ?: return
|
||||
val detail =
|
||||
app.get("$consumetCrunchyrollAPI/info/$id?fetchAllSeasons=true", timeout = 600L).text
|
||||
val epsId = tryParseJson<CrunchyrollDetails>(detail)?.findCrunchyrollId(
|
||||
season,
|
||||
episode,
|
||||
epsTitle
|
||||
val id = getCrunchyrollId(aniId) ?: return
|
||||
val audioLocal = listOf(
|
||||
"ja-JP",
|
||||
"en-US",
|
||||
"zh-CN",
|
||||
)
|
||||
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 {
|
||||
delay(2000)
|
||||
val json =
|
||||
seasonId?.apmap { (sId, audioL) ->
|
||||
val streamsLink =
|
||||
app.get(
|
||||
"$consumetCrunchyrollAPI/watch/${it?.first?.id ?: return@apmap null}",
|
||||
timeout = 600L
|
||||
)
|
||||
.parsedSafe<ConsumetSourcesResponse>()
|
||||
|
||||
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,
|
||||
"$crunchyrollAPI/content/v2/cms/seasons/${sId ?: return@apmap}/episodes",
|
||||
headers = headers
|
||||
).parsedSafe<CrunchyrollResponses>()?.data?.find {
|
||||
it.title.equals(epsTitle, true) || it.slug_title.equals(
|
||||
epsTitle.createSlug(),
|
||||
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(
|
||||
SubtitleFile(
|
||||
"${fixCrunchyrollLang(sub.lang ?: return@subtitle null) ?: sub.lang} [ass]",
|
||||
sub.url ?: return@subtitle null
|
||||
"${fixCrunchyrollLang(sub.key) ?: sub.key} [ass]",
|
||||
sub.value["url"] ?: return@map null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1900,6 +1842,7 @@ object SoraExtractor : SoraStream() {
|
|||
imdbId: String? = null,
|
||||
season: Int? = null,
|
||||
episode: Int? = null,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit,
|
||||
) {
|
||||
val url = if (season == null) {
|
||||
|
@ -1927,6 +1870,9 @@ object SoraExtractor : SoraStream() {
|
|||
it.first.contains("/nflim") -> {
|
||||
invokeSmashyNflim(it.second, it.first, callback)
|
||||
}
|
||||
it.first.contains("/rip") -> {
|
||||
invokeSmashyRip(it.second, it.first, subtitleCallback, callback)
|
||||
}
|
||||
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(
|
||||
apiUrl: String,
|
||||
api: String,
|
||||
|
@ -2700,7 +2606,7 @@ object SoraExtractor : SoraStream() {
|
|||
season: Int? = null,
|
||||
callback: (ExtractorLink) -> Unit,
|
||||
) {
|
||||
val monsterMainUrl = "https://freedoze.monster"
|
||||
val monsterMainUrl = "https://mobirs.monster"
|
||||
val playSlug = if (season == null) {
|
||||
"movies/play/$urlSlug"
|
||||
} else {
|
||||
|
@ -2713,7 +2619,7 @@ object SoraExtractor : SoraStream() {
|
|||
val res = app.get(url).document
|
||||
val script = res.selectFirst("script:containsData(window['show_storage'])")?.data()
|
||||
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) {
|
||||
"$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,
|
||||
)
|
||||
|
||||
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(
|
||||
@JsonProperty("Video") val video: String?,
|
||||
@JsonProperty("ThirdParty") val thirdParty: String?,
|
||||
|
@ -3246,23 +3087,6 @@ data class SorastreamVideos(
|
|||
@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(
|
||||
@JsonProperty("id") val id: Int? = null,
|
||||
@JsonProperty("sourceId") val sourceId: String? = null,
|
||||
|
@ -3325,74 +3149,6 @@ data class PutlockerResponses(
|
|||
@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(
|
||||
@JsonProperty("id") val id: String? = null,
|
||||
@JsonProperty("stream_link") val stream_link: String? = null,
|
||||
|
@ -3449,3 +3205,73 @@ data class VizcloudData(
|
|||
data class VizcloudResponses(
|
||||
@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.invokeBollyMaza
|
||||
import com.hexated.SoraExtractor.invokeCodexmovies
|
||||
import com.hexated.SoraExtractor.invokeCrunchyroll
|
||||
import com.hexated.SoraExtractor.invokeCryMovies
|
||||
import com.hexated.SoraExtractor.invokeDbgo
|
||||
import com.hexated.SoraExtractor.invokeFilmxy
|
||||
|
@ -17,7 +16,6 @@ import com.hexated.SoraExtractor.invokeMovieHab
|
|||
import com.hexated.SoraExtractor.invokeNoverse
|
||||
import com.hexated.SoraExtractor.invokeSeries9
|
||||
import com.hexated.SoraExtractor.invokeTwoEmbed
|
||||
import com.hexated.SoraExtractor.invokeUniqueStream
|
||||
import com.hexated.SoraExtractor.invokeVidSrc
|
||||
import com.hexated.SoraExtractor.invokeXmovies
|
||||
import com.lagradost.cloudstream3.*
|
||||
|
@ -41,8 +39,6 @@ import com.hexated.SoraExtractor.invokeMoviesbay
|
|||
import com.hexated.SoraExtractor.invokeMoviezAdd
|
||||
import com.hexated.SoraExtractor.invokeNinetv
|
||||
import com.hexated.SoraExtractor.invokeNowTv
|
||||
import com.hexated.SoraExtractor.invokePapaonMovies1
|
||||
import com.hexated.SoraExtractor.invokePapaonMovies2
|
||||
import com.hexated.SoraExtractor.invokePutlocker
|
||||
import com.hexated.SoraExtractor.invokeRStream
|
||||
import com.hexated.SoraExtractor.invokeRinzrymovies
|
||||
|
@ -51,7 +47,6 @@ import com.hexated.SoraExtractor.invokeShinobiMovies
|
|||
import com.hexated.SoraExtractor.invokeShivamhw
|
||||
import com.hexated.SoraExtractor.invokeSmashyStream
|
||||
import com.hexated.SoraExtractor.invokeSoraStream
|
||||
import com.hexated.SoraExtractor.invokeTgarMovies
|
||||
import com.hexated.SoraExtractor.invokeTvMovies
|
||||
import com.hexated.SoraExtractor.invokeUhdmovies
|
||||
import com.hexated.SoraExtractor.invokeVitoenMovies
|
||||
|
@ -81,17 +76,14 @@ open class SoraStream : TmdbProvider() {
|
|||
private const val tmdbAPI = "https://api.themoviedb.org/3"
|
||||
const val tmdb2anilist = "https://tmdb2anilist.slidemovies.org"
|
||||
const val gdbot = "https://gdtot.pro"
|
||||
const val consumetAnilistAPI = "https://api.consumet.org/meta/anilist"
|
||||
|
||||
private val apiKey =
|
||||
base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL
|
||||
private val apiKey = base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL
|
||||
|
||||
/** ALL SOURCES */
|
||||
const val twoEmbedAPI = "https://www.2embed.to"
|
||||
const val vidSrcAPI = "https://v2.vidsrc.me"
|
||||
const val dbgoAPI = "https://dbgo.fun"
|
||||
const val movieHabAPI = "https://moviehab.com"
|
||||
const val databaseGdriveAPI = "https://databasegdriveplayer.co"
|
||||
const val hdMovieBoxAPI = "https://hdmoviebox.net"
|
||||
const val series9API = "https://series9.sh"
|
||||
const val idlixAPI = "https://idlixian.com"
|
||||
|
@ -100,11 +92,11 @@ open class SoraStream : TmdbProvider() {
|
|||
const val filmxyAPI = "https://www.filmxy.vip"
|
||||
const val kimcartoonAPI = "https://kimcartoon.li"
|
||||
const val xMovieAPI = "https://xemovies.to"
|
||||
const val consumetZoroAPI = "https://api.consumet.org/anime/zoro"
|
||||
const val allanimeAPI = "https://api.allanime.to"
|
||||
const val zoroAPI = "https://sanji.to"
|
||||
const val crunchyrollAPI = "https://beta-api.crunchyroll.com"
|
||||
const val kissKhAPI = "https://kisskh.co"
|
||||
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 gMoviesAPI = "https://gdrivemovies.xyz"
|
||||
const val fdMoviesAPI = "https://freedrivemovie.lol"
|
||||
|
@ -600,7 +592,7 @@ open class SoraStream : TmdbProvider() {
|
|||
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(
|
||||
|
|
|
@ -167,7 +167,7 @@ class SoraStreamLite : SoraStream() {
|
|||
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(
|
||||
|
|
|
@ -2,10 +2,9 @@ package com.hexated
|
|||
|
||||
import android.util.Base64
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.hexated.SoraStream.Companion.allanimeAPI
|
||||
import com.hexated.SoraStream.Companion.base64DecodeAPI
|
||||
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.gdbot
|
||||
import com.hexated.SoraStream.Companion.putlockerAPI
|
||||
|
@ -17,10 +16,12 @@ import com.lagradost.cloudstream3.*
|
|||
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
|
||||
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.nicehttp.NiceResponse
|
||||
import com.lagradost.nicehttp.RequestBodyTypes
|
||||
import com.lagradost.nicehttp.requestCreator
|
||||
import kotlinx.coroutines.delay
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
|
@ -28,10 +29,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
|
|||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.jsoup.nodes.Document
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
import java.net.*
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.MessageDigest
|
||||
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 bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=")
|
||||
val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
val kaguyaBaseUrl = "https://kaguya.app/"
|
||||
const val kaguyaBaseUrl = "https://kaguya.app/"
|
||||
val soraHeaders = mapOf(
|
||||
"lang" to "en",
|
||||
"versioncode" to "33",
|
||||
"clienttype" to "android_Official",
|
||||
"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(
|
||||
"GamMovies",
|
||||
"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>? {
|
||||
val doc =
|
||||
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) {
|
||||
"${filmxyAPI}/movie/$imdbId"
|
||||
|
@ -905,47 +899,68 @@ suspend fun searchWatchOnline(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun searchCrunchyrollAnimeId(title: String): String? {
|
||||
val res = app.get("${consumetCrunchyrollAPI}/search/$title", timeout = 600L)
|
||||
.parsedSafe<ConsumetSearchResponse>()?.results
|
||||
return (if (res?.size == 1) {
|
||||
res.firstOrNull()
|
||||
} else {
|
||||
res?.find {
|
||||
(it.title?.contains(
|
||||
title,
|
||||
true
|
||||
) == true || it.title.createSlug()
|
||||
?.contains("${title.createSlug()}", true) == true) && it.type.equals("series")
|
||||
//modified code from https://github.com/jmir1/aniyomi-extensions/blob/master/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/AccessTokenInterceptor.kt
|
||||
fun getCrunchyrollToken(): Map<String, String> {
|
||||
val client = app.baseClient.newBuilder()
|
||||
.proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress("cr-unblocker.us.to", 1080)))
|
||||
.build()
|
||||
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||
return PasswordAuthentication("crunblocker", "crunblocker".toCharArray())
|
||||
}
|
||||
})?.id
|
||||
}
|
||||
})
|
||||
|
||||
fun CrunchyrollDetails.findCrunchyrollId(
|
||||
season: Int?,
|
||||
episode: Int?,
|
||||
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(
|
||||
epsTitle: String?,
|
||||
season: Int?,
|
||||
episode: Int?
|
||||
): CrunchyrollEpisodes? {
|
||||
return this?.mapNotNull { eps ->
|
||||
eps.value.find {
|
||||
(it.episode_number == episode && it.season_number == season) || it.title.equals(
|
||||
epsTitle,
|
||||
true
|
||||
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"
|
||||
)
|
||||
} ?: 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? {
|
||||
|
@ -1120,10 +1135,6 @@ fun String.decryptGomoviesJson(key: String = "123"): String {
|
|||
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> {
|
||||
val cookieList =
|
||||
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) {
|
||||
input.replace("""==?$""".toRegex(), "")
|
||||
} 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 r = ""
|
||||
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 encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20")
|
||||
|
@ -1599,7 +1606,9 @@ object CryptoAES {
|
|||
object RabbitStream {
|
||||
|
||||
suspend fun MainAPI.extractRabbitStream(
|
||||
server: String,
|
||||
url: String,
|
||||
ref: String,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit,
|
||||
useSidAuthentication: Boolean,
|
||||
|
@ -1681,8 +1690,8 @@ object RabbitStream {
|
|||
list.forEach { subList ->
|
||||
subList.first?.forEach { source ->
|
||||
source?.toExtractorLink(
|
||||
"Vidcloud",
|
||||
"$twoEmbedAPI/",
|
||||
server,
|
||||
ref,
|
||||
extractorData,
|
||||
)
|
||||
?.forEach {
|
||||
|
@ -1792,11 +1801,15 @@ object RabbitStream {
|
|||
return code.reversed()
|
||||
}
|
||||
|
||||
suspend fun getKey(): String? {
|
||||
suspend fun getKey(): String {
|
||||
return app.get("https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt")
|
||||
.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? {
|
||||
return tryParseJson(decrypt(input, key))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue