Added sflix downloads, fixed some sflix sources and added image caching (#128)

Waited long enough and it seems to work fine, I will just revert if this fucks up :)
This commit is contained in:
LagradOst 2021-10-07 19:40:31 +02:00 committed by GitHub
parent 96b5c9658d
commit 596f659a29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 112 additions and 56 deletions

View file

@ -15,10 +15,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up JDK 8 - name: Set up JDK 11
uses: actions/setup-java@v2 uses: actions/setup-java@v2
with: with:
java-version: '8' java-version: '11'
distribution: 'adopt' distribution: 'adopt'
- name: Grant execute permission for gradlew - name: Grant execute permission for gradlew
run: chmod +x gradlew run: chmod +x gradlew

View file

@ -7,10 +7,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up JDK 8 - name: Set up JDK 11
uses: actions/setup-java@v2 uses: actions/setup-java@v2
with: with:
java-version: '8' java-version: '11'
distribution: 'adopt' distribution: 'adopt'
- name: Grant execute permission for gradlew - name: Grant execute permission for gradlew
run: chmod +x gradlew run: chmod +x gradlew

View file

@ -3,4 +3,4 @@
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" /> <bytecodeTargetLevel target="11" />
</component> </component>
</project> </project>

View file

@ -4,6 +4,7 @@
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
<option name="delegatedBuild" value="true" />
<option name="testRunner" value="GRADLE" /> <option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
@ -18,4 +19,4 @@
</GradleProjectSettings> </GradleProjectSettings>
</option> </option>
</component> </component>
</project> </project>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK" />
</project> </project>

View file

