mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
9anime by Stormunblessed + minor code changes
This commit is contained in:
parent
73bcb4145e
commit
428e97ab1c
7 changed files with 331 additions and 43 deletions
|
@ -84,6 +84,7 @@ object APIHolder {
|
||||||
KdramaHoodProvider(),
|
KdramaHoodProvider(),
|
||||||
AkwamProvider(),
|
AkwamProvider(),
|
||||||
AnimePaheProvider(),
|
AnimePaheProvider(),
|
||||||
|
NineAnimeProvider(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val restrictedApis = arrayListOf(
|
val restrictedApis = arrayListOf(
|
||||||
|
|
|
@ -87,13 +87,13 @@ class AllAnimeProvider : MainAPI() {
|
||||||
@JsonProperty("data") val data: Data
|
@JsonProperty("data") val data: Data
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun search(query: String): ArrayList<SearchResponse> {
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
val link =
|
val link =
|
||||||
"""$mainUrl/graphql?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22query%22%3A%22$query%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229343797cc3d9e3f444e2d3b7db9a84d759b816a4d84512ea72d079f85bb96e98%22%7D%7D"""
|
"""$mainUrl/graphql?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22query%22%3A%22$query%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229343797cc3d9e3f444e2d3b7db9a84d759b816a4d84512ea72d079f85bb96e98%22%7D%7D"""
|
||||||
var res = app.get(link).text
|
var res = app.get(link).text
|
||||||
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) {
|
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) {
|
||||||
res = app.get(link).text
|
res = app.get(link).text
|
||||||
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return ArrayList()
|
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return emptyList()
|
||||||
}
|
}
|
||||||
val response = mapper.readValue<AllAnimeQuery>(res)
|
val response = mapper.readValue<AllAnimeQuery>(res)
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ class AllAnimeProvider : MainAPI() {
|
||||||
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
|
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ArrayList(results.map {
|
return results.map {
|
||||||
AnimeSearchResponse(
|
AnimeSearchResponse(
|
||||||
it.name,
|
it.name,
|
||||||
"$mainUrl/anime/${it.Id}",
|
"$mainUrl/anime/${it.Id}",
|
||||||
|
@ -115,7 +115,7 @@ class AllAnimeProvider : MainAPI() {
|
||||||
it.availableEpisodes?.dub,
|
it.availableEpisodes?.dub,
|
||||||
it.availableEpisodes?.sub
|
it.availableEpisodes?.sub
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class AvailableEpisodesDetail(
|
private data class AvailableEpisodesDetail(
|
||||||
|
@ -154,11 +154,11 @@ class AllAnimeProvider : MainAPI() {
|
||||||
|
|
||||||
val episodes = showData.availableEpisodes.let {
|
val episodes = showData.availableEpisodes.let {
|
||||||
if (it == null) return@let Pair(null, null)
|
if (it == null) return@let Pair(null, null)
|
||||||
Pair(if (it.sub != 0) ArrayList((1..it.sub).map { epNum ->
|
Pair(if (it.sub != 0) ((1..it.sub).map { epNum ->
|
||||||
AnimeEpisode(
|
AnimeEpisode(
|
||||||
"$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum
|
"$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum
|
||||||
)
|
)
|
||||||
}) else null, if (it.dub != 0) ArrayList((1..it.dub).map { epNum ->
|
}) else null, if (it.dub != 0) ((1..it.dub).map { epNum ->
|
||||||
AnimeEpisode(
|
AnimeEpisode(
|
||||||
"$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum
|
"$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum
|
||||||
)
|
)
|
||||||
|
@ -251,10 +251,9 @@ class AllAnimeProvider : MainAPI() {
|
||||||
private fun getM3u8Qualities(
|
private fun getM3u8Qualities(
|
||||||
m3u8Link: String,
|
m3u8Link: String,
|
||||||
referer: String,
|
referer: String,
|
||||||
qualityName: String
|
qualityName: String,
|
||||||
): ArrayList<ExtractorLink> {
|
): List<ExtractorLink> {
|
||||||
return ArrayList(
|
return hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(m3u8Link, null), true).map { stream ->
|
||||||
hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(m3u8Link, null), true).map { stream ->
|
|
||||||
val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p"
|
val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p"
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
this.name,
|
this.name,
|
||||||
|
@ -265,7 +264,7 @@ class AllAnimeProvider : MainAPI() {
|
||||||
true,
|
true,
|
||||||
stream.headers
|
stream.headers
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun loadLinks(
|
override suspend fun loadLinks(
|
||||||
|
|
|
@ -27,12 +27,12 @@ class AnimeFlickProvider : MainAPI() {
|
||||||
TvType.OVA
|
TvType.OVA
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun search(query: String): ArrayList<SearchResponse> {
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
val link = "https://animeflick.net/search.php?search=$query"
|
val link = "https://animeflick.net/search.php?search=$query"
|
||||||
val html = app.get(link).text
|
val html = app.get(link).text
|
||||||
val doc = Jsoup.parse(html)
|
val doc = Jsoup.parse(html)
|
||||||
|
|
||||||
return ArrayList(doc.select(".row.mt-2").map {
|
return doc.select(".row.mt-2").map {
|
||||||
val href = mainUrl + it.selectFirst("a").attr("href")
|
val href = mainUrl + it.selectFirst("a").attr("href")
|
||||||
val title = it.selectFirst("h5 > a").text()
|
val title = it.selectFirst("h5 > a").text()
|
||||||
val poster = mainUrl + it.selectFirst("img").attr("src").replace("70x110", "225x320")
|
val poster = mainUrl + it.selectFirst("img").attr("src").replace("70x110", "225x320")
|
||||||
|
@ -45,7 +45,7 @@ class AnimeFlickProvider : MainAPI() {
|
||||||
null,
|
null,
|
||||||
EnumSet.of(DubStatus.Subbed),
|
EnumSet.of(DubStatus.Subbed),
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun load(url: String): LoadResponse {
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
|
|
@ -0,0 +1,274 @@
|
||||||
|
package com.lagradost.cloudstream3.animeproviders
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class NineAnimeProvider : MainAPI() {
|
||||||
|
override val mainUrl = "https://9anime.center"
|
||||||
|
override val name = "9Anime"
|
||||||
|
override val hasMainPage = true
|
||||||
|
override val hasChromecastSupport = true
|
||||||
|
override val hasDownloadSupport = true
|
||||||
|
override val supportedTypes = setOf(TvType.Anime)
|
||||||
|
|
||||||
|
override suspend fun getMainPage(): HomePageResponse {
|
||||||
|
val items = listOf(
|
||||||
|
Pair("$mainUrl/ajax/home/widget?name=trending", "Trending"),
|
||||||
|
Pair("$mainUrl/ajax/home/widget?name=updated_all", "All"),
|
||||||
|
Pair("$mainUrl/ajax/home/widget?name=updated_sub&page=1", "Recently Updated (SUB)"),
|
||||||
|
Pair("$mainUrl/ajax/home/widget?name=updated_dub&page=1", "Recently Updated (DUB)"),
|
||||||
|
Pair(
|
||||||
|
"$mainUrl/ajax/home/widget?name=updated_chinese&page=1",
|
||||||
|
"Recently Updated (Chinese)"
|
||||||
|
),
|
||||||
|
Pair("$mainUrl/ajax/home/widget?name=random", "Random"),
|
||||||
|
).map { (url, name) ->
|
||||||
|
val home = Jsoup.parse(
|
||||||
|
app.get(
|
||||||
|
url
|
||||||
|
).mapped<Response>().html
|
||||||
|
).select("ul.anime-list li").map {
|
||||||
|
val title = it.selectFirst("a.name").text()
|
||||||
|
val link = it.selectFirst("a").attr("href")
|
||||||
|
val poster = it.selectFirst("a.poster img").attr("src")
|
||||||
|
AnimeSearchResponse(
|
||||||
|
title,
|
||||||
|
link,
|
||||||
|
this.name,
|
||||||
|
TvType.Anime,
|
||||||
|
poster,
|
||||||
|
null,
|
||||||
|
if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of(
|
||||||
|
DubStatus.Dubbed
|
||||||
|
) else EnumSet.of(DubStatus.Subbed),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HomePageList(name, home)
|
||||||
|
}
|
||||||
|
|
||||||
|
return HomePageResponse(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Credits to https://github.com/jmir1
|
||||||
|
private val key = "0wMrYU+ixjJ4QdzgfN2HlyIVAt3sBOZnCT9Lm7uFDovkb/EaKpRWhqXS5168ePcG"
|
||||||
|
|
||||||
|
private fun getVrf(id: String): String? {
|
||||||
|
val reversed = ue(encode(id) + "0000000").slice(0..5).reversed()
|
||||||
|
|
||||||
|
return reversed + ue(je(reversed, encode(id) ?: return null)).replace(
|
||||||
|
"""=+$""".toRegex(),
|
||||||
|
""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLink(url: String): String? {
|
||||||
|
val i = url.slice(0..5)
|
||||||
|
val n = url.slice(6..url.lastIndex)
|
||||||
|
return decode(je(i, ze(n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ue(input: String): String {
|
||||||
|
if (input.any { it.code >= 256 }) throw Exception("illegal characters!")
|
||||||
|
var output = ""
|
||||||
|
for (i in input.indices step 3) {
|
||||||
|
val a = intArrayOf(-1, -1, -1, -1)
|
||||||
|
a[0] = input[i].code shr 2
|
||||||
|
a[1] = (3 and input[i].code) shl 4
|
||||||
|
if (input.length > i + 1) {
|
||||||
|
a[1] = a[1] or (input[i + 1].code shr 4)
|
||||||
|
a[2] = (15 and input[i + 1].code) shl 2
|
||||||
|
}
|
||||||
|
if (input.length > i + 2) {
|
||||||
|
a[2] = a[2] or (input[i + 2].code shr 6)
|
||||||
|
a[3] = 63 and input[i + 2].code
|
||||||
|
}
|
||||||
|
for (n in a) {
|
||||||
|
if (n == -1) output += "="
|
||||||
|
else {
|
||||||
|
if (n in 0..63) output += key[n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun je(inputOne: String, inputTwo: String): String {
|
||||||
|
val arr = IntArray(256) { it }
|
||||||
|
var output = ""
|
||||||
|
var u = 0
|
||||||
|
var r: Int
|
||||||
|
for (a in arr.indices) {
|
||||||
|
u = (u + arr[a] + inputOne[a % inputOne.length].code) % 256
|
||||||
|
r = arr[a]
|
||||||
|
arr[a] = arr[u]
|
||||||
|
arr[u] = r
|
||||||
|
}
|
||||||
|
u = 0
|
||||||
|
var c = 0
|
||||||
|
for (f in inputTwo.indices) {
|
||||||
|
c = (c + f) % 256
|
||||||
|
u = (u + arr[c]) % 256
|
||||||
|
r = arr[c]
|
||||||
|
arr[c] = arr[u]
|
||||||
|
arr[u] = r
|
||||||
|
output += (inputTwo[f].code xor arr[(arr[c] + arr[u]) % 256]).toChar()
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ze(input: 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")
|
||||||
|
var i: Int
|
||||||
|
var r = ""
|
||||||
|
var e = 0
|
||||||
|
var u = 0
|
||||||
|
for (o in t.indices) {
|
||||||
|
e = e shl 6
|
||||||
|
i = key.indexOf(t[o])
|
||||||
|
e = e or i
|
||||||
|
u += 6
|
||||||
|
if (24 == u) {
|
||||||
|
r += ((16711680 and e) shr 16).toChar()
|
||||||
|
r += ((65280 and e) shr 8).toChar()
|
||||||
|
r += (255 and e).toChar()
|
||||||
|
e = 0
|
||||||
|
u = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return if (12 == u) {
|
||||||
|
e = e shr 4
|
||||||
|
r + e.toChar()
|
||||||
|
} else {
|
||||||
|
if (18 == u) {
|
||||||
|
e = e shr 2
|
||||||
|
r += ((65280 and e) shr 8).toChar()
|
||||||
|
r += (255 and e).toChar()
|
||||||
|
}
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun encode(input: String): String? = java.net.URLEncoder.encode(input, "utf-8")
|
||||||
|
|
||||||
|
private fun decode(input: String): String? = java.net.URLDecoder.decode(input, "utf-8")
|
||||||
|
|
||||||
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
|
val url = "$mainUrl/filter?sort=title%3Aasc&keyword=$query"
|
||||||
|
|
||||||
|
return app.get(url).document.select("ul.anime-list li").mapNotNull {
|
||||||
|
val title = it.selectFirst("a.name").text()
|
||||||
|
val href =
|
||||||
|
fixUrlNull(it.selectFirst("a").attr("href"))?.replace(Regex("(\\?ep=(\\d+)\$)"), "")
|
||||||
|
?: return@mapNotNull null
|
||||||
|
val image = it.selectFirst("a.poster img").attr("src")
|
||||||
|
AnimeSearchResponse(
|
||||||
|
title,
|
||||||
|
href,
|
||||||
|
this.name,
|
||||||
|
TvType.Anime,
|
||||||
|
image,
|
||||||
|
null,
|
||||||
|
if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of(
|
||||||
|
DubStatus.Dubbed
|
||||||
|
) else EnumSet.of(DubStatus.Subbed),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Response(
|
||||||
|
@JsonProperty("html") val html: String
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun load(url: String): LoadResponse? {
|
||||||
|
val urlclean = url.substringAfter("watch/")
|
||||||
|
val regexID = Regex("(\\.[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)")
|
||||||
|
val animeid = regexID.find(urlclean)?.value?.replace(".", "") ?: return null
|
||||||
|
val animeidencoded = encode(getVrf(animeid) ?: return null)
|
||||||
|
|
||||||
|
val doc = app.get(url).document
|
||||||
|
val poster = doc.selectFirst("aside.main div.thumb div img").attr("src")
|
||||||
|
val title = doc.selectFirst(".info .title").text()
|
||||||
|
val description = doc.selectFirst("div.info p").text().replace("Ver menos", "").trim()
|
||||||
|
val episodes = Jsoup.parse(
|
||||||
|
app.get(
|
||||||
|
"$mainUrl/ajax/anime/servers?ep=1&id=${animeid}&vrf=$animeidencoded&ep=8&episode=&token="
|
||||||
|
).mapped<Response>().html
|
||||||
|
)?.select("ul.episodes li a")?.mapNotNull {
|
||||||
|
val link = it?.attr("href") ?: return@mapNotNull null
|
||||||
|
val epnum = it.attr("data-base")?.toIntOrNull()
|
||||||
|
AnimeEpisode(link, episode = epnum)
|
||||||
|
} ?: return null
|
||||||
|
|
||||||
|
return newAnimeLoadResponse(title, url, TvType.Anime) {
|
||||||
|
posterUrl = poster
|
||||||
|
addEpisodes(DubStatus.Subbed, episodes)
|
||||||
|
plot = description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Links(
|
||||||
|
@JsonProperty("url") val url: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Servers(
|
||||||
|
@JsonProperty("28") val mcloud: String?,
|
||||||
|
@JsonProperty("35") val mp4upload: String?,
|
||||||
|
@JsonProperty("40") val streamtape: String?,
|
||||||
|
@JsonProperty("41") val vidstream: String?,
|
||||||
|
@JsonProperty("43") val videovard: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
val urlclean = data.substringAfter("watch/")
|
||||||
|
val regexID = Regex("(\\.[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)")
|
||||||
|
val animeid = regexID.find(urlclean)?.value?.replace(".", "") ?: return false
|
||||||
|
|
||||||
|
val animeidencoded = encode(getVrf(animeid) ?: return false)
|
||||||
|
|
||||||
|
Jsoup.parse(
|
||||||
|
app.get(
|
||||||
|
"$mainUrl/ajax/anime/servers?&id=${animeid}&vrf=$animeidencoded&episode=&token="
|
||||||
|
).mapped<Response>().html
|
||||||
|
).select("div.body").map { element ->
|
||||||
|
val jsonregex = Regex("(\\{.+\\}.*$data)")
|
||||||
|
val servers = jsonregex.find(element.toString())?.value?.replace(
|
||||||
|
Regex("(\".*data-base=.*href=\"$data)"),
|
||||||
|
""
|
||||||
|
)?.replace(""", "\"") ?: return@map
|
||||||
|
|
||||||
|
val jsonservers = parseJson<Servers?>(servers) ?: return@map
|
||||||
|
listOfNotNull(
|
||||||
|
jsonservers.vidstream,
|
||||||
|
jsonservers.mcloud,
|
||||||
|
jsonservers.mp4upload,
|
||||||
|
jsonservers.streamtape
|
||||||
|
).mapNotNull {
|
||||||
|
val epserver = app.get("$mainUrl/ajax/anime/episode?id=$it").text
|
||||||
|
(if (epserver.contains("url")) {
|
||||||
|
parseJson<Links>(epserver)
|
||||||
|
} else null)?.url?.let { it1 -> getLink(it1.replace("=", "")) }
|
||||||
|
?.replace("/embed/", "/e/")
|
||||||
|
}.apmap { url ->
|
||||||
|
loadExtractor(
|
||||||
|
url, data, callback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,14 @@
|
||||||
package com.lagradost.cloudstream3.extractors
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
import com.lagradost.cloudstream3.utils.*
|
|
||||||
import com.lagradost.cloudstream3.app
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.USER_AGENT
|
import com.lagradost.cloudstream3.USER_AGENT
|
||||||
import com.lagradost.cloudstream3.apmap
|
import com.lagradost.cloudstream3.apmap
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
|
|
||||||
open class Mcloud : ExtractorApi() {
|
open class Mcloud : ExtractorApi() {
|
||||||
override val name = "Mcloud"
|
override val name = "Mcloud"
|
||||||
|
@ -30,6 +32,11 @@ open class Mcloud : ExtractorApi() {
|
||||||
val link = url.replace("$mainUrl/e/","$mainUrl/info/")
|
val link = url.replace("$mainUrl/e/","$mainUrl/info/")
|
||||||
val response = app.get(link, headers = headers).text
|
val response = app.get(link, headers = headers).text
|
||||||
|
|
||||||
|
if(response.startsWith("<!DOCTYPE html>")) {
|
||||||
|
// TODO decrypt html for link
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
data class Sources (
|
data class Sources (
|
||||||
@JsonProperty("file") val file: String
|
@JsonProperty("file") val file: String
|
||||||
)
|
)
|
||||||
|
@ -43,7 +50,7 @@ open class Mcloud : ExtractorApi() {
|
||||||
@JsonProperty("media") val media: Media,
|
@JsonProperty("media") val media: Media,
|
||||||
)
|
)
|
||||||
|
|
||||||
val mapped = response.let { parseJson<JsonMcloud>(it) }
|
val mapped = parseJson<JsonMcloud>(response)
|
||||||
val sources = mutableListOf<ExtractorLink>()
|
val sources = mutableListOf<ExtractorLink>()
|
||||||
|
|
||||||
if (mapped.success)
|
if (mapped.success)
|
||||||
|
|
|
@ -6,7 +6,11 @@ import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.mapper
|
import com.lagradost.cloudstream3.mapper
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
|
||||||
class WcoStream : ExtractorApi() {
|
class Vidstreamz : WcoStream() {
|
||||||
|
override val mainUrl: String = "https://vidstreamz.online"
|
||||||
|
}
|
||||||
|
|
||||||
|
open class WcoStream : ExtractorApi() {
|
||||||
override val name = "VidStream" //Cause works for animekisa and wco
|
override val name = "VidStream" //Cause works for animekisa and wco
|
||||||
override val mainUrl = "https://vidstream.pro"
|
override val mainUrl = "https://vidstream.pro"
|
||||||
override val requiresReferer = false
|
override val requiresReferer = false
|
||||||
|
@ -16,8 +20,8 @@ class WcoStream : ExtractorApi() {
|
||||||
val baseUrl = url.split("/e/")[0]
|
val baseUrl = url.split("/e/")[0]
|
||||||
|
|
||||||
val html = app.get(url, headers = mapOf("Referer" to "https://wcostream.cc/")).text
|
val html = app.get(url, headers = mapOf("Referer" to "https://wcostream.cc/")).text
|
||||||
val (Id) = "/e/(.*?)?domain".toRegex().find(url)!!.destructured
|
val (Id) = ("/e/(.*?)?domain".toRegex().find(url)?.destructured ?: Regex("""/e/(.*)""").find(url)?.destructured) ?: return emptyList()
|
||||||
val (skey) = """skey\s=\s['"](.*?)['"];""".toRegex().find(html)!!.destructured
|
val (skey) = """skey\s=\s['"](.*?)['"];""".toRegex().find(html)?.destructured ?: return emptyList()
|
||||||
|
|
||||||
val apiLink = "$baseUrl/info/$Id?domain=wcostream.cc&skey=$skey"
|
val apiLink = "$baseUrl/info/$Id?domain=wcostream.cc&skey=$skey"
|
||||||
val referrer = "$baseUrl/e/$Id?domain=wcostream.cc"
|
val referrer = "$baseUrl/e/$Id?domain=wcostream.cc"
|
||||||
|
@ -44,8 +48,10 @@ class WcoStream : ExtractorApi() {
|
||||||
if (mapped.success) {
|
if (mapped.success) {
|
||||||
mapped.media.sources.forEach {
|
mapped.media.sources.forEach {
|
||||||
if (it.file.contains("m3u8")) {
|
if (it.file.contains("m3u8")) {
|
||||||
hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(it.file, null), true).forEach { stream ->
|
hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(it.file, null), true)
|
||||||
val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p"
|
.forEach { stream ->
|
||||||
|
val qualityString =
|
||||||
|
if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p"
|
||||||
sources.add(
|
sources.add(
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -94,6 +94,7 @@ suspend fun loadExtractor(url: String, referer: String? = null, callback: (Extra
|
||||||
val extractorApis: Array<ExtractorApi> = arrayOf(
|
val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
//AllProvider(),
|
//AllProvider(),
|
||||||
WcoStream(),
|
WcoStream(),
|
||||||
|
Vidstreamz(),
|
||||||
Mp4Upload(),
|
Mp4Upload(),
|
||||||
StreamTape(),
|
StreamTape(),
|
||||||
MixDrop(),
|
MixDrop(),
|
||||||
|
|
Loading…
Reference in a new issue