forked from recloudstream/cloudstream
added extractors (#86)
This commit is contained in:
parent
abc9421fb1
commit
ae137f4a34
6 changed files with 195 additions and 20 deletions
|
@ -0,0 +1,37 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
|
import com.lagradost.cloudstream3.utils.httpsify
|
||||||
|
|
||||||
|
class Embedgram : ExtractorApi() {
|
||||||
|
override val name = "Embedgram"
|
||||||
|
override val mainUrl = "https://embedgram.com"
|
||||||
|
override val requiresReferer = true
|
||||||
|
|
||||||
|
override suspend fun getUrl(
|
||||||
|
url: String,
|
||||||
|
referer: String?,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val document = app.get(url, referer = referer).document
|
||||||
|
val link = document.select("video source:last-child").attr("src")
|
||||||
|
val quality = document.select("video source:last-child").attr("title")
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
this.name,
|
||||||
|
this.name,
|
||||||
|
httpsify(link),
|
||||||
|
"$mainUrl/",
|
||||||
|
getQualityFromName(quality),
|
||||||
|
headers = mapOf(
|
||||||
|
"Range" to "bytes=0-"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.Qualities
|
||||||
|
|
||||||
|
class Mvidoo : ExtractorApi() {
|
||||||
|
override val name = "Mvidoo"
|
||||||
|
override val mainUrl = "https://mvidoo.com"
|
||||||
|
override val requiresReferer = true
|
||||||
|
|
||||||
|
private fun String.decodeHex(): String {
|
||||||
|
require(length % 2 == 0) { "Must have an even length" }
|
||||||
|
return String(
|
||||||
|
chunked(2)
|
||||||
|
.map { it.toInt(16).toByte() }
|
||||||
|
.toByteArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getUrl(
|
||||||
|
url: String,
|
||||||
|
referer: String?,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val document = app.get(url, referer = referer).text
|
||||||
|
val data = Regex("""\{var\s*[^\s]+\s*=\s*(\[[^]]+])""").find(document)?.groupValues?.get(1)
|
||||||
|
?.removeSurrounding("[", "]")?.replace("\"", "")?.replace("\\x", "")?.split(",")?.map { it.decodeHex() }?.reversed()?.joinToString("") ?: return
|
||||||
|
Regex("source\\s*src=\"([^\"]+)").find(data)?.groupValues?.get(1)?.let { link ->
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
this.name,
|
||||||
|
this.name,
|
||||||
|
link,
|
||||||
|
"$mainUrl/",
|
||||||
|
Qualities.Unknown.value,
|
||||||
|
headers = mapOf(
|
||||||
|
"Range" to "bytes=0-"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
|
||||||
|
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||||
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
class Streamplay : ExtractorApi() {
|
||||||
|
override val name = "Streamplay"
|
||||||
|
override val mainUrl = "https://streamplay.to"
|
||||||
|
override val requiresReferer = true
|
||||||
|
|
||||||
|
override suspend fun getUrl(
|
||||||
|
url: String,
|
||||||
|
referer: String?,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val request = app.get(url, referer = referer)
|
||||||
|
val redirectUrl = request.url
|
||||||
|
val mainServer = URI(redirectUrl).let {
|
||||||
|
"${it.scheme}://${it.host}"
|
||||||
|
}
|
||||||
|
val key = redirectUrl.substringAfter("embed-").substringBefore(".html")
|
||||||
|
val token =
|
||||||
|
request.document.select("script").find { it.data().contains("sitekey:") }?.data()
|
||||||
|
?.substringAfterLast("sitekey: '")?.substringBefore("',")?.let { captchaKey ->
|
||||||
|
getCaptchaToken(
|
||||||
|
redirectUrl,
|
||||||
|
captchaKey,
|
||||||
|
referer = "$mainServer/"
|
||||||
|
)
|
||||||
|
} ?: throw ErrorLoadingException("can't bypass captcha")
|
||||||
|
app.post(
|
||||||
|
"$mainServer/player-$key-488x286.html", data = mapOf(
|
||||||
|
"op" to "embed",
|
||||||
|
"token" to token
|
||||||
|
),
|
||||||
|
referer = redirectUrl,
|
||||||
|
headers = mapOf(
|
||||||
|
"Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||||
|
"Content-Type" to "application/x-www-form-urlencoded"
|
||||||
|
)
|
||||||
|
).document.select("script").find { script ->
|
||||||
|
script.data().contains("eval(function(p,a,c,k,e,d)")
|
||||||
|
}?.let {
|
||||||
|
val data = getAndUnpack(it.data()).substringAfter("sources=[").substringBefore(",desc")
|
||||||
|
.replace("file", "\"file\"")
|
||||||
|
.replace("label", "\"label\"")
|
||||||
|
tryParseJson<List<Source>>("[$data}]")?.map { res ->
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
this.name,
|
||||||
|
this.name,
|
||||||
|
res.file ?: return@map null,
|
||||||
|
"$mainServer/",
|
||||||
|
when (res.label) {
|
||||||
|
"HD" -> Qualities.P720.value
|
||||||
|
"SD" -> Qualities.P480.value
|
||||||
|
else -> Qualities.Unknown.value
|
||||||
|
},
|
||||||
|
headers = mapOf(
|
||||||
|
"Range" to "bytes=0-"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Source(
|
||||||
|
@JsonProperty("file") val file: String? = null,
|
||||||
|
@JsonProperty("label") val label: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
|
@ -1,19 +1,23 @@
|
||||||
package com.lagradost.cloudstream3.extractors
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.Qualities
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
|
|
||||||
class UpstreamExtractor : ExtractorApi() {
|
class UpstreamExtractor : ExtractorApi() {
|
||||||
override val name: String = "Upstream.to"
|
override val name: String = "Upstream"
|
||||||
override val mainUrl: String = "https://upstream.to"
|
override val mainUrl: String = "https://upstream.to"
|
||||||
override val requiresReferer = true
|
override val requiresReferer = true
|
||||||
|
|
||||||
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
|
override suspend fun getUrl(
|
||||||
// WIP: m3u8 link fetched but sometimes not playing
|
url: String,
|
||||||
|
referer: String?,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
//Log.i(this.name, "Result => (no extractor) ${url}")
|
//Log.i(this.name, "Result => (no extractor) ${url}")
|
||||||
val sources: MutableList<ExtractorLink> = mutableListOf()
|
|
||||||
val doc = app.get(url, referer = referer).text
|
val doc = app.get(url, referer = referer).text
|
||||||
if (doc.isNotBlank()) {
|
if (doc.isNotBlank()) {
|
||||||
var reg = Regex("(?<=master)(.*)(?=hls)")
|
var reg = Regex("(?<=master)(.*)(?=hls)")
|
||||||
|
@ -30,7 +34,9 @@ class UpstreamExtractor: ExtractorApi() {
|
||||||
domName = "${part}.${domName}"
|
domName = "${part}.${domName}"
|
||||||
}
|
}
|
||||||
domName.trimEnd('.')
|
domName.trimEnd('.')
|
||||||
} else { "" }
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
false -> ""
|
false -> ""
|
||||||
}
|
}
|
||||||
|
@ -42,18 +48,13 @@ class UpstreamExtractor: ExtractorApi() {
|
||||||
|
|
||||||
result?.forEach {
|
result?.forEach {
|
||||||
val linkUrl = "https://${domain}/hls/${it}/master.m3u8"
|
val linkUrl = "https://${domain}/hls/${it}/master.m3u8"
|
||||||
sources.add(
|
M3u8Helper.generateM3u8(
|
||||||
ExtractorLink(
|
this.name,
|
||||||
name = "Upstream m3u8",
|
linkUrl,
|
||||||
source = this.name,
|
"$mainUrl/",
|
||||||
url = linkUrl,
|
headers = mapOf("Origin" to mainUrl)
|
||||||
quality = Qualities.Unknown.value,
|
).forEach(callback)
|
||||||
referer = referer ?: linkUrl,
|
|
||||||
isM3u8 = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sources
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,6 +7,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
|
|
||||||
|
class FEnet: XStreamCdn() {
|
||||||
|
override val name: String = "FEnet"
|
||||||
|
override val mainUrl: String = "https://fembed.net"
|
||||||
|
}
|
||||||
|
|
||||||
class Rasacintaku: XStreamCdn() {
|
class Rasacintaku: XStreamCdn() {
|
||||||
override val mainUrl: String = "https://rasa-cintaku-semakin-berantai.xyz"
|
override val mainUrl: String = "https://rasa-cintaku-semakin-berantai.xyz"
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,13 +246,14 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
||||||
Luxubu(),
|
Luxubu(),
|
||||||
LayarKaca(),
|
LayarKaca(),
|
||||||
Rasacintaku(),
|
Rasacintaku(),
|
||||||
|
FEnet(),
|
||||||
// WatchSB(), 'cause StreamSB.kt works
|
// WatchSB(), 'cause StreamSB.kt works
|
||||||
Uqload(),
|
Uqload(),
|
||||||
Uqload1(),
|
Uqload1(),
|
||||||
Evoload(),
|
Evoload(),
|
||||||
Evoload1(),
|
Evoload1(),
|
||||||
VoeExtractor(),
|
VoeExtractor(),
|
||||||
// UpstreamExtractor(), GenericM3U8.kt works
|
UpstreamExtractor(),
|
||||||
|
|
||||||
Tomatomatela(),
|
Tomatomatela(),
|
||||||
Cinestart(),
|
Cinestart(),
|
||||||
|
@ -316,6 +317,9 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
||||||
Acefile(),
|
Acefile(),
|
||||||
SpeedoStream(),
|
SpeedoStream(),
|
||||||
Zorofile(),
|
Zorofile(),
|
||||||
|
Embedgram(),
|
||||||
|
Mvidoo(),
|
||||||
|
Streamplay(),
|
||||||
|
|
||||||
YoutubeExtractor(),
|
YoutubeExtractor(),
|
||||||
YoutubeShortLinkExtractor(),
|
YoutubeShortLinkExtractor(),
|
||||||
|
|
Loading…
Reference in a new issue