2021-05-19 18:44:02 +00:00
|
|
|
package com.lagradost.cloudstream3.utils
|
|
|
|
|
2022-01-07 19:27:25 +00:00
|
|
|
import android.net.Uri
|
|
|
|
import com.lagradost.cloudstream3.TvType
|
2021-09-30 15:51:34 +00:00
|
|
|
import com.lagradost.cloudstream3.USER_AGENT
|
2021-12-05 16:22:30 +00:00
|
|
|
import com.lagradost.cloudstream3.app
|
2021-07-30 01:14:53 +00:00
|
|
|
import com.lagradost.cloudstream3.extractors.*
|
2022-01-29 21:25:12 +00:00
|
|
|
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
|
|
|
import kotlinx.coroutines.delay
|
2021-09-30 15:51:34 +00:00
|
|
|
import org.jsoup.Jsoup
|
2021-05-19 18:44:02 +00:00
|
|
|
|
|
|
|
data class ExtractorLink(
|
2021-05-20 15:22:28 +00:00
|
|
|
val source: String,
|
2021-05-19 18:44:02 +00:00
|
|
|
val name: String,
|
2021-08-22 17:14:48 +00:00
|
|
|
override val url: String,
|
|
|
|
override val referer: String,
|
2021-05-19 18:44:02 +00:00
|
|
|
val quality: Int,
|
|
|
|
val isM3u8: Boolean = false,
|
2022-02-11 09:17:04 +00:00
|
|
|
override val headers: Map<String, String> = mapOf(),
|
|
|
|
/** Used for getExtractorVerifierJob() */
|
|
|
|
val extractorData: String? = null
|
2021-08-22 17:14:48 +00:00
|
|
|
) : VideoDownloadManager.IDownloadableMinimum
|
2021-05-19 18:44:02 +00:00
|
|
|
|
2022-01-07 19:27:25 +00:00
|
|
|
data class ExtractorUri(
|
|
|
|
val uri : Uri,
|
|
|
|
val name : String,
|
|
|
|
|
|
|
|
val basePath: String? = null,
|
|
|
|
val relativePath: String? = null,
|
|
|
|
val displayName: String? = null,
|
|
|
|
|
|
|
|
val id : Int? = null,
|
|
|
|
val parentId : Int? = null,
|
|
|
|
val episode : Int? = null,
|
|
|
|
val season : Int? = null,
|
|
|
|
val headerName : String? = null,
|
|
|
|
val tvType: TvType? = null,
|
|
|
|
)
|
|
|
|
|
2021-08-22 17:14:48 +00:00
|
|
|
data class ExtractorSubtitleLink(
|
|
|
|
val name: String,
|
|
|
|
override val url: String,
|
|
|
|
override val referer: String,
|
2021-09-11 18:44:37 +00:00
|
|
|
override val headers: Map<String, String> = mapOf()
|
2021-08-22 17:14:48 +00:00
|
|
|
) : VideoDownloadManager.IDownloadableMinimum
|
2021-05-28 13:38:06 +00:00
|
|
|
|
2021-05-19 18:44:02 +00:00
|
|
|
enum class Qualities(var value: Int) {
|
|
|
|
Unknown(0),
|
2021-08-24 21:06:40 +00:00
|
|
|
P360(-2), // 360p
|
|
|
|
P480(-1), // 480p
|
|
|
|
P720(1), // 720p
|
|
|
|
P1080(2), // 1080p
|
|
|
|
P1440(3), // 1440p
|
2022-04-15 13:06:43 +00:00
|
|
|
P2160(4); // 4k or 2160p
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
fun getStringByInt(qual: Int?) : String {
|
|
|
|
return when (qual) {
|
|
|
|
P360.value -> "360p"
|
|
|
|
P480.value -> "480p"
|
|
|
|
P720.value -> "720p"
|
|
|
|
P1080.value -> "1080p"
|
|
|
|
P1440.value -> "1440p"
|
|
|
|
P2160.value -> "2160p/4k"
|
|
|
|
else -> ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-19 18:44:02 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 13:06:43 +00:00
|
|
|
fun getQualityFromName(qualityName: String?): Int {
|
|
|
|
if (qualityName == null) {
|
|
|
|
return Qualities.Unknown.value
|
|
|
|
}
|
2022-01-29 18:37:23 +00:00
|
|
|
return when (qualityName.replace("p", "").replace("P", "").trim()) {
|
2021-08-24 21:06:40 +00:00
|
|
|
"360" -> Qualities.P360
|
|
|
|
"480" -> Qualities.P480
|
|
|
|
"720" -> Qualities.P720
|
|
|
|
"1080" -> Qualities.P1080
|
|
|
|
"1440" -> Qualities.P1440
|
|
|
|
"2160" -> Qualities.P2160
|
|
|
|
"4k" -> Qualities.P2160
|
|
|
|
"4K" -> Qualities.P2160
|
2021-06-16 22:31:41 +00:00
|
|
|
else -> Qualities.Unknown
|
|
|
|
}.value
|
|
|
|
}
|
|
|
|
|
2021-05-19 18:44:02 +00:00
|
|
|
private val packedRegex = Regex("""eval\(function\(p,a,c,k,e,.*\)\)""")
|
|
|
|
fun getPacked(string: String): String? {
|
|
|
|
return packedRegex.find(string)?.value
|
|
|
|
}
|
|
|
|
|
2021-09-25 10:07:40 +00:00
|
|
|
fun getAndUnpack(string: String): String {
|
2021-05-19 18:44:02 +00:00
|
|
|
val packedText = getPacked(string)
|
2021-09-25 10:07:40 +00:00
|
|
|
return JsUnpacker(packedText).unpack() ?: string
|
2021-05-19 18:44:02 +00:00
|
|
|
}
|
|
|
|
|
2021-12-12 01:49:41 +00:00
|
|
|
/**
|
|
|
|
* Tries to load the appropriate extractor based on link, returns true if any extractor is loaded.
|
|
|
|
* */
|
2022-01-30 01:50:49 +00:00
|
|
|
suspend fun loadExtractor(url: String, referer: String? = null, callback: (ExtractorLink) -> Unit) : Boolean {
|
2021-08-25 22:16:20 +00:00
|
|
|
for (extractor in extractorApis) {
|
|
|
|
if (url.startsWith(extractor.mainUrl)) {
|
|
|
|
extractor.getSafeUrl(url, referer)?.forEach(callback)
|
2021-12-12 01:49:41 +00:00
|
|
|
return true
|
2021-08-25 22:16:20 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-12 01:49:41 +00:00
|
|
|
return false
|
2021-08-25 22:16:20 +00:00
|
|
|
}
|
|
|
|
|
2021-05-20 15:22:28 +00:00
|
|
|
val extractorApis: Array<ExtractorApi> = arrayOf(
|
2021-05-19 18:44:02 +00:00
|
|
|
//AllProvider(),
|
2021-07-23 11:59:41 +00:00
|
|
|
WcoStream(),
|
2022-02-26 23:56:20 +00:00
|
|
|
Vidstreamz(),
|
2022-02-27 18:29:27 +00:00
|
|
|
Vizcloud(),
|
2022-03-05 12:19:50 +00:00
|
|
|
Vizcloud2(),
|
2022-03-21 18:19:29 +00:00
|
|
|
VizcloudOnline(),
|
2022-03-22 09:57:08 +00:00
|
|
|
VizcloudXyz(),
|
2022-04-07 15:54:20 +00:00
|
|
|
VizcloudLive(),
|
2022-04-10 23:06:17 +00:00
|
|
|
VizcloudInfo(),
|
2022-04-12 16:20:16 +00:00
|
|
|
MwvnVizcloudInfo(),
|
2022-04-18 17:52:40 +00:00
|
|
|
VizcloudDigital(),
|
2021-05-19 18:44:02 +00:00
|
|
|
Mp4Upload(),
|
|
|
|
StreamTape(),
|
|
|
|
MixDrop(),
|
2022-02-10 09:27:33 +00:00
|
|
|
Mcloud(),
|
2021-07-23 16:41:37 +00:00
|
|
|
XStreamCdn(),
|
|
|
|
StreamSB(),
|
2022-02-20 16:25:44 +00:00
|
|
|
StreamSB1(),
|
|
|
|
StreamSB2(),
|
|
|
|
StreamSB3(),
|
|
|
|
StreamSB4(),
|
|
|
|
StreamSB5(),
|
|
|
|
StreamSB6(),
|
|
|
|
StreamSB7(),
|
|
|
|
StreamSB8(),
|
|
|
|
StreamSB9(),
|
2022-03-16 20:11:14 +00:00
|
|
|
StreamSB10(),
|
2022-03-12 13:02:30 +00:00
|
|
|
// Streamhub(), cause Streamhub2() works
|
|
|
|
Streamhub2(),
|
2021-12-25 18:54:44 +00:00
|
|
|
|
2021-12-09 08:57:41 +00:00
|
|
|
FEmbed(),
|
2021-12-27 17:41:55 +00:00
|
|
|
FeHD(),
|
|
|
|
Fplayer(),
|
2022-02-20 16:25:44 +00:00
|
|
|
// WatchSB(), 'cause StreamSB.kt works
|
2021-12-27 01:15:49 +00:00
|
|
|
Uqload(),
|
2022-01-16 17:43:44 +00:00
|
|
|
Uqload1(),
|
2021-12-27 01:15:49 +00:00
|
|
|
Evoload(),
|
2022-01-24 21:15:40 +00:00
|
|
|
Evoload1(),
|
2021-12-21 23:11:13 +00:00
|
|
|
VoeExtractor(),
|
2022-02-20 16:25:44 +00:00
|
|
|
// UpstreamExtractor(), GenericM3U8.kt works
|
2021-08-29 13:10:36 +00:00
|
|
|
|
2022-02-05 13:32:02 +00:00
|
|
|
Tomatomatela(),
|
|
|
|
Cinestart(),
|
|
|
|
OkRu(),
|
|
|
|
|
2021-08-29 13:10:36 +00:00
|
|
|
// dood extractors
|
2021-08-29 11:45:58 +00:00
|
|
|
DoodToExtractor(),
|
2021-08-29 13:10:36 +00:00
|
|
|
DoodSoExtractor(),
|
|
|
|
DoodLaExtractor(),
|
2021-12-27 17:41:55 +00:00
|
|
|
DoodWsExtractor(),
|
2021-10-03 20:23:06 +00:00
|
|
|
|
2021-12-25 18:54:44 +00:00
|
|
|
AsianLoad(),
|
|
|
|
|
2022-03-04 12:47:36 +00:00
|
|
|
// GenericM3U8(),
|
2022-02-20 16:25:44 +00:00
|
|
|
Jawcloud(),
|
2022-03-04 12:47:36 +00:00
|
|
|
Zplayer(),
|
|
|
|
ZplayerV2(),
|
|
|
|
Upstream(),
|
|
|
|
|
2022-02-20 16:25:44 +00:00
|
|
|
|
|
|
|
// StreamSB.kt works
|
|
|
|
// SBPlay(),
|
|
|
|
// SBPlay1(),
|
|
|
|
// SBPlay2(),
|
|
|
|
|
2022-03-13 06:44:02 +00:00
|
|
|
PlayerVoxzer(),
|
2021-05-19 18:44:02 +00:00
|
|
|
)
|
|
|
|
|
2021-05-20 15:22:28 +00:00
|
|
|
fun getExtractorApiFromName(name: String): ExtractorApi {
|
|
|
|
for (api in extractorApis) {
|
|
|
|
if (api.name == name) return api
|
|
|
|
}
|
|
|
|
return extractorApis[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
fun requireReferer(name: String): Boolean {
|
|
|
|
return getExtractorApiFromName(name).requiresReferer
|
|
|
|
}
|
|
|
|
|
2021-05-19 18:44:02 +00:00
|
|
|
fun httpsify(url: String): String {
|
|
|
|
return if (url.startsWith("//")) "https:$url" else url
|
|
|
|
}
|
|
|
|
|
2022-01-29 21:25:12 +00:00
|
|
|
suspend fun getPostForm(requestUrl : String, html : String) : String? {
|
2021-09-30 15:51:34 +00:00
|
|
|
val document = Jsoup.parse(html)
|
|
|
|
val inputs = document.select("Form > input")
|
|
|
|
if (inputs.size < 4) return null
|
|
|
|
var op: String? = null
|
|
|
|
var id: String? = null
|
|
|
|
var mode: String? = null
|
|
|
|
var hash: String? = null
|
|
|
|
|
|
|
|
for (input in inputs) {
|
|
|
|
val value = input.attr("value") ?: continue
|
|
|
|
when (input.attr("name")) {
|
|
|
|
"op" -> op = value
|
|
|
|
"id" -> id = value
|
|
|
|
"mode" -> mode = value
|
|
|
|
"hash" -> hash = value
|
2022-01-07 19:27:25 +00:00
|
|
|
else -> Unit
|
2021-09-30 15:51:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (op == null || id == null || mode == null || hash == null) {
|
|
|
|
return null
|
|
|
|
}
|
2022-01-29 21:25:12 +00:00
|
|
|
delay(5000) // ye this is needed, wont work with 0 delay
|
2021-09-30 15:51:34 +00:00
|
|
|
|
2021-12-05 16:22:30 +00:00
|
|
|
val postResponse = app.post(
|
2021-09-30 15:51:34 +00:00
|
|
|
requestUrl,
|
|
|
|
headers = mapOf(
|
|
|
|
"content-type" to "application/x-www-form-urlencoded",
|
|
|
|
"referer" to requestUrl,
|
|
|
|
"user-agent" to USER_AGENT,
|
|
|
|
"accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
|
|
|
),
|
|
|
|
data = mapOf("op" to op, "id" to id, "mode" to mode, "hash" to hash)
|
|
|
|
).text
|
|
|
|
|
|
|
|
return postResponse
|
|
|
|
}
|
|
|
|
|
2021-05-19 18:44:02 +00:00
|
|
|
abstract class ExtractorApi {
|
|
|
|
abstract val name: String
|
|
|
|
abstract val mainUrl: String
|
|
|
|
abstract val requiresReferer: Boolean
|
|
|
|
|
2022-01-29 21:25:12 +00:00
|
|
|
suspend fun getSafeUrl(url: String, referer: String? = null): List<ExtractorLink>? {
|
|
|
|
return suspendSafeApiCall { getUrl(url, referer) }
|
2021-07-23 16:41:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Will throw errors, use getSafeUrl if you don't want to handle the exception yourself
|
|
|
|
*/
|
2022-01-29 21:25:12 +00:00
|
|
|
abstract suspend fun getUrl(url: String, referer: String? = null): List<ExtractorLink>?
|
2021-05-19 18:44:02 +00:00
|
|
|
|
2021-05-20 15:22:28 +00:00
|
|
|
open fun getExtractorUrl(id: String): String {
|
2021-05-19 18:44:02 +00:00
|
|
|
return id
|
|
|
|
}
|
2021-07-23 11:59:41 +00:00
|
|
|
}
|