ref: playbackDataSourceFactory to be in parity with Nuvio TV

This commit is contained in:
tapframe 2026-04-12 19:44:31 +05:30
parent 3625a72ee7
commit 73c29ccab8
3 changed files with 108 additions and 26 deletions

View file

@ -3,18 +3,9 @@ package com.nuvio.app.features.player
import android.content.Context
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.DefaultHttpDataSource
import com.nuvio.app.features.trailer.YoutubeChunkedDataSourceFactory
internal object PlatformPlaybackDataSourceFactory {
private val DEFAULT_STREAM_HEADERS = mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/120.0.0.0 Safari/537.36",
"Referer" to "https://www.strem.io/"
)
fun create(
context: Context,
defaultRequestHeaders: Map<String, String>,
@ -24,9 +15,7 @@ internal object PlatformPlaybackDataSourceFactory {
val networkFactory: DataSource.Factory = if (useYoutubeChunkedPlayback) {
YoutubeChunkedDataSourceFactory(defaultRequestHeaders = defaultRequestHeaders)
} else {
DefaultHttpDataSource.Factory()
.setAllowCrossProtocolRedirects(true)
.setDefaultRequestProperties(DEFAULT_STREAM_HEADERS + defaultRequestHeaders)
PlayerPlaybackNetworking.createHttpDataSourceFactory(defaultRequestHeaders)
}
val baseFactory: DataSource.Factory = DefaultDataSource.Factory(context, networkFactory)
return if (defaultResponseHeaders.isEmpty()) {
@ -38,4 +27,4 @@ internal object PlatformPlaybackDataSourceFactory {
)
}
}
}
}

View file

@ -0,0 +1,104 @@
package com.nuvio.app.features.player
import android.content.Context
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.okhttp.OkHttpDataSource
import com.nuvio.app.core.network.IPv4FirstDns
import okhttp3.OkHttpClient
import java.net.HttpURLConnection
import java.net.URL
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
internal object PlayerPlaybackNetworking {
private val DEFAULT_STREAM_HEADERS = mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/120.0.0.0 Safari/537.36",
)
internal const val DEFAULT_USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
private val trustAllManager = object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) = Unit
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) = Unit
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
}
private val playbackHostnameVerifier = HostnameVerifier { _, _ -> true }
private val sslContext: SSLContext by lazy {
SSLContext.getInstance("TLS").apply {
init(null, arrayOf<TrustManager>(trustAllManager), SecureRandom())
}
}
private val playbackHttpClient: OkHttpClient by lazy {
OkHttpClient.Builder()
.dns(IPv4FirstDns())
.sslSocketFactory(sslContext.socketFactory, trustAllManager)
.hostnameVerifier(playbackHostnameVerifier)
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.build()
}
fun createHttpDataSourceFactory(defaultHeaders: Map<String, String> = emptyMap()): DataSource.Factory {
val mergedHeaders = DEFAULT_STREAM_HEADERS + defaultHeaders
return OkHttpDataSource.Factory(playbackHttpClient).apply {
setDefaultRequestProperties(mergedHeaders)
setUserAgent(DEFAULT_USER_AGENT)
}
}
fun createDataSourceFactory(
context: Context,
defaultHeaders: Map<String, String> = emptyMap(),
): DataSource.Factory {
return DefaultDataSource.Factory(context, createHttpDataSourceFactory(defaultHeaders))
}
fun openConnection(
url: String,
headers: Map<String, String>,
method: String,
connectTimeoutMs: Int,
readTimeoutMs: Int,
range: String? = null,
): HttpURLConnection {
val mergedHeaders = DEFAULT_STREAM_HEADERS + headers
return (URL(url).openConnection() as HttpURLConnection).apply {
if (this is HttpsURLConnection) {
sslSocketFactory = sslContext.socketFactory
hostnameVerifier = playbackHostnameVerifier
}
instanceFollowRedirects = true
connectTimeout = connectTimeoutMs
readTimeout = readTimeoutMs
requestMethod = method
setRequestProperty("User-Agent", mergedHeaders["User-Agent"] ?: DEFAULT_USER_AGENT)
mergedHeaders.forEach { (key, value) ->
if (key.equals("Range", ignoreCase = true)) return@forEach
if (key.equals("User-Agent", ignoreCase = true)) return@forEach
setRequestProperty(key, value)
}
range?.let { setRequestProperty("Range", it) }
}
}
}

View file

@ -3,26 +3,15 @@ package com.nuvio.app.features.player
import android.content.Context
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultDataSource
import androidx.media3.datasource.DefaultHttpDataSource
internal object PlatformPlaybackDataSourceFactory {
private val DEFAULT_STREAM_HEADERS = mapOf(
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/120.0.0.0 Safari/537.36",
"Referer" to "https://www.strem.io/"
)
fun create(
context: Context,
defaultRequestHeaders: Map<String, String>,
defaultResponseHeaders: Map<String, String>,
useYoutubeChunkedPlayback: Boolean,
): DataSource.Factory {
val httpFactory = DefaultHttpDataSource.Factory()
.setAllowCrossProtocolRedirects(true)
.setDefaultRequestProperties(DEFAULT_STREAM_HEADERS + defaultRequestHeaders)
val httpFactory = PlayerPlaybackNetworking.createHttpDataSourceFactory(defaultRequestHeaders)
val baseFactory: DataSource.Factory = DefaultDataSource.Factory(context, httpFactory)
return if (defaultResponseHeaders.isEmpty()) {
baseFactory
@ -33,4 +22,4 @@ internal object PlatformPlaybackDataSourceFactory {
)
}
}
}
}