@ -35,7 +35,7 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 31
versionCode 27 versionCode 27
versionName "1.9.11" versionName "1.9.12"
resValue "string", "app_version", resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}" "${defaultConfig.versionName}${versionNameSuffix ?: ""}"
@ -105,6 +105,8 @@ dependencies {
implementation "androidx.preference:preference-ktx:1.1.1" implementation "androidx.preference:preference-ktx:1.1.1"
implementation 'com.github.bumptech.glide:glide:4.12.0' 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 'jp.wasabeef:glide-transformations:4.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'

View file

@ -3,10 +3,7 @@ package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.network.get import com.lagradost.cloudstream3.network.get
import com.lagradost.cloudstream3.network.text import com.lagradost.cloudstream3.network.text
import com.lagradost.cloudstream3.network.url import com.lagradost.cloudstream3.network.url
import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
import java.net.URI import java.net.URI
class AsianLoad : ExtractorApi() { class AsianLoad : ExtractorApi() {
@ -17,8 +14,6 @@ class AsianLoad : ExtractorApi() {
override val requiresReferer: Boolean override val requiresReferer: Boolean
get() = true 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*?["'](.*?)["']""") private val sourceRegex = Regex("""sources:[\W\w]*?file:\s*?["'](.*?)["']""")
override fun getUrl(url: String, referer: String?): List<ExtractorLink> { override fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf() val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
@ -27,21 +22,25 @@ class AsianLoad : ExtractorApi() {
val extractedUrl = sourceMatch.groupValues[1] val extractedUrl = sourceMatch.groupValues[1]
// Trusting this isn't mp4, may fuck up stuff // Trusting this isn't mp4, may fuck up stuff
if (URI(extractedUrl).path.endsWith(".m3u8")) { if (URI(extractedUrl).path.endsWith(".m3u8")) {
with(get(extractedUrl, referer = this.url)) { M3u8Helper().m3u8Generation(
m3u8UrlRegex.findAll(this.text).forEach { match -> 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( extractedLinksList.add(
ExtractorLink( ExtractorLink(
name, name,
"$name ${match.groupValues[1]}p", "$name $qualityString",
urlRegex.find(this.url)!!.groupValues[1] + match.groupValues[2], stream.streamUrl,
url, url,
getQualityFromName(match.groupValues[1]), getQualityFromName(stream.quality.toString()),
isM3u8 = true true
) )
) )
} }
}
} else if (extractedUrl.endsWith(".mp4")) { } else if (extractedUrl.endsWith(".mp4")) {
extractedLinksList.add( extractedLinksList.add(
ExtractorLink( ExtractorLink(

View file

@ -45,11 +45,12 @@ class WcoStream : ExtractorApi() {
if (mapped.success) { if (mapped.success) {
mapped.media.sources.forEach { mapped.media.sources.forEach {
if (it.file.contains("m3u8")) { 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( sources.add(
ExtractorLink( ExtractorLink(
name, name,
name + if (stream.quality != null) " - ${stream.quality}" else "", "$name $qualityString",
stream.streamUrl, stream.streamUrl,
"", "",
getQualityFromName(stream.quality.toString()), getQualityFromName(stream.quality.toString()),

View file

@ -8,6 +8,7 @@ import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.network.get import com.lagradost.cloudstream3.network.get
import com.lagradost.cloudstream3.network.text import com.lagradost.cloudstream3.network.text
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -29,7 +30,7 @@ class SflixProvider : MainAPI() {
get() = true get() = true
override val hasDownloadSupport: Boolean override val hasDownloadSupport: Boolean
get() = false get() = true
override val supportedTypes: Set<TvType> override val supportedTypes: Set<TvType>
get() = setOf( get() = setOf(
@ -257,21 +258,39 @@ class SflixProvider : MainAPI() {
) )
data class SourceObject( data class SourceObject(
@JsonProperty("sources") val sources: List<Sources1?>?,
@JsonProperty("sources_1") val sources1: List<Sources1?>?, @JsonProperty("sources_1") val sources1: List<Sources1?>?,
@JsonProperty("sources_2") val sources2: List<Sources1?>?, @JsonProperty("sources_2") val sources2: List<Sources1?>?,
@JsonProperty("sourcesBackup") val sourcesBackup: List<Sources1?>?,
@JsonProperty("tracks") val tracks: List<Tracks?>? @JsonProperty("tracks") val tracks: List<Tracks?>?
) )
private fun Sources1.toExtractorLink(): ExtractorLink? { private fun Sources1.toExtractorLink(name: String): List<ExtractorLink>? {
return this.file?.let { return this.file?.let {
ExtractorLink( val isM3u8 = URI(this.file).path.endsWith(".m3u8") || this.type.equals("hls", ignoreCase = true)
this@SflixProvider.name, if (isM3u8) {
this.label?.let { "${this@SflixProvider.name} - $it" } ?: this@SflixProvider.name, M3u8Helper().m3u8Generation(M3u8Helper.M3u8Stream(this.file, null), true).map { stream ->
it, val qualityString = if ((stream.quality ?: 0) == 0) label ?: "" else "${stream.quality}p"
this@SflixProvider.mainUrl, ExtractorLink(
getQualityFromName(this.label ?: ""), this@SflixProvider.name,
URI(this.file).path.endsWith(".m3u8") || this.label.equals("hls", ignoreCase = true), "${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<SourceObject>(sources) val mapped = mapper.readValue<SourceObject>(sources)
mapped.sources1?.forEach { val list = listOf(
it?.toExtractorLink()?.let { extractorLink -> mapped.sources1 to "source 1",
callback.invoke( mapped.sources2 to "source 2",
extractorLink mapped.sources to "source 0",
) mapped.sourcesBackup to "source 3"
} )
} list.forEach { subList ->
mapped.sources2?.forEach { subList.first?.forEach {
it?.toExtractorLink()?.let { extractorLink -> it?.toExtractorLink(subList.second)?.forEach(callback)
callback.invoke(
extractorLink
)
} }
} }
mapped.tracks?.forEach { mapped.tracks?.forEach {

View file

@ -1,10 +1,8 @@
package com.lagradost.cloudstream3.network package com.lagradost.cloudstream3.network
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.webkit.WebResourceRequest import android.net.http.SslError
import android.webkit.WebResourceResponse import android.webkit.*
import android.webkit.WebView
import android.webkit.WebViewClient
import com.lagradost.cloudstream3.AcraApplication import com.lagradost.cloudstream3.AcraApplication
import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.Coroutines.main
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -26,6 +24,7 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor {
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
suspend fun resolveUsingWebView(request: Request): Request? { suspend fun resolveUsingWebView(request: Request): Request? {
val url = request.url.toString() val url = request.url.toString()
println("Initial web-view request: $url")
var webView: WebView? = null var webView: WebView? = null
fun destroyWebView() { fun destroyWebView() {
@ -43,6 +42,7 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor {
webView = WebView( webView = WebView(
AcraApplication.context ?: throw RuntimeException("No base context in WebViewResolver") AcraApplication.context ?: throw RuntimeException("No base context in WebViewResolver")
).apply { ).apply {
settings.cacheMode
settings.javaScriptEnabled = true settings.javaScriptEnabled = true
} }
@ -62,10 +62,15 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor {
10, 10,
TimeUnit.MINUTES TimeUnit.MINUTES
) )
println("Web-view request finished: $webViewUrl")
destroyWebView() destroyWebView()
} }
return super.shouldInterceptRequest(view, request) return super.shouldInterceptRequest(view, request)
} }
override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
handler?.proceed() // Ignore ssl issues
}
} }
webView?.loadUrl(url) webView?.loadUrl(url)
@ -83,6 +88,7 @@ class WebViewResolver(val interceptUrl: Regex) : Interceptor {
loop += 1 loop += 1
} }
println("Web-view timeout after ${totalTime / 1000}s")
destroyWebView() destroyWebView()
return null return null
} }

View file

@ -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()))
}
}
}

View file

@ -12,7 +12,8 @@ import kotlin.math.pow
class M3u8Helper { class M3u8Helper {
private val ENCRYPTION_DETECTION_REGEX = Regex("#EXT-X-KEY:METHOD=([^,]+),") 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 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.*)""") private val TS_EXTENSION_REGEX = Regex("""(.*\.ts.*)""")
fun absoluteExtensionDetermination(url: String): String? { fun absoluteExtensionDetermination(url: String): String? {
@ -78,7 +79,7 @@ class M3u8Helper {
return !url.contains("https://") && !url.contains("http://") return !url.contains("https://") && !url.contains("http://")
} }
fun m3u8Generation(m3u8: M3u8Stream): List<M3u8Stream> { fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean): List<M3u8Stream> {
val generate = sequence { val generate = sequence {
val m3u8Parent = getParentLink(m3u8.streamUrl) val m3u8Parent = getParentLink(m3u8.streamUrl)
val response = get(m3u8.streamUrl, headers = m3u8.headers).text val response = get(m3u8.streamUrl, headers = m3u8.headers).text
@ -99,7 +100,7 @@ class M3u8Helper {
m3u8Link, m3u8Link,
quality.toIntOrNull(), quality.toIntOrNull(),
m3u8.headers m3u8.headers
) ), false
) )
) )
} }
@ -111,6 +112,15 @@ class M3u8Helper {
) )
) )
} }
if (returnThis) {
yield(
M3u8Stream(
m3u8.streamUrl,
0,
m3u8.headers
)
)
}
} }
return generate.toList() return generate.toList()
} }
@ -131,7 +141,7 @@ class M3u8Helper {
} }
val headers = selected.headers 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 sslVerification = if (headers.containsKey("ssl_verification")) headers["ssl_verification"].toBoolean() else true
val secondSelection = selectBest(streams.ifEmpty { listOf(selected) }) val secondSelection = selectBest(streams.ifEmpty { listOf(selected) })

View file

@ -96,7 +96,7 @@ object UIHelper {
fun ImageView?.setImage(url : String?) { fun ImageView?.setImage(url : String?) {
if(this == null || url.isNullOrBlank()) return if(this == null || url.isNullOrBlank()) return
try { try {
Glide.with(this.context) GlideApp.with(this.context)
.load(GlideUrl(url)) .load(GlideUrl(url))
.into(this) .into(this)
} catch (e : Exception) { } catch (e : Exception) {

View file

@ -202,7 +202,7 @@ object VideoDownloadManager {
return cachedBitmaps[url] return cachedBitmaps[url]
} }
val bitmap = Glide.with(this) val bitmap = GlideApp.with(this)
.asBitmap() .asBitmap()
.load(url).into(720, 720) .load(url).into(720, 720)
.get() .get()