diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 00242ccc..5ab95416 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -15,10 +15,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v2 with: - java-version: '8' + java-version: '11' distribution: 'adopt' - name: Grant execute permission for gradlew run: chmod +x gradlew diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 281bb10b..1a4db134 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v2 with: - java-version: '8' + java-version: '11' distribution: 'adopt' - name: Grant execute permission for gradlew run: chmod +x gradlew diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8a..5421743a 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -3,4 +3,4 @@ - \ No newline at end of file + diff --git a/.idea/gradle.xml b/.idea/gradle.xml index ada5a4e9..c525ac92 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,6 +4,7 @@ - \ No newline at end of file + diff --git a/.idea/misc.xml b/.idea/misc.xml index 4bc4fc6e..25d34a47 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 908ff43b..e41c48e0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,7 +35,7 @@ android { minSdkVersion 21 targetSdkVersion 31 versionCode 27 - versionName "1.9.11" + versionName "1.9.12" resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}" @@ -105,6 +105,8 @@ dependencies { implementation "androidx.preference:preference-ktx:1.1.1" implementation 'com.github.bumptech.glide:glide:4.12.0' + kapt 'com.github.bumptech.glide:compiler:4.12.0' + implementation 'jp.wasabeef:glide-transformations:4.0.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/AsianLoad.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/AsianLoad.kt index 7ad7105c..2c087b10 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/AsianLoad.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/AsianLoad.kt @@ -3,10 +3,7 @@ package com.lagradost.cloudstream3.extractors import com.lagradost.cloudstream3.network.get import com.lagradost.cloudstream3.network.text import com.lagradost.cloudstream3.network.url -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.getQualityFromName +import com.lagradost.cloudstream3.utils.* import java.net.URI class AsianLoad : ExtractorApi() { @@ -17,8 +14,6 @@ class AsianLoad : ExtractorApi() { override val requiresReferer: Boolean get() = true - private val urlRegex = Regex("""(.*?)([^/]+$)""") - private val m3u8UrlRegex = Regex("""RESOLUTION=\d*x(\d*).*\n(.*\.m3u8)""") private val sourceRegex = Regex("""sources:[\W\w]*?file:\s*?["'](.*?)["']""") override fun getUrl(url: String, referer: String?): List { val extractedLinksList: MutableList = mutableListOf() @@ -27,21 +22,25 @@ class AsianLoad : ExtractorApi() { val extractedUrl = sourceMatch.groupValues[1] // Trusting this isn't mp4, may fuck up stuff if (URI(extractedUrl).path.endsWith(".m3u8")) { - with(get(extractedUrl, referer = this.url)) { - m3u8UrlRegex.findAll(this.text).forEach { match -> + M3u8Helper().m3u8Generation( + M3u8Helper.M3u8Stream( + extractedUrl, + headers = mapOf("referer" to this.url) + ), true + ) + .forEach { stream -> + val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" extractedLinksList.add( ExtractorLink( name, - "$name ${match.groupValues[1]}p", - urlRegex.find(this.url)!!.groupValues[1] + match.groupValues[2], + "$name $qualityString", + stream.streamUrl, url, - getQualityFromName(match.groupValues[1]), - isM3u8 = true + getQualityFromName(stream.quality.toString()), + true ) ) } - - } } else if (extractedUrl.endsWith(".mp4")) { extractedLinksList.add( ExtractorLink( diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt index 587bb376..c239e4f5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt @@ -45,11 +45,12 @@ class WcoStream : ExtractorApi() { if (mapped.success) { mapped.media.sources.forEach { if (it.file.contains("m3u8")) { - hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(it.file, null)).forEach { stream -> + hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(it.file, null), true).forEach { stream -> + val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" sources.add( ExtractorLink( name, - name + if (stream.quality != null) " - ${stream.quality}" else "", + "$name $qualityString", stream.streamUrl, "", getQualityFromName(stream.quality.toString()), 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 74dc91b4..f36607e9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/SflixProvider.kt @@ -8,6 +8,7 @@ import com.lagradost.cloudstream3.network.WebViewResolver import com.lagradost.cloudstream3.network.get import com.lagradost.cloudstream3.network.text import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.getQualityFromName import org.jsoup.Jsoup import org.jsoup.nodes.Element @@ -29,7 +30,7 @@ class SflixProvider : MainAPI() { get() = true override val hasDownloadSupport: Boolean - get() = false + get() = true override val supportedTypes: Set get() = setOf( @@ -257,21 +258,39 @@ class SflixProvider : MainAPI() { ) data class SourceObject( + @JsonProperty("sources") val sources: List?, @JsonProperty("sources_1") val sources1: List?, @JsonProperty("sources_2") val sources2: List?, + @JsonProperty("sourcesBackup") val sourcesBackup: List?, @JsonProperty("tracks") val tracks: List? ) - private fun Sources1.toExtractorLink(): ExtractorLink? { + private fun Sources1.toExtractorLink(name: String): List? { return this.file?.let { - ExtractorLink( - this@SflixProvider.name, - this.label?.let { "${this@SflixProvider.name} - $it" } ?: this@SflixProvider.name, - it, - this@SflixProvider.mainUrl, - getQualityFromName(this.label ?: ""), - URI(this.file).path.endsWith(".m3u8") || this.label.equals("hls", ignoreCase = true), - ) + val isM3u8 = URI(this.file).path.endsWith(".m3u8") || this.type.equals("hls", ignoreCase = true) + if (isM3u8) { + M3u8Helper().m3u8Generation(M3u8Helper.M3u8Stream(this.file, null), true).map { stream -> + val qualityString = if ((stream.quality ?: 0) == 0) label ?: "" else "${stream.quality}p" + ExtractorLink( + this@SflixProvider.name, + "${this@SflixProvider.name} $qualityString $name", + stream.streamUrl, + mainUrl, + getQualityFromName(stream.quality.toString()), + true + ) + } + } else { + listOf(ExtractorLink( + this@SflixProvider.name, + this.label?.let { "${this@SflixProvider.name} - $it" } ?: this@SflixProvider.name, + it, + this@SflixProvider.mainUrl, + getQualityFromName(this.type ?: ""), + false, + )) + } + } } @@ -319,18 +338,15 @@ class SflixProvider : MainAPI() { val mapped = mapper.readValue(sources) - mapped.sources1?.forEach { - it?.toExtractorLink()?.let { extractorLink -> - callback.invoke( - extractorLink - ) - } - } - mapped.sources2?.forEach { - it?.toExtractorLink()?.let { extractorLink -> - callback.invoke( - extractorLink - ) + val list = listOf( + mapped.sources1 to "source 1", + mapped.sources2 to "source 2", + mapped.sources to "source 0", + mapped.sourcesBackup to "source 3" + ) + list.forEach { subList -> + subList.first?.forEach { + it?.toExtractorLink(subList.second)?.forEach(callback) } } mapped.tracks?.forEach { diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/WebViewResolver.kt b/app/src/main/java/com/lagradost/cloudstream3/network/WebViewResolver.kt index ff2e061c..952d1d59 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/WebViewResolver.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/WebViewResolver.kt @@ -1,10 +1,8 @@ package com.lagradost.cloudstream3.network import android.annotation.SuppressLint -import android.webkit.WebResourceRequest -import android.webkit.WebResourceResponse -import android.webkit.WebView -import android.webkit.WebViewClient +import android.net.http.SslError +import android.webkit.* import com.lagradost.cloudstream3.AcraApplication import com.lagradost.cloudstream3.utils.Coroutines.main import kotlinx.coroutines.delay @@ -26,6 +24,7 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor { @SuppressLint("SetJavaScriptEnabled") suspend fun resolveUsingWebView(request: Request): Request? { val url = request.url.toString() + println("Initial web-view request: $url") var webView: WebView? = null fun destroyWebView() { @@ -43,6 +42,7 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor { webView = WebView( AcraApplication.context ?: throw RuntimeException("No base context in WebViewResolver") ).apply { + settings.cacheMode settings.javaScriptEnabled = true } @@ -62,10 +62,15 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor { 10, TimeUnit.MINUTES ) + println("Web-view request finished: $webViewUrl") destroyWebView() } return super.shouldInterceptRequest(view, request) } + + override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) { + handler?.proceed() // Ignore ssl issues + } } webView?.loadUrl(url) @@ -83,6 +88,7 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor { loop += 1 } + println("Web-view timeout after ${totalTime / 1000}s") destroyWebView() return null } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt new file mode 100644 index 00000000..b9731742 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt @@ -0,0 +1,21 @@ +package com.lagradost.cloudstream3.utils + +import android.content.Context +import com.bumptech.glide.GlideBuilder +import com.bumptech.glide.annotation.GlideModule +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.module.AppGlideModule +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.signature.ObjectKey + +@GlideModule +class GlideModule : AppGlideModule() { + override fun applyOptions(context: Context, builder: GlideBuilder) { + super.applyOptions(context, builder) + builder.apply { + RequestOptions() + .diskCacheStrategy(DiskCacheStrategy.ALL) + .signature(ObjectKey(System.currentTimeMillis().toShort())) + } + } +} \ No newline at end of file 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 108b1a2a..2cf81f6b 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,8 @@ import kotlin.math.pow class M3u8Helper { private val ENCRYPTION_DETECTION_REGEX = Regex("#EXT-X-KEY:METHOD=([^,]+),") private val ENCRYPTION_URL_IV_REGEX = Regex("#EXT-X-KEY:METHOD=([^,]+),URI=\"([^\"]+)\"(?:,IV=(.*))?") - private val QUALITY_REGEX = Regex("""#EXT-X-STREAM-INF:(?:(?:.*?(?:RESOLUTION=\d+x(\d+)).*?\s+(.*))|(?:.*?\s+(.*)))""") + private val QUALITY_REGEX = + Regex("""#EXT-X-STREAM-INF:(?:(?:.*?(?:RESOLUTION=\d+x(\d+)).*?\s+(.*))|(?:.*?\s+(.*)))""") private val TS_EXTENSION_REGEX = Regex("""(.*\.ts.*)""") fun absoluteExtensionDetermination(url: String): String? { @@ -78,7 +79,7 @@ class M3u8Helper { return !url.contains("https://") && !url.contains("http://") } - fun m3u8Generation(m3u8: M3u8Stream): List { + fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean): List { val generate = sequence { val m3u8Parent = getParentLink(m3u8.streamUrl) val response = get(m3u8.streamUrl, headers = m3u8.headers).text @@ -99,7 +100,7 @@ class M3u8Helper { m3u8Link, quality.toIntOrNull(), m3u8.headers - ) + ), false ) ) } @@ -111,6 +112,15 @@ class M3u8Helper { ) ) } + if (returnThis) { + yield( + M3u8Stream( + m3u8.streamUrl, + 0, + m3u8.headers + ) + ) + } } return generate.toList() } @@ -131,7 +141,7 @@ class M3u8Helper { } val headers = selected.headers - val streams = qualities.map { m3u8Generation(it) }.flatten() + val streams = qualities.map { m3u8Generation(it, false) }.flatten() //val sslVerification = if (headers.containsKey("ssl_verification")) headers["ssl_verification"].toBoolean() else true val secondSelection = selectBest(streams.ifEmpty { listOf(selected) }) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index b314f29b..9cfe71d5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -96,7 +96,7 @@ object UIHelper { fun ImageView?.setImage(url : String?) { if(this == null || url.isNullOrBlank()) return try { - Glide.with(this.context) + GlideApp.with(this.context) .load(GlideUrl(url)) .into(this) } catch (e : Exception) { 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 453da090..ed24ee5e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt @@ -202,7 +202,7 @@ object VideoDownloadManager { return cachedBitmaps[url] } - val bitmap = Glide.with(this) + val bitmap = GlideApp.with(this) .asBitmap() .load(url).into(720, 720) .get()