diff --git a/app/build.gradle b/app/build.gradle index 185a081a..49b881b7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -108,7 +108,7 @@ dependencies { //implementation "io.karn:khttp-android:0.1.2" //okhttp instead // 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" @@ -156,7 +156,7 @@ dependencies { // Networking // implementation "com.squareup.okhttp3:okhttp:4.9.2" // 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 🙏 implementation "com.github.tachiyomiorg:unifile:17bec43" diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 73c346d0..86a0aafe 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -21,6 +21,9 @@ import androidx.navigation.NavOptions import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController 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.material.navigationrail.NavigationRailView 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.USER_PROVIDER_API import com.lagradost.nicehttp.Requests +import com.lagradost.nicehttp.ResponseParser import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_result_swipe.* import kotlinx.coroutines.Dispatchers @@ -83,6 +87,7 @@ import kotlinx.coroutines.withContext import org.schabi.newpipe.extractor.NewPipe import java.io.File import kotlin.concurrent.thread +import kotlin.reflect.KClass 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" // 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 parse(text: String, kClass: KClass): T { + return mapper.readValue(text, kClass.java) + } + + override fun parseSafe(text: String, kClass: KClass): 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) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt index c9e2e8fe..fe207cd3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt @@ -312,7 +312,7 @@ class AllAnimeProvider : MainAPI() { @JsonProperty("episodeIframeHead") val episodeIframeHead: String ) - private fun getM3u8Qualities( + private suspend fun getM3u8Qualities( m3u8Link: String, referer: String, qualityName: String, diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt index 812ce5c0..97c36e7d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt @@ -91,7 +91,7 @@ class KawaiifuProvider : MainAPI() { val title = soup.selectFirst(".title")!!.text() val tags = soup.select(".table a[href*=\"/tag/\"]").map { tag -> tag.text() } 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() } val year = url.split("/").filter { it.contains("-") }[0].split("-")[1].toIntOrNull() diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WatchCartoonOnlineProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WatchCartoonOnlineProvider.kt index ef393089..e3be650b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WatchCartoonOnlineProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WatchCartoonOnlineProvider.kt @@ -72,7 +72,7 @@ class WatchCartoonOnlineProvider : MainAPI() { ).text document = Jsoup.parse(response) 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) { val titleHeader = item.selectFirst("a") diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AltadefinizioneProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AltadefinizioneProvider.kt index ba689f97..63015bd1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AltadefinizioneProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/AltadefinizioneProvider.kt @@ -144,7 +144,7 @@ class AltadefinizioneProvider : MainAPI() { val doc = app.get(data).document if (doc.select("div.guardahd-player").isNullOrEmpty()) { 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) doc.select("#mirrors > li > a").forEach { loadExtractor(fixUrl(it.attr("data-target")), data, subtitleCallback, callback) diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CineblogProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CineblogProvider.kt index 9eff8cce..f843fce0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CineblogProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/CineblogProvider.kt @@ -126,7 +126,7 @@ class CineblogProvider : MainAPI() { val episodeList = ArrayList() document.select("#seasons > div").reversed().map { element -> 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 epNum =episode.selectFirst("div.numerando")!!.text().substringAfter("-").filter { it.isDigit() }.toIntOrNull() val epTitle = episode.selectFirst("div.episodiotitle > a")!!.text() @@ -160,7 +160,7 @@ class CineblogProvider : MainAPI() { ) } else { val actors: List = - 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 actorImage : String? = actordata.selectFirst("div.img > a > img")?.attr("src") val roleActor = actordata.selectFirst("div.data > div.caracter")!!.text() diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmpertuttiProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmpertuttiProvider.kt index 3f14622b..6741640c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmpertuttiProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/FilmpertuttiProvider.kt @@ -1,5 +1,4 @@ package com.lagradost.cloudstream3.movieproviders - import androidx.core.text.parseAsHtml import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addRating @@ -65,8 +64,7 @@ class FilmpertuttiProvider : MainAPI() { val url = "$mainUrl/?s=$queryformatted" val doc = app.get(url).document return doc.select("ul.posts > li").map { - val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(") - .substringBeforeLast("[") + val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(").substringBeforeLast("[") val link = it.selectFirst("a")!!.attr("href") val image = it.selectFirst("a")!!.attr("data-thumbnail") val quality = getQualityFromString(it.selectFirst("div.hd")?.text()) @@ -90,58 +88,50 @@ class FilmpertuttiProvider : MainAPI() { val title = document.selectFirst("#content > h1")!!.text().substringBeforeLast("(") .substringBeforeLast("[") - val description = - document.selectFirst("i.fa.fa-file-text-o.fa-fw")?.parent()?.nextSibling()?.toString() - ?.parseAsHtml().toString() + val description = 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 year = - document.selectFirst("#content > h1")?.text()?.substringAfterLast("(") - ?.filter { it.isDigit() }?.toIntOrNull() - ?: description.substringAfter("trasmessa nel").take(6).filter { it.isDigit() } - .toIntOrNull() ?: (document.selectFirst("i.fa.fa-calendar.fa-fw")?.parent() - ?.nextSibling() as Element?)?.text()?.substringAfterLast(" ") - ?.filter { it.isDigit() }?.toIntOrNull() + document.selectFirst("#content > h1")?.text()?.substringAfterLast("(")?.filter { it.isDigit() }?.toIntOrNull() ?: + description.substringAfter("trasmessa nel").take(6).filter { it.isDigit() }.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 trailerurl = - document.selectFirst("div.youtube-player")?.attr("data-id")?.let { urldata -> - "https://www.youtube.com/watch?v=$urldata" - } + val trailerurl = document.selectFirst("div.youtube-player")?.attr("data-id")?.let{ urldata-> + "https://www.youtube.com/watch?v=$urldata" + } if (type == TvType.TvSeries) { val episodeList = ArrayList() - document.select("div.accordion-item") - .filter { it.selectFirst("#season > ul > li.s_title > span")!!.text().isNotEmpty() } - .map { element -> - val season = - element.selectFirst("#season > ul > li.s_title > span")!!.text().toInt() - element.select("div.episode-wrap").map { episode -> - val href = - episode.select("#links > div > div > table > tbody:nth-child(2) > tr") - .map { it.selectFirst("a")!!.attr("href") }.toJson() - val epNum = episode.selectFirst("li.season-no")!!.text().substringAfter("x") - .filter { it.isDigit() }.toIntOrNull() - val epTitle = episode.selectFirst("li.other_link > a")?.text() + document.select("div.accordion-item").filter{it.selectFirst("#season > ul > li.s_title > span")!!.text().isNotEmpty()}.map { element -> + val season = + element.selectFirst("#season > ul > li.s_title > span")!!.text().toInt() + element.select("div.episode-wrap").map { episode -> + val href = + episode.select("#links > div > div > table > tbody:nth-child(2) > tr") + .map { it.selectFirst("a")!!.attr("href") }.toJson() + val epNum = episode.selectFirst("li.season-no")!!.text().substringAfter("x") + .filter { it.isDigit() }.toIntOrNull() + val epTitle = episode.selectFirst("li.other_link > a")?.text() - val posterUrl = episode.selectFirst("figure > img")?.attr("data-src") - episodeList.add( - Episode( - href, - epTitle, - season, - epNum, - posterUrl, - ) + val posterUrl = episode.selectFirst("figure > img")?.attr("data-src") + episodeList.add( + Episode( + href, + epTitle, + season, + epNum, + posterUrl, ) - } + ) } + } return newTvSeriesLoadResponse( title, url, type, episodeList @@ -155,12 +145,10 @@ class FilmpertuttiProvider : MainAPI() { } else { val urls0 = document.select("div.embed-player") - val urls = if (urls0.isNotEmpty()) { - urls0.map { it.attr("data-id") }.toJson() - } else { - document.select("#info > ul > li ").mapNotNull { it.selectFirst("a")?.attr("href") } - .toJson() - } + val urls = if (urls0.isNotEmpty()){ + urls0.map { it.attr("data-id") }.toJson() + } + else{ document.select("#info > ul > li ").mapNotNull { it.selectFirst("a")?.attr("href") }.toJson() } return newMovieLoadResponse( 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 { var r: NiceResponse? = null var uri = uri - when { + when{ uri.contains("/tv/") -> uri = uri.replace("/tv/", "/tva/") uri.contains("delta") -> uri = uri.replace("/delta/", "/adelta/") - (uri.contains("/ga/") || uri.contains("/ga2/")) -> uri = - base64Decode(uri.split('/').last()).trim() - uri.contains("/speedx/") -> uri = - uri.replace("http://linkup.pro/speedx", "http://speedvideo.net") + (uri.contains("/ga/") || uri.contains("/ga2/")) -> uri = base64Decode(uri.split('/').last()).trim() + uri.contains("/speedx/") -> uri = uri.replace("http://linkup.pro/speedx", "http://speedvideo.net") else -> { r = app.get(uri, allowRedirects = true) uri = r.url val link = - Regex("]*src=\\'([^'>]*)\\'[^<>]*>").find(r.text)?.value - ?: Regex("""action="(?:[^/]+.*?/[^/]+/([a-zA-Z0-9_]+))">""").find(r.text)?.value - ?: Regex("""href","((.|\\n)*?)"""").findAll(r.text) - .elementAtOrNull(1)?.groupValues?.get(1) + Regex("]*src=\\'([^'>]*)\\'[^<>]*>").find(r.text)?.value ?: + Regex("""action="(?:[^/]+.*?/[^/]+/([a-zA-Z0-9_]+))">""").find(r.text)?.value ?: + Regex("""href","((.|\\n)*?)"""").findAll(r.text).elementAtOrNull(1)?.groupValues?.get(1) - if (link != null) { + if (link!=null) { uri = link } } } val short = Regex("""^https?://.*?(https?://.*)""").find(uri)?.value - if (short != null) { + if (short!=null){ uri = short } - if (r == null) { + if (r==null){ r = app.get( uri, - allowRedirects = false - ) - if (r.headers["location"] != null) { + allowRedirects = false) + if (r.headers["location"]!= null){ uri = r.headers["location"].toString() } } if (uri.contains("snip.")) { if (uri.contains("out_generator")) { uri = Regex("url=(.*)\$").find(uri)!!.value - } else if (uri.contains("/decode/")) { + } + else if (uri.contains("/decode/")) { uri = app.get(uri, allowRedirects = true).url } } @@ -235,14 +220,16 @@ class FilmpertuttiProvider : MainAPI() { callback: (ExtractorLink) -> Unit ): Boolean { tryParseJson>(data)?.apmap { id -> - if (id.contains("buckler")) { - val id2 = unshorten_linkup(id).trim().replace("/v/", "/e/").replace("/f/", "/e/") + if (id.contains("buckler")){ + val id2 = unshorten_linkup(id).trim().replace("/v/","/e/").replace("/f/","/e/") loadExtractor(id2, data, subtitleCallback, callback) - } else if (id.contains("isecure")) { + } + else if (id.contains("isecure")){ val doc1 = app.get(id).document val id2 = doc1.selectFirst("iframe")!!.attr("src") loadExtractor(id2, data, subtitleCallback, callback) - } else { + } + else{ loadExtractor(id, data, subtitleCallback, callback) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt index bd61b164..46dbe081 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt @@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addActors import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer 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.utils.AppUtils.parseJson 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" if (iframeLink.contains("streamlare", ignoreCase = true)) { - loadExtractor(iframeLink, null,subtitleCallback,callback) + loadExtractor(iframeLink, null, subtitleCallback, callback) } else { extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it } } @@ -584,7 +586,7 @@ open class SflixProvider : MainAPI() { } // For re-use in Zoro - private fun Sources.toExtractorLink( + private suspend fun Sources.toExtractorLink( caller: MainAPI, name: String, extractorData: String? = null, @@ -595,19 +597,38 @@ open class SflixProvider : MainAPI() { "hls", ignoreCase = true ) - if (isM3u8) { - M3u8Helper().m3u8Generation(M3u8Helper.M3u8Stream(this.file, null), null) - .map { stream -> - ExtractorLink( - caller.name, - "${caller.name} $name", - stream.streamUrl, - caller.mainUrl, - getQualityFromName(stream.quality?.toString()), - true, - extractorData = extractorData - ) - } + return if (isM3u8) { + suspendSafeApiCall { + M3u8Helper().m3u8Generation( + M3u8Helper.M3u8Stream( + this.file, + null, + mapOf("Referer" to "https://mzzcloud.life/") + ), false + ) + .map { stream -> + 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 { listOf( ExtractorLink( diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/StreamingcommunityProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/StreamingcommunityProvider.kt index 5f3ae345..27788444 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/StreamingcommunityProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/StreamingcommunityProvider.kt @@ -398,7 +398,7 @@ class StreamingcommunityProvider : MainAPI() { } - private fun getM3u8Qualities( + private suspend fun getM3u8Qualities( m3u8Link: String, referer: String, qualityName: String, diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TantiFilmProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TantiFilmProvider.kt index e84e6ddf..2bc341c3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TantiFilmProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TantiFilmProvider.kt @@ -163,7 +163,7 @@ class TantifilmProvider : MainAPI() { val actors: List? = if (Linkactor.isNotEmpty()) { val actorpage = app.get(Linkactor + "cast/").document actorpage.select("article.membro-cast").filter { - it.selectFirst("img") + it -> it.selectFirst("img") ?.attr("src") != "https://www.filmtv.it/imgbank/DUMMY/no_portrait.jpg" }.mapNotNull { val name = it.selectFirst("div.info > h3")!!.text() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/M3u8Helper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/M3u8Helper.kt index d8065f0c..c309d36e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/M3u8Helper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/M3u8Helper.kt @@ -12,7 +12,7 @@ import kotlin.math.pow class M3u8Helper { companion object { private val generator = M3u8Helper() - fun generateM3u8( + suspend fun generateM3u8( source: String, streamUrl: String, referer: String, @@ -116,57 +116,52 @@ class M3u8Helper { return !url.contains("https://") && !url.contains("http://") } - fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean?): List { - val generate = sequence { - val m3u8Parent = getParentLink(m3u8.streamUrl) - val response = runBlocking { - app.get(m3u8.streamUrl, headers = m3u8.headers).text - } + suspend fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean?): List { + val list = mutableListOf() - var hasAnyContent = false - for (match in QUALITY_REGEX.findAll(response)) { - hasAnyContent = true + val m3u8Parent = getParentLink(m3u8.streamUrl) + val response = app.get(m3u8.streamUrl, headers = m3u8.headers, verify = false).text - var (quality, m3u8Link, m3u8Link2) = match.destructured - if (m3u8Link.isEmpty()) m3u8Link = m3u8Link2 - if (absoluteExtensionDetermination(m3u8Link) == "m3u8") { - if (isNotCompleteUrl(m3u8Link)) { - m3u8Link = "$m3u8Parent/$m3u8Link" - } - if (quality.isEmpty()) { - println(m3u8.streamUrl) - } - yieldAll( - m3u8Generation( - M3u8Stream( - m3u8Link, - quality.toIntOrNull(), - m3u8.headers - ), false - ) - ) + var hasAnyContent = false + for (match in QUALITY_REGEX.findAll(response)) { + hasAnyContent = true + var (quality, m3u8Link, m3u8Link2) = match.destructured + if (m3u8Link.isEmpty()) m3u8Link = m3u8Link2 + if (absoluteExtensionDetermination(m3u8Link) == "m3u8") { + if (isNotCompleteUrl(m3u8Link)) { + m3u8Link = "$m3u8Parent/$m3u8Link" } - yield( + if (quality.isEmpty()) { + println(m3u8.streamUrl) + } + list += m3u8Generation( M3u8Stream( m3u8Link, quality.toIntOrNull(), m3u8.headers - ) - ) - } - if (returnThis ?: !hasAnyContent) { - yield( - M3u8Stream( - m3u8.streamUrl, - Qualities.Unknown.value, - m3u8.headers - ) + ), false ) + } + 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( val bytes: ByteArray, val currentIndex: Int, @@ -174,7 +169,7 @@ class M3u8Helper { val errored: Boolean = false ) - fun hlsYield(qualities: List, startIndex: Int = 0): Iterator { + suspend fun hlsYield(qualities: List, startIndex: Int = 0): Iterator { if (qualities.isEmpty()) return listOf( HlsDownloadData( byteArrayOf(), @@ -196,7 +191,13 @@ class M3u8Helper { val secondSelection = selectBest(streams.ifEmpty { listOf(selected) }) if (secondSelection != null) { val m3u8Response = - runBlocking { app.get(secondSelection.streamUrl, headers = headers).text } + runBlocking { + app.get( + secondSelection.streamUrl, + headers = headers, + verify = false + ).text + } var encryptionUri: String? var encryptionIv = byteArrayOf() @@ -215,7 +216,7 @@ class M3u8Helper { encryptionIv = match.component3().toByteArray() val encryptionKeyResponse = - runBlocking { app.get(encryptionUri, headers = headers) } + runBlocking { app.get(encryptionUri, headers = headers, verify = false) } encryptionData = encryptionKeyResponse.body?.bytes() ?: byteArrayOf() } @@ -238,7 +239,8 @@ class M3u8Helper { while (lastYield != c) { 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() if (encryptionState) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt index 9450175b..c4d05cfe 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt @@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import okhttp3.internal.closeQuietly import java.io.BufferedInputStream @@ -1129,7 +1130,9 @@ object VideoDownloadManager { if (!stream.resume!!) realIndex = 0 val fileLengthAdd = stream.fileLength!! - val tsIterator = m3u8Helper.hlsYield(listOf(m3u8), realIndex) + val tsIterator = runBlocking { + m3u8Helper.hlsYield(listOf(m3u8), realIndex) + } val displayName = getDisplayName(name, extension)