mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
sora: fixed Crunchyroll
This commit is contained in:
parent
f7c648455b
commit
09df0b9aa8
5 changed files with 147 additions and 4 deletions
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 96
|
version = 97
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -1573,6 +1573,65 @@ object SoraExtractor : SoraStream() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun invokeKickassanime(
|
||||||
|
title: String? = null,
|
||||||
|
epsTitle: String? = null,
|
||||||
|
season: Int? = null,
|
||||||
|
episode: Int? = null,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val body = """{"query":"$title"}""".toRequestBody(
|
||||||
|
RequestBodyTypes.JSON.toMediaTypeOrNull()
|
||||||
|
)
|
||||||
|
val animeId = app.post(
|
||||||
|
"$kickassanimeAPI/api/search", requestBody = body
|
||||||
|
).text.let { tryParseJson<List<KaaSearchResponse>>(it) }.let { res ->
|
||||||
|
(if (res?.size == 1) {
|
||||||
|
res.firstOrNull()
|
||||||
|
} else {
|
||||||
|
res?.find {
|
||||||
|
it.title?.equals(
|
||||||
|
title,
|
||||||
|
true
|
||||||
|
) == true || it.title.createSlug()
|
||||||
|
?.equals("${title.createSlug()}", true) == true
|
||||||
|
}
|
||||||
|
})?._id
|
||||||
|
} ?: return
|
||||||
|
|
||||||
|
val seasonData =
|
||||||
|
app.get("$kickassanimeAPI/api/season/$animeId").text.let { tryParseJson<List<KaaSeason>>(it) }?.find {
|
||||||
|
val seasonNumber = when (title) {
|
||||||
|
"One Piece" -> 13
|
||||||
|
"Hunter x Hunter" -> 5
|
||||||
|
else -> season
|
||||||
|
}
|
||||||
|
it.number == seasonNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
val language = seasonData?.languages?.filter {
|
||||||
|
it == "ja-JP" || it == "en-US"
|
||||||
|
}
|
||||||
|
|
||||||
|
language?.apmap { lang ->
|
||||||
|
val episodeSlug =
|
||||||
|
app.get("$kickassanimeAPI/api/episodes/${seasonData.id}?lh=$lang&page=1")
|
||||||
|
.parsedSafe<KaaEpisodeResults>()?.result?.find { eps ->
|
||||||
|
eps.episodeNumber == episode || eps.slug?.contains("${epsTitle.createSlug()}", true) == true
|
||||||
|
}?.slug ?: return@apmap
|
||||||
|
|
||||||
|
val server = app.get("$kickassanimeAPI/api/watch/$episodeSlug").parsedSafe<KaaServers>()?.servers?.find {
|
||||||
|
it.contains("/sapphire-duck/")
|
||||||
|
} ?: return@apmap
|
||||||
|
|
||||||
|
invokeSapphire(server, lang == "en-US", subtitleCallback, callback)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun invokeMoviesbay(
|
suspend fun invokeMoviesbay(
|
||||||
title: String? = null,
|
title: String? = null,
|
||||||
year: Int? = null,
|
year: Int? = null,
|
||||||
|
@ -2922,3 +2981,44 @@ data class SorastreamVideos(
|
||||||
@JsonProperty("mediaUrl") val mediaUrl: String? = null,
|
@JsonProperty("mediaUrl") val mediaUrl: String? = null,
|
||||||
@JsonProperty("currentDefinition") val currentDefinition: String? = null,
|
@JsonProperty("currentDefinition") val currentDefinition: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class KaaServers(
|
||||||
|
@JsonProperty("servers") val servers: ArrayList<String>? = arrayListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class KaaEpisode(
|
||||||
|
@JsonProperty("episodeNumber") val episodeNumber: Int? = null,
|
||||||
|
@JsonProperty("slug") val slug: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class KaaEpisodeResults(
|
||||||
|
@JsonProperty("result") val result: ArrayList<KaaEpisode>? = arrayListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class KaaSeason(
|
||||||
|
@JsonProperty("id") val id: String? = null,
|
||||||
|
@JsonProperty("number") val number: Int? = null,
|
||||||
|
@JsonProperty("languages") val languages: ArrayList<String>? = arrayListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class KaaSearchResponse(
|
||||||
|
@JsonProperty("_id") val _id: String? = null,
|
||||||
|
@JsonProperty("title") val title: 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(),
|
||||||
|
)
|
|
@ -32,6 +32,7 @@ import com.hexated.SoraExtractor.invokeGMovies
|
||||||
import com.hexated.SoraExtractor.invokeGomovies
|
import com.hexated.SoraExtractor.invokeGomovies
|
||||||
import com.hexated.SoraExtractor.invokeJmdkhMovies
|
import com.hexated.SoraExtractor.invokeJmdkhMovies
|
||||||
import com.hexated.SoraExtractor.invokeJsmovies
|
import com.hexated.SoraExtractor.invokeJsmovies
|
||||||
|
import com.hexated.SoraExtractor.invokeKickassanime
|
||||||
import com.hexated.SoraExtractor.invokeKisskh
|
import com.hexated.SoraExtractor.invokeKisskh
|
||||||
import com.hexated.SoraExtractor.invokeLing
|
import com.hexated.SoraExtractor.invokeLing
|
||||||
import com.hexated.SoraExtractor.invokeM4uhd
|
import com.hexated.SoraExtractor.invokeM4uhd
|
||||||
|
@ -98,7 +99,8 @@ open class SoraStream : TmdbProvider() {
|
||||||
const val xMovieAPI = "https://xemovies.to"
|
const val xMovieAPI = "https://xemovies.to"
|
||||||
const val consumetFlixhqAPI = "https://api.consumet.org/movies/flixhq"
|
const val consumetFlixhqAPI = "https://api.consumet.org/movies/flixhq"
|
||||||
const val consumetZoroAPI = "https://api.consumet.org/anime/zoro"
|
const val consumetZoroAPI = "https://api.consumet.org/anime/zoro"
|
||||||
const val consumetCrunchyrollAPI = "https://api.consumet.org/anime/crunchyroll"
|
const val consumetCrunchyrollAPI = "https://api.consumet.org/anime/crunchyroll" // dead
|
||||||
|
const val kickassanimeAPI = "https://www2.kickassanime.ro"
|
||||||
const val kissKhAPI = "https://kisskh.me"
|
const val kissKhAPI = "https://kisskh.me"
|
||||||
const val lingAPI = "https://ling-online.net"
|
const val lingAPI = "https://ling-online.net"
|
||||||
const val uhdmoviesAPI = "https://uhdmovies.world"
|
const val uhdmoviesAPI = "https://uhdmovies.world"
|
||||||
|
@ -405,7 +407,7 @@ open class SoraStream : TmdbProvider() {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
if (res.season != null && res.isAnime) invokeCrunchyroll(
|
if (res.season != null && res.isAnime) invokeKickassanime(
|
||||||
res.title,
|
res.title,
|
||||||
res.epsTitle,
|
res.epsTitle,
|
||||||
res.season,
|
res.season,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.hexated.SoraExtractor.invokeFwatayako
|
||||||
import com.hexated.SoraExtractor.invokeGomovies
|
import com.hexated.SoraExtractor.invokeGomovies
|
||||||
import com.hexated.SoraExtractor.invokeHDMovieBox
|
import com.hexated.SoraExtractor.invokeHDMovieBox
|
||||||
import com.hexated.SoraExtractor.invokeIdlix
|
import com.hexated.SoraExtractor.invokeIdlix
|
||||||
|
import com.hexated.SoraExtractor.invokeKickassanime
|
||||||
import com.hexated.SoraExtractor.invokeKimcartoon
|
import com.hexated.SoraExtractor.invokeKimcartoon
|
||||||
import com.hexated.SoraExtractor.invokeKisskh
|
import com.hexated.SoraExtractor.invokeKisskh
|
||||||
import com.hexated.SoraExtractor.invokeLing
|
import com.hexated.SoraExtractor.invokeLing
|
||||||
|
@ -107,7 +108,7 @@ class SoraStreamLite : SoraStream() {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
if (res.season != null && res.isAnime) invokeCrunchyroll(
|
if (res.season != null && res.isAnime) invokeKickassanime(
|
||||||
res.title,
|
res.title,
|
||||||
res.epsTitle,
|
res.epsTitle,
|
||||||
res.season,
|
res.season,
|
||||||
|
|
|
@ -506,6 +506,42 @@ suspend fun fetchSoraEpisodes(id: String, type: String, episode: Int?) : Episode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun invokeSapphire(
|
||||||
|
url: String? = null,
|
||||||
|
isDub: Boolean = false,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit,
|
||||||
|
) {
|
||||||
|
var data = app.get("${url?.replace("player.php", "config.php")}&action=config", referer = url).text
|
||||||
|
while (true) {
|
||||||
|
if (data.startsWith("{")) break
|
||||||
|
if (data == "null") {
|
||||||
|
data = app.get("$url&action=config", referer = url).text
|
||||||
|
delay(1000)
|
||||||
|
}
|
||||||
|
data = data.decodeBase64()
|
||||||
|
}
|
||||||
|
|
||||||
|
tryParseJson<SapphireSources>(data).let { res ->
|
||||||
|
res?.streams?.filter { it.format == "adaptive_hls" && it.hardsub_lang.isNullOrEmpty() }?.reversed()?.map { source ->
|
||||||
|
val name = if (isDub) "English Dub" else "Raw"
|
||||||
|
M3u8Helper.generateM3u8(
|
||||||
|
"Crunchyroll [$name]",
|
||||||
|
source.url ?: return@map null,
|
||||||
|
"https://static.crunchyroll.com/",
|
||||||
|
).forEach(callback)
|
||||||
|
}
|
||||||
|
res?.subtitles?.map { sub ->
|
||||||
|
subtitleCallback.invoke(
|
||||||
|
SubtitleFile(
|
||||||
|
getLanguage(sub.language ?: return@map null),
|
||||||
|
sub.url ?: return@map null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun bypassOuo(url: String?): String? {
|
suspend fun bypassOuo(url: String?): String? {
|
||||||
var res = session.get(url ?: return null)
|
var res = session.get(url ?: return null)
|
||||||
run lit@{
|
run lit@{
|
||||||
|
@ -951,6 +987,10 @@ fun getBaseUrl(url: String): String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun String.decodeBase64(): String {
|
||||||
|
return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8)
|
||||||
|
}
|
||||||
|
|
||||||
fun encode(input: String): String? = URLEncoder.encode(input, "utf-8")
|
fun encode(input: String): String? = URLEncoder.encode(input, "utf-8")
|
||||||
|
|
||||||
fun decryptStreamUrl(data: String): String {
|
fun decryptStreamUrl(data: String): String {
|
||||||
|
|
Loading…
Reference in a new issue