Merge remote-tracking branch 'origin/master'

# Conflicts:
#	app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmpertuttiProvider.kt
This commit is contained in:
LagradOst 2022-07-30 18:58:17 +02:00
commit c7531faceb
13 changed files with 176 additions and 136 deletions

View file

@ -108,7 +108,7 @@ dependencies {
//implementation "io.karn:khttp-android:0.1.2" //okhttp instead //implementation "io.karn:khttp-android:0.1.2" //okhttp instead
// implementation 'org.jsoup:jsoup:1.13.1' // implementation 'org.jsoup:jsoup:1.13.1'
// implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.12.3" implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1"
implementation "androidx.preference:preference-ktx:1.2.0" implementation "androidx.preference:preference-ktx:1.2.0"
@ -156,7 +156,7 @@ dependencies {
// Networking // Networking
// implementation "com.squareup.okhttp3:okhttp:4.9.2" // implementation "com.squareup.okhttp3:okhttp:4.9.2"
// implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1" // implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1"
implementation 'com.github.Blatzar:NiceHttp:0.2.0' implementation 'com.github.Blatzar:NiceHttp:0.3.2'
// Util to skip the URI file fuckery 🙏 // Util to skip the URI file fuckery 🙏
implementation "com.github.tachiyomiorg:unifile:17bec43" implementation "com.github.tachiyomiorg:unifile:17bec43"

View file

@ -21,6 +21,9 @@ import androidx.navigation.NavOptions
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.android.gms.cast.framework.* import com.google.android.gms.cast.framework.*
import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.navigationrail.NavigationRailView
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
@ -75,6 +78,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.UIHelper.requestRW
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.ResponseParser
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_result_swipe.* import kotlinx.android.synthetic.main.fragment_result_swipe.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -83,6 +87,7 @@ import kotlinx.coroutines.withContext
import org.schabi.newpipe.extractor.NewPipe import org.schabi.newpipe.extractor.NewPipe
import java.io.File import java.io.File
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.reflect.KClass
const val VLC_PACKAGE = "org.videolan.vlc" const val VLC_PACKAGE = "org.videolan.vlc"
@ -98,7 +103,29 @@ const val VLC_EXTRA_DURATION_OUT = "extra_duration"
const val VLC_LAST_ID_KEY = "vlc_last_open_id" const val VLC_LAST_ID_KEY = "vlc_last_open_id"
// Short name for requests client to make it nicer to use // Short name for requests client to make it nicer to use
var app = Requests().apply {
var app = Requests(responseParser = object : ResponseParser {
val mapper: ObjectMapper = jacksonObjectMapper().configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false
)
override fun <T : Any> parse(text: String, kClass: KClass<T>): T {
return mapper.readValue(text, kClass.java)
}
override fun <T : Any> parseSafe(text: String, kClass: KClass<T>): T? {
return try {
mapper.readValue(text, kClass.java)
} catch (e: Exception) {
null
}
}
override fun writeValueAsString(obj: Any): String {
return mapper.writeValueAsString(obj)
}
}).apply {
defaultHeaders = mapOf("user-agent" to USER_AGENT) defaultHeaders = mapOf("user-agent" to USER_AGENT)
} }

View file

@ -312,7 +312,7 @@ class AllAnimeProvider : MainAPI() {
@JsonProperty("episodeIframeHead") val episodeIframeHead: String @JsonProperty("episodeIframeHead") val episodeIframeHead: String
) )
private fun getM3u8Qualities( private suspend fun getM3u8Qualities(
m3u8Link: String, m3u8Link: String,
referer: String, referer: String,
qualityName: String, qualityName: String,

View file

@ -91,7 +91,7 @@ class KawaiifuProvider : MainAPI() {
val title = soup.selectFirst(".title")!!.text() val title = soup.selectFirst(".title")!!.text()
val tags = soup.select(".table a[href*=\"/tag/\"]").map { tag -> tag.text() } val tags = soup.select(".table a[href*=\"/tag/\"]").map { tag -> tag.text() }
val description = soup.select(".sub-desc p") val description = soup.select(".sub-desc p")
.filter { it.select("strong").isEmpty() && it.select("iframe").isEmpty() } .filter { it -> it.select("strong").isEmpty() && it.select("iframe").isEmpty() }
.joinToString("\n") { it.text() } .joinToString("\n") { it.text() }
val year = url.split("/").filter { it.contains("-") }[0].split("-")[1].toIntOrNull() val year = url.split("/").filter { it.contains("-") }[0].split("-")[1].toIntOrNull()

View file

@ -72,7 +72,7 @@ class WatchCartoonOnlineProvider : MainAPI() {
).text ).text
document = Jsoup.parse(response) document = Jsoup.parse(response)
items = document.select("#catlist-listview2 > ul > li") items = document.select("#catlist-listview2 > ul > li")
.filter { it?.text() != null && !it.text().toString().contains("Episode") } .filter { it -> it?.text() != null && !it.text().toString().contains("Episode") }
for (item in items) { for (item in items) {
val titleHeader = item.selectFirst("a") val titleHeader = item.selectFirst("a")

View file

@ -144,7 +144,7 @@ class AltadefinizioneProvider : MainAPI() {
val doc = app.get(data).document val doc = app.get(data).document
if (doc.select("div.guardahd-player").isNullOrEmpty()) { if (doc.select("div.guardahd-player").isNullOrEmpty()) {
val videoUrl = val videoUrl =
doc.select("input").filter { it.hasAttr("data-mirror") }.last().attr("value") doc.select("input").last { it.hasAttr("data-mirror") }.attr("value")
loadExtractor(videoUrl, data, subtitleCallback, callback) loadExtractor(videoUrl, data, subtitleCallback, callback)
doc.select("#mirrors > li > a").forEach { doc.select("#mirrors > li > a").forEach {
loadExtractor(fixUrl(it.attr("data-target")), data, subtitleCallback, callback) loadExtractor(fixUrl(it.attr("data-target")), data, subtitleCallback, callback)

View file

@ -126,7 +126,7 @@ class CineblogProvider : MainAPI() {
val episodeList = ArrayList<Episode>() val episodeList = ArrayList<Episode>()
document.select("#seasons > div").reversed().map { element -> document.select("#seasons > div").reversed().map { element ->
val season = element.selectFirst("div.se-q > span.se-t")!!.text().toInt() val season = element.selectFirst("div.se-q > span.se-t")!!.text().toInt()
element.select("div.se-a > ul > li").filter { it.text()!="There are still no episodes this season" }.map{ episode -> element.select("div.se-a > ul > li").filter { it -> it.text()!="There are still no episodes this season" }.map{ episode ->
val href = episode.selectFirst("div.episodiotitle > a")!!.attr("href") val href = episode.selectFirst("div.episodiotitle > a")!!.attr("href")
val epNum =episode.selectFirst("div.numerando")!!.text().substringAfter("-").filter { it.isDigit() }.toIntOrNull() val epNum =episode.selectFirst("div.numerando")!!.text().substringAfter("-").filter { it.isDigit() }.toIntOrNull()
val epTitle = episode.selectFirst("div.episodiotitle > a")!!.text() val epTitle = episode.selectFirst("div.episodiotitle > a")!!.text()
@ -160,7 +160,7 @@ class CineblogProvider : MainAPI() {
) )
} else { } else {
val actors: List<ActorData> = val actors: List<ActorData> =
document.select("div.person").filter{it.selectFirst("div.img > a > img")?.attr("src")!!.contains("/no/cast.png").not()}.map { actordata -> document.select("div.person").filter{ it -> it.selectFirst("div.img > a > img")?.attr("src")!!.contains("/no/cast.png").not()}.map { actordata ->
val actorName = actordata.selectFirst("div.data > div.name > a")!!.text() val actorName = actordata.selectFirst("div.data > div.name > a")!!.text()
val actorImage : String? = actordata.selectFirst("div.img > a > img")?.attr("src") val actorImage : String? = actordata.selectFirst("div.img > a > img")?.attr("src")
val roleActor = actordata.selectFirst("div.data > div.caracter")!!.text() val roleActor = actordata.selectFirst("div.data > div.caracter")!!.text()

View file

@ -1,5 +1,4 @@
package com.lagradost.cloudstream3.movieproviders package com.lagradost.cloudstream3.movieproviders
import androidx.core.text.parseAsHtml import androidx.core.text.parseAsHtml
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addRating import com.lagradost.cloudstream3.LoadResponse.Companion.addRating
@ -65,8 +64,7 @@ class FilmpertuttiProvider : MainAPI() {
val url = "$mainUrl/?s=$queryformatted" val url = "$mainUrl/?s=$queryformatted"
val doc = app.get(url).document val doc = app.get(url).document
return doc.select("ul.posts > li").map { return doc.select("ul.posts > li").map {
val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(") val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(").substringBeforeLast("[")
.substringBeforeLast("[")
val link = it.selectFirst("a")!!.attr("href") val link = it.selectFirst("a")!!.attr("href")
val image = it.selectFirst("a")!!.attr("data-thumbnail") val image = it.selectFirst("a")!!.attr("data-thumbnail")
val quality = getQualityFromString(it.selectFirst("div.hd")?.text()) val quality = getQualityFromString(it.selectFirst("div.hd")?.text())
@ -90,58 +88,50 @@ class FilmpertuttiProvider : MainAPI() {
val title = document.selectFirst("#content > h1")!!.text().substringBeforeLast("(") val title = document.selectFirst("#content > h1")!!.text().substringBeforeLast("(")
.substringBeforeLast("[") .substringBeforeLast("[")
val description = val description = document.selectFirst("i.fa.fa-file-text-o.fa-fw")?.parent()?.nextSibling()?.toString()?.parseAsHtml().toString()
document.selectFirst("i.fa.fa-file-text-o.fa-fw")?.parent()?.nextSibling()?.toString()
?.parseAsHtml().toString()
val rating = document.selectFirst("div.rating > div.value")?.text() val rating = document.selectFirst("div.rating > div.value")?.text()
val year = val year =
document.selectFirst("#content > h1")?.text()?.substringAfterLast("(") document.selectFirst("#content > h1")?.text()?.substringAfterLast("(")?.filter { it.isDigit() }?.toIntOrNull() ?:
?.filter { it.isDigit() }?.toIntOrNull() description.substringAfter("trasmessa nel").take(6).filter { it.isDigit() }.toIntOrNull() ?:
?: description.substringAfter("trasmessa nel").take(6).filter { it.isDigit() } (document.selectFirst("i.fa.fa-calendar.fa-fw")?.parent()?.nextSibling() as Element?)?.text()?.substringAfterLast(" ")?.filter { it.isDigit() }?.toIntOrNull()
.toIntOrNull() ?: (document.selectFirst("i.fa.fa-calendar.fa-fw")?.parent()
?.nextSibling() as Element?)?.text()?.substringAfterLast(" ")
?.filter { it.isDigit() }?.toIntOrNull()
val poster = document.selectFirst("div.meta > div > img")?.attr("data-src") val poster = document.selectFirst("div.meta > div > img")?.attr("data-src")
val trailerurl = val trailerurl = document.selectFirst("div.youtube-player")?.attr("data-id")?.let{ urldata->
document.selectFirst("div.youtube-player")?.attr("data-id")?.let { urldata -> "https://www.youtube.com/watch?v=$urldata"
"https://www.youtube.com/watch?v=$urldata" }
}
if (type == TvType.TvSeries) { if (type == TvType.TvSeries) {
val episodeList = ArrayList<Episode>() val episodeList = ArrayList<Episode>()
document.select("div.accordion-item") document.select("div.accordion-item").filter{it.selectFirst("#season > ul > li.s_title > span")!!.text().isNotEmpty()}.map { element ->
.filter { it.selectFirst("#season > ul > li.s_title > span")!!.text().isNotEmpty() } val season =
.map { element -> element.selectFirst("#season > ul > li.s_title > span")!!.text().toInt()
val season = element.select("div.episode-wrap").map { episode ->
element.selectFirst("#season > ul > li.s_title > span")!!.text().toInt() val href =
element.select("div.episode-wrap").map { episode -> episode.select("#links > div > div > table > tbody:nth-child(2) > tr")
val href = .map { it.selectFirst("a")!!.attr("href") }.toJson()
episode.select("#links > div > div > table > tbody:nth-child(2) > tr") val epNum = episode.selectFirst("li.season-no")!!.text().substringAfter("x")
.map { it.selectFirst("a")!!.attr("href") }.toJson() .filter { it.isDigit() }.toIntOrNull()
val epNum = episode.selectFirst("li.season-no")!!.text().substringAfter("x") val epTitle = episode.selectFirst("li.other_link > a")?.text()
.filter { it.isDigit() }.toIntOrNull()
val epTitle = episode.selectFirst("li.other_link > a")?.text()
val posterUrl = episode.selectFirst("figure > img")?.attr("data-src") val posterUrl = episode.selectFirst("figure > img")?.attr("data-src")
episodeList.add( episodeList.add(
Episode( Episode(
href, href,
epTitle, epTitle,
season, season,
epNum, epNum,
posterUrl, posterUrl,
)
) )
} )
} }
}
return newTvSeriesLoadResponse( return newTvSeriesLoadResponse(
title, title,
url, type, episodeList url, type, episodeList
@ -155,12 +145,10 @@ class FilmpertuttiProvider : MainAPI() {
} else { } else {
val urls0 = document.select("div.embed-player") val urls0 = document.select("div.embed-player")
val urls = if (urls0.isNotEmpty()) { val urls = if (urls0.isNotEmpty()){
urls0.map { it.attr("data-id") }.toJson() urls0.map { it.attr("data-id") }.toJson()
} else { }
document.select("#info > ul > li ").mapNotNull { it.selectFirst("a")?.attr("href") } else{ document.select("#info > ul > li ").mapNotNull { it.selectFirst("a")?.attr("href") }.toJson() }
.toJson()
}
return newMovieLoadResponse( return newMovieLoadResponse(
title, title,
@ -178,49 +166,46 @@ class FilmpertuttiProvider : MainAPI() {
} }
} }
// to be updated when UnshortenUrl is ready // to be updated when UnshortenUrl is ready
suspend fun unshorten_linkup(uri: String): String { suspend fun unshorten_linkup(uri: String): String {
var r: NiceResponse? = null var r: NiceResponse? = null
var uri = uri var uri = uri
when { when{
uri.contains("/tv/") -> uri = uri.replace("/tv/", "/tva/") uri.contains("/tv/") -> uri = uri.replace("/tv/", "/tva/")
uri.contains("delta") -> uri = uri.replace("/delta/", "/adelta/") uri.contains("delta") -> uri = uri.replace("/delta/", "/adelta/")
(uri.contains("/ga/") || uri.contains("/ga2/")) -> uri = (uri.contains("/ga/") || uri.contains("/ga2/")) -> uri = base64Decode(uri.split('/').last()).trim()
base64Decode(uri.split('/').last()).trim() uri.contains("/speedx/") -> uri = uri.replace("http://linkup.pro/speedx", "http://speedvideo.net")
uri.contains("/speedx/") -> uri =
uri.replace("http://linkup.pro/speedx", "http://speedvideo.net")
else -> { else -> {
r = app.get(uri, allowRedirects = true) r = app.get(uri, allowRedirects = true)
uri = r.url uri = r.url
val link = val link =
Regex("<iframe[^<>]*src=\\'([^'>]*)\\'[^<>]*>").find(r.text)?.value Regex("<iframe[^<>]*src=\\'([^'>]*)\\'[^<>]*>").find(r.text)?.value ?:
?: Regex("""action="(?:[^/]+.*?/[^/]+/([a-zA-Z0-9_]+))">""").find(r.text)?.value Regex("""action="(?:[^/]+.*?/[^/]+/([a-zA-Z0-9_]+))">""").find(r.text)?.value ?:
?: Regex("""href","((.|\\n)*?)"""").findAll(r.text) Regex("""href","((.|\\n)*?)"""").findAll(r.text).elementAtOrNull(1)?.groupValues?.get(1)
.elementAtOrNull(1)?.groupValues?.get(1)
if (link != null) { if (link!=null) {
uri = link uri = link
} }
} }
} }
val short = Regex("""^https?://.*?(https?://.*)""").find(uri)?.value val short = Regex("""^https?://.*?(https?://.*)""").find(uri)?.value
if (short != null) { if (short!=null){
uri = short uri = short
} }
if (r == null) { if (r==null){
r = app.get( r = app.get(
uri, uri,
allowRedirects = false allowRedirects = false)
) if (r.headers["location"]!= null){
if (r.headers["location"] != null) {
uri = r.headers["location"].toString() uri = r.headers["location"].toString()
} }
} }
if (uri.contains("snip.")) { if (uri.contains("snip.")) {
if (uri.contains("out_generator")) { if (uri.contains("out_generator")) {
uri = Regex("url=(.*)\$").find(uri)!!.value uri = Regex("url=(.*)\$").find(uri)!!.value
} else if (uri.contains("/decode/")) { }
else if (uri.contains("/decode/")) {
uri = app.get(uri, allowRedirects = true).url uri = app.get(uri, allowRedirects = true).url
} }
} }
@ -235,14 +220,16 @@ class FilmpertuttiProvider : MainAPI() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
tryParseJson<List<String>>(data)?.apmap { id -> tryParseJson<List<String>>(data)?.apmap { id ->
if (id.contains("buckler")) { if (id.contains("buckler")){
val id2 = unshorten_linkup(id).trim().replace("/v/", "/e/").replace("/f/", "/e/") val id2 = unshorten_linkup(id).trim().replace("/v/","/e/").replace("/f/","/e/")
loadExtractor(id2, data, subtitleCallback, callback) loadExtractor(id2, data, subtitleCallback, callback)
} else if (id.contains("isecure")) { }
else if (id.contains("isecure")){
val doc1 = app.get(id).document val doc1 = app.get(id).document
val id2 = doc1.selectFirst("iframe")!!.attr("src") val id2 = doc1.selectFirst("iframe")!!.attr("src")
loadExtractor(id2, data, subtitleCallback, callback) loadExtractor(id2, data, subtitleCallback, callback)
} else { }
else{
loadExtractor(id, data, subtitleCallback, callback) loadExtractor(id, data, subtitleCallback, callback)
} }
} }

View file

@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.animeproviders.ZoroProvider import com.lagradost.cloudstream3.animeproviders.ZoroProvider
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
@ -345,7 +347,7 @@ open class SflixProvider : MainAPI() {
"https://ws11.rabbitstream.net/socket.io/?EIO=4&transport=polling" "https://ws11.rabbitstream.net/socket.io/?EIO=4&transport=polling"
if (iframeLink.contains("streamlare", ignoreCase = true)) { if (iframeLink.contains("streamlare", ignoreCase = true)) {
loadExtractor(iframeLink, null,subtitleCallback,callback) loadExtractor(iframeLink, null, subtitleCallback, callback)
} else { } else {
extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it } extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it }
} }
@ -584,7 +586,7 @@ open class SflixProvider : MainAPI() {
} }
// For re-use in Zoro // For re-use in Zoro
private fun Sources.toExtractorLink( private suspend fun Sources.toExtractorLink(
caller: MainAPI, caller: MainAPI,
name: String, name: String,
extractorData: String? = null, extractorData: String? = null,
@ -595,19 +597,38 @@ open class SflixProvider : MainAPI() {
"hls", "hls",
ignoreCase = true ignoreCase = true
) )
if (isM3u8) { return if (isM3u8) {
M3u8Helper().m3u8Generation(M3u8Helper.M3u8Stream(this.file, null), null) suspendSafeApiCall {
.map { stream -> M3u8Helper().m3u8Generation(
ExtractorLink( M3u8Helper.M3u8Stream(
caller.name, this.file,
"${caller.name} $name", null,
stream.streamUrl, mapOf("Referer" to "https://mzzcloud.life/")
caller.mainUrl, ), false
getQualityFromName(stream.quality?.toString()), )
true, .map { stream ->
extractorData = extractorData ExtractorLink(
) caller.name,
} "${caller.name} $name",
stream.streamUrl,
caller.mainUrl,
getQualityFromName(stream.quality?.toString()),
true,
extractorData = extractorData
)
}
} ?: listOf(
// Fallback if m3u8 extractor fails
ExtractorLink(
caller.name,
"${caller.name} $name",
this.file,
caller.mainUrl,
getQualityFromName(this.label),
isM3u8,
extractorData = extractorData
)
)
} else { } else {
listOf( listOf(
ExtractorLink( ExtractorLink(

View file

@ -398,7 +398,7 @@ class StreamingcommunityProvider : MainAPI() {
} }
private fun getM3u8Qualities( private suspend fun getM3u8Qualities(
m3u8Link: String, m3u8Link: String,
referer: String, referer: String,
qualityName: String, qualityName: String,

View file

@ -163,7 +163,7 @@ class TantifilmProvider : MainAPI() {
val actors: List<ActorData>? = if (Linkactor.isNotEmpty()) { val actors: List<ActorData>? = if (Linkactor.isNotEmpty()) {
val actorpage = app.get(Linkactor + "cast/").document val actorpage = app.get(Linkactor + "cast/").document
actorpage.select("article.membro-cast").filter { actorpage.select("article.membro-cast").filter {
it.selectFirst("img") it -> it.selectFirst("img")
?.attr("src") != "https://www.filmtv.it/imgbank/DUMMY/no_portrait.jpg" ?.attr("src") != "https://www.filmtv.it/imgbank/DUMMY/no_portrait.jpg"
}.mapNotNull { }.mapNotNull {
val name = it.selectFirst("div.info > h3")!!.text() val name = it.selectFirst("div.info > h3")!!.text()

View file

@ -12,7 +12,7 @@ import kotlin.math.pow
class M3u8Helper { class M3u8Helper {
companion object { companion object {
private val generator = M3u8Helper() private val generator = M3u8Helper()
fun generateM3u8( suspend fun generateM3u8(
source: String, source: String,
streamUrl: String, streamUrl: String,
referer: String, referer: String,
@ -116,57 +116,52 @@ class M3u8Helper {
return !url.contains("https://") && !url.contains("http://") return !url.contains("https://") && !url.contains("http://")
} }
fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean?): List<M3u8Stream> { suspend fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean?): List<M3u8Stream> {
val generate = sequence { val list = mutableListOf<M3u8Stream>()
val m3u8Parent = getParentLink(m3u8.streamUrl)
val response = runBlocking {
app.get(m3u8.streamUrl, headers = m3u8.headers).text
}
var hasAnyContent = false val m3u8Parent = getParentLink(m3u8.streamUrl)
for (match in QUALITY_REGEX.findAll(response)) { val response = app.get(m3u8.streamUrl, headers = m3u8.headers, verify = false).text
hasAnyContent = true
var (quality, m3u8Link, m3u8Link2) = match.destructured var hasAnyContent = false
if (m3u8Link.isEmpty()) m3u8Link = m3u8Link2 for (match in QUALITY_REGEX.findAll(response)) {
if (absoluteExtensionDetermination(m3u8Link) == "m3u8") { hasAnyContent = true
if (isNotCompleteUrl(m3u8Link)) { var (quality, m3u8Link, m3u8Link2) = match.destructured
m3u8Link = "$m3u8Parent/$m3u8Link" if (m3u8Link.isEmpty()) m3u8Link = m3u8Link2
} if (absoluteExtensionDetermination(m3u8Link) == "m3u8") {
if (quality.isEmpty()) { if (isNotCompleteUrl(m3u8Link)) {
println(m3u8.streamUrl) m3u8Link = "$m3u8Parent/$m3u8Link"
}
yieldAll(
m3u8Generation(
M3u8Stream(
m3u8Link,
quality.toIntOrNull(),
m3u8.headers
), false
)
)
} }
yield( if (quality.isEmpty()) {
println(m3u8.streamUrl)
}
list += m3u8Generation(
M3u8Stream( M3u8Stream(
m3u8Link, m3u8Link,
quality.toIntOrNull(), quality.toIntOrNull(),
m3u8.headers m3u8.headers
) ), false
)
}
if (returnThis ?: !hasAnyContent) {
yield(
M3u8Stream(
m3u8.streamUrl,
Qualities.Unknown.value,
m3u8.headers
)
) )
} }
list += M3u8Stream(
m3u8Link,
quality.toIntOrNull(),
m3u8.headers
)
} }
return generate.toList() if (returnThis ?: !hasAnyContent) {
list += M3u8Stream(
m3u8.streamUrl,
Qualities.Unknown.value,
m3u8.headers
)
}
return list
} }
data class HlsDownloadData( data class HlsDownloadData(
val bytes: ByteArray, val bytes: ByteArray,
val currentIndex: Int, val currentIndex: Int,
@ -174,7 +169,7 @@ class M3u8Helper {
val errored: Boolean = false val errored: Boolean = false
) )
fun hlsYield(qualities: List<M3u8Stream>, startIndex: Int = 0): Iterator<HlsDownloadData> { suspend fun hlsYield(qualities: List<M3u8Stream>, startIndex: Int = 0): Iterator<HlsDownloadData> {
if (qualities.isEmpty()) return listOf( if (qualities.isEmpty()) return listOf(
HlsDownloadData( HlsDownloadData(
byteArrayOf(), byteArrayOf(),
@ -196,7 +191,13 @@ class M3u8Helper {
val secondSelection = selectBest(streams.ifEmpty { listOf(selected) }) val secondSelection = selectBest(streams.ifEmpty { listOf(selected) })
if (secondSelection != null) { if (secondSelection != null) {
val m3u8Response = val m3u8Response =
runBlocking { app.get(secondSelection.streamUrl, headers = headers).text } runBlocking {
app.get(
secondSelection.streamUrl,
headers = headers,
verify = false
).text
}
var encryptionUri: String? var encryptionUri: String?
var encryptionIv = byteArrayOf() var encryptionIv = byteArrayOf()
@ -215,7 +216,7 @@ class M3u8Helper {
encryptionIv = match.component3().toByteArray() encryptionIv = match.component3().toByteArray()
val encryptionKeyResponse = val encryptionKeyResponse =
runBlocking { app.get(encryptionUri, headers = headers) } runBlocking { app.get(encryptionUri, headers = headers, verify = false) }
encryptionData = encryptionKeyResponse.body?.bytes() ?: byteArrayOf() encryptionData = encryptionKeyResponse.body?.bytes() ?: byteArrayOf()
} }
@ -238,7 +239,8 @@ class M3u8Helper {
while (lastYield != c) { while (lastYield != c) {
try { try {
val tsResponse = runBlocking { app.get(url, headers = headers) } val tsResponse =
runBlocking { app.get(url, headers = headers, verify = false) }
var tsData = tsResponse.body?.bytes() ?: byteArrayOf() var tsData = tsResponse.body?.bytes() ?: byteArrayOf()
if (encryptionState) { if (encryptionState) {

View file

@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.internal.closeQuietly import okhttp3.internal.closeQuietly
import java.io.BufferedInputStream import java.io.BufferedInputStream
@ -1129,7 +1130,9 @@ object VideoDownloadManager {
if (!stream.resume!!) realIndex = 0 if (!stream.resume!!) realIndex = 0
val fileLengthAdd = stream.fileLength!! val fileLengthAdd = stream.fileLength!!
val tsIterator = m3u8Helper.hlsYield(listOf(m3u8), realIndex) val tsIterator = runBlocking {
m3u8Helper.hlsYield(listOf(m3u8), realIndex)
}
val displayName = getDisplayName(name, extension) val displayName = getDisplayName(name, extension)