diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0379aae9..702406c7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ package="com.lagradost.cloudstream3"> - + @@ -53,13 +53,19 @@ + + + + + + + @@ -68,11 +74,15 @@ + + + + @@ -84,15 +94,15 @@ - - + + @@ -104,39 +114,71 @@ - + - + + + + - - - - + + + + + + + + + + + + + + + - + + + + - + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt index 6c7318e1..dc1bbba3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt @@ -1,5 +1,7 @@ package com.lagradost.cloudstream3.ui.player +import android.content.Intent +import android.net.Uri import android.os.Bundle import android.util.Log import android.view.KeyEvent @@ -35,6 +37,34 @@ class DownloadedPlayerActivity : AppCompatActivity() { finish() } + private fun playLink(url: String) { + this.navigate( + R.id.global_to_navigation_player, GeneratorPlayer.newInstance( + LinkGenerator( + listOf( + url + ) + ) + ) + ) + } + + private fun playUri(uri: Uri) { + val name = UniFile.fromUri(this, uri).name + this.navigate( + R.id.global_to_navigation_player, GeneratorPlayer.newInstance( + DownloadFileGenerator( + listOf( + ExtractorUri( + uri = uri, + name = name ?: getString(R.string.downloaded_file) + ) + ) + ) + ) + ) + } + override fun onCreate(savedInstanceState: Bundle?) { Log.i(DTAG, "onCreate") @@ -45,69 +75,35 @@ class DownloadedPlayerActivity : AppCompatActivity() { setContentView(R.layout.empty_layout) val data = intent.data - if (data == null) { + + if (intent?.action == Intent.ACTION_SEND) { + val extraText = try { // I dont trust android + intent.getStringExtra(Intent.EXTRA_TEXT) + } catch (e: Exception) { + null + } + val cd = intent.clipData + val item = if (cd != null && cd.itemCount > 0) cd.getItemAt(0) else null + val url = item?.text?.toString() + + // idk what I am doing, just hope any of these work + if (item?.uri != null) + playUri(item.uri) + else if (url != null) + playLink(url) + else if (data != null) + playUri(data) + else if (extraText != null) + playLink(extraText) + else { + finish() + return + } + } else if (data?.scheme == "content") { + playUri(data) + } else { finish() return } - - if (data.scheme == "content") { - val name = UniFile.fromUri(this, data).name - this.navigate( - R.id.global_to_navigation_player, GeneratorPlayer.newInstance( - DownloadFileGenerator( - listOf( - ExtractorUri( - uri = data, - name = name ?: getString(R.string.downloaded_file) - ) - ) - ) - ) - ) - } - - // Legacy code, seems to work perfectly fine without it - -// } else { -// val uri = getUri(intent.data) -// if (uri == null) { -// finish() -// return -// } -// val path = uri.path -// // Because it doesn't get the path when it's downloaded, I have no idea -// val realPath = if (File( -// intent.data?.path?.removePrefix("/file") ?: "NONE" -// ).exists() -// ) intent.data?.path?.removePrefix("/file") else path -// -// if (realPath == null) { -// finish() -// return -// } -// -// val name = try { -// File(realPath).name -// } catch (e: Exception) { -// "NULL" -// } -// -// val tryUri = try { -// AppUtils.getVideoContentUri(this, realPath) ?: uri -// } catch (e: Exception) { -// logError(e) -// uri -// } -// -// setContentView(R.layout.empty_layout) -// Log.i(DTAG, "navigating") -// -// //TODO add relative path for subs -// this.navigate( -// R.id.global_to_navigation_player, GeneratorPlayer.newInstance( -// DownloadFileGenerator(listOf(ExtractorUri(uri = tryUri, name = name))) -// ) -// ) -// } } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt index 27bf1435..d604e2f8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt @@ -1,10 +1,7 @@ package com.lagradost.cloudstream3.ui.player import com.lagradost.cloudstream3.apmap -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.ExtractorUri -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.cloudstream3.utils.* class LinkGenerator( private val links: List, @@ -50,12 +47,13 @@ class LinkGenerator( if (!extract || !loadExtractor(link, referer) { callback(it to null) }) { + // if don't extract or if no extractor found simply return the link callback( ExtractorLink( "", link, - link, + unshortenLinkSafe(link), // unshorten because it might be a raw link referer ?: "", Qualities.Unknown.value, link.contains(".m3u") // TODO USE REAL PARSER ) to null diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 110c72e3..be0ca1ad 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -5,6 +5,7 @@ import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.USER_AGENT import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.extractors.* +import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import kotlinx.coroutines.delay import org.jsoup.Jsoup @@ -143,6 +144,17 @@ fun getAndUnpack(string: String): String { return JsUnpacker(packedText).unpack() ?: string } +suspend fun unshortenLinkSafe(url : String) : String { + return try { + if (ShortLink.isShortLink(url)) + ShortLink.unshorten(url) + else url + } catch (e: Exception) { + logError(e) + url + } +} + /** * Tries to load the appropriate extractor based on link, returns true if any extractor is loaded. * */ @@ -151,14 +163,17 @@ suspend fun loadExtractor( referer: String? = null, callback: (ExtractorLink) -> Unit ): Boolean { + val currentUrl = unshortenLinkSafe(url) + for (extractor in extractorApis) { - if (url.replace(schemaStripRegex, "") + if (currentUrl.replace(schemaStripRegex, "") .startsWith(extractor.mainUrl.replace(schemaStripRegex, "")) ) { - extractor.getSafeUrl(url, referer)?.forEach(callback) + extractor.getSafeUrl(currentUrl, referer)?.forEach(callback) return true } } + return false } @@ -166,9 +181,11 @@ suspend fun loadExtractor( url: String, referer: String? = null, ): List { + val currentUrl = unshortenLinkSafe(url) + for (extractor in extractorApis) { - if (url.startsWith(extractor.mainUrl)) { - return extractor.getSafeUrl(url, referer) ?: emptyList() + if (currentUrl.startsWith(extractor.mainUrl)) { + return extractor.getSafeUrl(currentUrl, referer) ?: emptyList() } } return emptyList() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UnshortenUrl.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UnshortenUrl.kt index 119e4a00..d0d3298d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UnshortenUrl.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UnshortenUrl.kt @@ -1,4 +1,5 @@ package com.lagradost.cloudstream3.utils + import android.util.Base64 import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.base64Decode @@ -8,163 +9,182 @@ import java.net.URLDecoder //Code heavily based on unshortenit.py form kodiondemand /addon -private data class ShortUrl( - val regex: Regex, - val type: String, - val function: suspend (String) -> String, -) { - constructor(regex: String, type: String, function: suspend (String) -> String) : this( - Regex(regex), - type, - function - ) - companion object { - val adflyRegex = """adf\.ly|j\.gs|q\.gs|u\.bb|ay\.gy|atominik\.com|tinyium\.com|microify\.com|threadsphere\.bid|clearload\.bid|activetect\.net|swiftviz\.net|briskgram\.net|activetect\.net|stoodsef\.com|baymaleti\.net|thouth\.net|uclaut\.net|gloyah\.net|larati\.net|scuseami\.net""" - val linkupRegex = """linkup\.pro|buckler.link""" - val linksafeRegex = """linksafe\.cc""" - val nuovoIndirizzoRegex = """mixdrop\.nuovoindirizzo\.com""" - val nuovoLinkRegex = """nuovolink\.com""" - val uprotRegex = """uprot\.net""" - val davisonbarkerRegex = """davisonbarker\.pro|lowrihouston\.pro""" - - val shortList = listOf( - ShortUrl(adflyRegex,"adfly",::unshortenAdfly), - ShortUrl(linkupRegex,"linkup",::unshortenLinkup), - ShortUrl(linksafeRegex,"linksafe",::unshortenLinksafe), - ShortUrl(nuovoIndirizzoRegex,"nuovoindirizzo",::unshortenNuovoIndirizzo), - ShortUrl(nuovoLinkRegex,"nuovolink",::unshortenNuovoLink), - ShortUrl(uprotRegex,"uprot",::unshortenUprot), - ShortUrl(davisonbarkerRegex,"uprot",::unshortenDavisonbarker), +object ShortLink { + data class ShortUrl( + val regex: Regex, + val type: String, + val function: suspend (String) -> String, + ) { + constructor(regex: String, type: String, function: suspend (String) -> String) : this( + Regex(regex), + type, + function ) } -} -class ShortLink{ - fun isShortLink(url : String) : Boolean{ - return ShortUrl.shortList.firstOrNull { - it.regex.find(url) != null}!=null + private val adflyRegex = + """adf\.ly|j\.gs|q\.gs|u\.bb|ay\.gy|atominik\.com|tinyium\.com|microify\.com|threadsphere\.bid|clearload\.bid|activetect\.net|swiftviz\.net|briskgram\.net|activetect\.net|stoodsef\.com|baymaleti\.net|thouth\.net|uclaut\.net|gloyah\.net|larati\.net|scuseami\.net""" + private val linkupRegex = """linkup\.pro|buckler.link""" + private val linksafeRegex = """linksafe\.cc""" + private val nuovoIndirizzoRegex = """mixdrop\.nuovoindirizzo\.com""" + private val nuovoLinkRegex = """nuovolink\.com""" + private val uprotRegex = """uprot\.net""" + private val davisonbarkerRegex = """davisonbarker\.pro|lowrihouston\.pro""" + + private val shortList = listOf( + ShortUrl(adflyRegex, "adfly", ::unshortenAdfly), + ShortUrl(linkupRegex, "linkup", ::unshortenLinkup), + ShortUrl(linksafeRegex, "linksafe", ::unshortenLinksafe), + ShortUrl(nuovoIndirizzoRegex, "nuovoindirizzo", ::unshortenNuovoIndirizzo), + ShortUrl(nuovoLinkRegex, "nuovolink", ::unshortenNuovoLink), + ShortUrl(uprotRegex, "uprot", ::unshortenUprot), + ShortUrl(davisonbarkerRegex, "uprot", ::unshortenDavisonbarker), + ) + + fun isShortLink(url: String): Boolean { + return shortList.any { + it.regex.find(url) != null + } } + suspend fun unshorten(uri: String, type: String? = null): String { var currentUrl = uri while (true) { val oldurl = currentUrl - val domain = URI(currentUrl).host ?: throw IllegalArgumentException("No domain found in URI!") - currentUrl = ShortUrl.shortList.firstOrNull { + val domain = + URI(currentUrl).host ?: throw IllegalArgumentException("No domain found in URI!") + currentUrl = shortList.firstOrNull { it.regex.find(domain) != null || type == it.type }?.function?.let { it(currentUrl) } ?: break - if (oldurl == currentUrl){ + if (oldurl == currentUrl) { break } } return currentUrl } -} -suspend fun unshortenAdfly(uri: String): String{ - val html = app.get(uri).text - val ysmm = Regex("""var ysmm =.*\;?""").find(html)!!.value - if (ysmm.isNotEmpty()){ - var left = "" - var right = "" + suspend fun unshortenAdfly(uri: String): String { + val html = app.get(uri).text + val ysmm = Regex("""var ysmm =.*;?""").find(html)!!.value + + if (ysmm.isNotEmpty()) { + var left = "" + var right = "" - for (c in ysmm.replace(Regex("""var ysmm \= \'|\'\;"""),"").chunked(2).dropLastWhile {it.length==1}) { - left += c[0] - right = c[1] + right + for (c in ysmm.replace(Regex("""var ysmm = '|';"""), "").chunked(2) + .dropLastWhile { it.length == 1 }) { + left += c[0] + right = c[1] + right + } + val encodedUri = (left + right).toMutableList() + val numbers = + encodedUri.mapIndexed { i, n -> Pair(i, n) }.filter { it.second.isDigit() } + for (el in numbers.chunked(2).dropLastWhile { it.size == 1 }) { + val xor = (el[0].second).code.xor(el[1].second.code) + if (xor < 10) { + encodedUri[el[0].first] = xor.digitToChar() + } + } + val encodedbytearray = encodedUri.map { it.code.toByte() }.toByteArray() + var decodedUri = + Base64.decode(encodedbytearray, Base64.DEFAULT).decodeToString().dropLast(16) + .drop(16) + + if (Regex("""go\.php\?u=""").find(decodedUri) != null) { + decodedUri = + Base64.decode(decodedUri.replace(Regex("""(.*?)u="""), ""), Base64.DEFAULT) + .decodeToString() + } + + return decodedUri + } else { + return uri } - val encodedUri = (left + right).toMutableList() - val numbers = encodedUri.mapIndexed{ i, n-> Pair(i,n)}.filter{it.second.isDigit()} - for (el in numbers.chunked(2).dropLastWhile {it.size==1}){ - val xor = (el[0].second).code.xor(el[1].second.code) - if (xor < 10){ - encodedUri[el[0].first] = xor.digitToChar() + } + + suspend fun unshortenLinkup(uri: String): String { + var r: NiceResponse? = null + var uri = uri + 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") + 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) + + if (link != null) { + uri = link + } } } - val encodedbytearray = encodedUri.map { it.code.toByte() }.toByteArray() - var decodedUri = Base64.decode(encodedbytearray,Base64.DEFAULT).decodeToString().dropLast(16).drop(16) - if (Regex("""go\.php\?u\=""").find(decodedUri) != null){ - decodedUri = Base64.decode(decodedUri.replace(Regex("""(.*?)u="""),""),Base64.DEFAULT).decodeToString() + val short = Regex("""^https?://.*?(https?://.*)""").find(uri)?.value + if (short != null) { + uri = short + } + if (r == null) { + r = app.get( + uri, + 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/")) { + uri = app.get(uri, allowRedirects = true).url + } } - - return decodedUri - } - else { return uri } - return uri -} -suspend fun unshortenLinkup(uri: String): String { - var r: NiceResponse? = null - var uri = uri - 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") - 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) - if (link!=null) { - uri = link + fun unshortenLinksafe(uri: String): String { + return base64Decode(uri.split("?url=").last()) + } + + suspend fun unshortenNuovoIndirizzo(uri: String): String { + val soup = app.get(uri, allowRedirects = true) + val header = soup.headers["refresh"] + val link: String = if (header != null) { + soup.headers["refresh"]!!.substringAfter("=") + } else { + "non trovato" + } + return link + } + + suspend fun unshortenNuovoLink(uri: String): String { + return app.get(uri, allowRedirects = true).document.selectFirst("a")!!.attr("href") + + } + + suspend fun unshortenUprot(uri: String): String { + val page = app.get(uri).text + Regex("""]+href="([^"]+)""").findAll(page) + .map { it.value.replace(""" + if (link.contains("https://maxstream.video") || link.contains("https://uprot.net") && link != uri) { + return link + } } - } + return uri } - val short = Regex("""^https?://.*?(https?://.*)""").find(uri)?.value - if (short!=null){ - uri = short + fun unshortenDavisonbarker(uri: String): String { + return URLDecoder.decode(uri.substringAfter("dest=")) } - if (r==null){ - r = app.get( - uri, - 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/")) { - uri = app.get(uri, allowRedirects = true).url - } - } - return uri -} -fun unshortenLinksafe(uri:String) : String { - return base64Decode(uri.split("?url=").last()) -} -suspend fun unshortenNuovoIndirizzo(uri:String) : String { - val soup = app.get(uri, allowRedirects = true) - val header = soup.headers["refresh"] - val link : String= if (header != null) { - soup.headers["refresh"]!!.substringAfter("=") } - else{ - "non trovato" - } - return link -} -suspend fun unshortenNuovoLink(uri:String) : String { - return app.get(uri, allowRedirects = true).document.selectFirst("a")!!.attr("href") - -} -suspend fun unshortenUprot(uri: String): String { - val page = app.get(uri).text - Regex("""]+href="([^"]+)""").findAll(page).map { it.value.replace(""" - if (link.contains("https://maxstream.video") || link.contains("https://uprot.net") && link != uri){ - return link - } - } - return uri -} -fun unshortenDavisonbarker(uri: String): String { - return URLDecoder.decode(uri.substringAfter("dest=")) } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7c841e0f..c55d6836 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -92,6 +92,7 @@ %d min CloudStream + Play with CloudStream Home Search Downloads