mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-16 23:12:12 +00:00
Merge 16a271ebd9 into 70d3eee9d2
This commit is contained in:
commit
692d2ac6d1
10 changed files with 27 additions and 5 deletions
|
|
@ -66,6 +66,7 @@ private const val TAG = "NuvioPlayer"
|
|||
actual fun PlatformPlayerSurface(
|
||||
sourceUrl: String,
|
||||
sourceAudioUrl: String?,
|
||||
streamType: String?,
|
||||
sourceHeaders: Map<String, String>,
|
||||
sourceResponseHeaders: Map<String, String>,
|
||||
useYoutubeChunkedPlayback: Boolean,
|
||||
|
|
@ -163,11 +164,11 @@ actual fun PlatformPlayerSurface(
|
|||
player.apply {
|
||||
if (!sourceAudioUrl.isNullOrBlank()) {
|
||||
val msf = DefaultMediaSourceFactory(dataSourceFactory, extractorsFactory)
|
||||
val videoSource = msf.createMediaSource(MediaItem.fromUri(sourceUrl))
|
||||
val audioSource = msf.createMediaSource(MediaItem.fromUri(sourceAudioUrl))
|
||||
val videoSource = msf.createMediaSource(MediaItem.Builder().setUri(sourceUrl).apply { if ("hls".equals(streamType, ignoreCase = true) || sourceUrl.isHlsUrl()) setMimeType(MimeTypes.APPLICATION_M3U8) }.build())
|
||||
val audioSource = msf.createMediaSource(MediaItem.Builder().setUri(sourceAudioUrl).apply { if ("hls".equals(streamType, ignoreCase = true) || sourceAudioUrl.isHlsUrl()) setMimeType(MimeTypes.APPLICATION_M3U8) }.build())
|
||||
setMediaSource(MergingMediaSource(videoSource, audioSource))
|
||||
} else {
|
||||
setMediaItem(MediaItem.fromUri(sourceUrl))
|
||||
setMediaItem(MediaItem.Builder().setUri(sourceUrl).apply { if ("hls".equals(streamType, ignoreCase = true) || sourceUrl.isHlsUrl()) setMimeType(MimeTypes.APPLICATION_M3U8) }.build())
|
||||
}
|
||||
prepare()
|
||||
this.playWhenReady = playWhenReady
|
||||
|
|
@ -736,3 +737,10 @@ private fun guessSubtitleMime(url: String): String {
|
|||
else -> MimeTypes.TEXT_VTT
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.isHlsUrl(): Boolean =
|
||||
endsWith(".m3u8", ignoreCase = true) ||
|
||||
contains(".m3u8?", ignoreCase = true) ||
|
||||
contains("/playlist/", ignoreCase = true) ||
|
||||
contains("/master/", ignoreCase = true) ||
|
||||
contains("/chunklist/", ignoreCase = true)
|
||||
|
|
|
|||
|
|
@ -1437,7 +1437,7 @@ private fun MainAppContent(
|
|||
bingeGroup = cached.bingeGroup,
|
||||
pauseDescription = pauseDescription,
|
||||
providerName = cached.addonName,
|
||||
providerAddonId = cached.addonId,
|
||||
providerAddonId = cached.addonId,
|
||||
contentType = launch.type,
|
||||
videoId = effectiveVideoId,
|
||||
parentMetaId = launch.parentMetaId ?: effectiveVideoId,
|
||||
|
|
@ -1544,6 +1544,7 @@ private fun MainAppContent(
|
|||
pauseDescription = pauseDescription,
|
||||
providerName = stream.addonName,
|
||||
providerAddonId = stream.addonId,
|
||||
streamType = stream.streamType,
|
||||
contentType = launch.type,
|
||||
videoId = effectiveVideoId,
|
||||
parentMetaId = launch.parentMetaId ?: effectiveVideoId,
|
||||
|
|
@ -1654,6 +1655,7 @@ private fun MainAppContent(
|
|||
pauseDescription = pauseDescription,
|
||||
providerName = stream.addonName,
|
||||
providerAddonId = stream.addonId,
|
||||
streamType = stream.streamType,
|
||||
contentType = launch.type,
|
||||
videoId = effectiveVideoId,
|
||||
parentMetaId = launch.parentMetaId ?: effectiveVideoId,
|
||||
|
|
@ -1769,6 +1771,7 @@ private fun MainAppContent(
|
|||
title = launch.title,
|
||||
sourceUrl = launch.sourceUrl,
|
||||
sourceAudioUrl = launch.sourceAudioUrl,
|
||||
streamType = launch.streamType,
|
||||
sourceHeaders = launch.sourceHeaders,
|
||||
sourceResponseHeaders = launch.sourceResponseHeaders,
|
||||
logo = launch.logo,
|
||||
|
|
@ -1874,7 +1877,7 @@ private fun MainAppContent(
|
|||
streamTitle = item.streamTitle,
|
||||
streamSubtitle = item.streamSubtitle,
|
||||
providerName = item.providerName,
|
||||
providerAddonId = item.providerAddonId,
|
||||
providerAddonId = item.providerAddonId,
|
||||
contentType = item.contentType,
|
||||
videoId = item.videoId,
|
||||
parentMetaId = item.parentMetaId,
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@ internal object MetaDetailsParser {
|
|||
externalUrl = externalUrl,
|
||||
addonName = addonName,
|
||||
addonId = "embedded",
|
||||
streamType = obj.string("type"),
|
||||
behaviorHints = StreamBehaviorHints(
|
||||
bingeGroup = hintsObj?.string("bingeGroup"),
|
||||
notWebReady = (hintsObj?.boolean("notWebReady") ?: false) || proxyHeaders != null,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ internal fun sanitizePlaybackResponseHeaders(headers: Map<String, String>?): Map
|
|||
expect fun PlatformPlayerSurface(
|
||||
sourceUrl: String,
|
||||
sourceAudioUrl: String? = null,
|
||||
streamType: String? = null,
|
||||
sourceHeaders: Map<String, String> = emptyMap(),
|
||||
sourceResponseHeaders: Map<String, String> = emptyMap(),
|
||||
useYoutubeChunkedPlayback: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ data class PlayerLaunch(
|
|||
val title: String,
|
||||
val sourceUrl: String,
|
||||
val sourceAudioUrl: String? = null,
|
||||
val streamType: String? = null,
|
||||
val sourceHeaders: Map<String, String> = emptyMap(),
|
||||
val sourceResponseHeaders: Map<String, String> = emptyMap(),
|
||||
val logo: String? = null,
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ fun PlayerScreen(
|
|||
title: String,
|
||||
sourceUrl: String,
|
||||
sourceAudioUrl: String? = null,
|
||||
streamType: String? = null,
|
||||
sourceHeaders: Map<String, String> = emptyMap(),
|
||||
sourceResponseHeaders: Map<String, String> = emptyMap(),
|
||||
providerName: String,
|
||||
|
|
@ -187,6 +188,7 @@ fun PlayerScreen(
|
|||
// Active playback state (mutable to support source/episode switching)
|
||||
var activeSourceUrl by rememberSaveable { mutableStateOf(sourceUrl) }
|
||||
var activeSourceAudioUrl by rememberSaveable { mutableStateOf(sourceAudioUrl) }
|
||||
var activeStreamType by rememberSaveable { mutableStateOf(streamType) }
|
||||
var activeSourceHeaders by remember(sourceUrl, sourceHeaders) {
|
||||
mutableStateOf(sanitizePlaybackHeaders(sourceHeaders))
|
||||
}
|
||||
|
|
@ -1633,6 +1635,7 @@ fun PlayerScreen(
|
|||
PlatformPlayerSurface(
|
||||
sourceUrl = activeSourceUrl,
|
||||
sourceAudioUrl = activeSourceAudioUrl,
|
||||
streamType = activeStreamType,
|
||||
sourceHeaders = activeSourceHeaders,
|
||||
sourceResponseHeaders = activeSourceResponseHeaders,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
|
|
|||
|
|
@ -380,6 +380,7 @@ private fun PluginRuntimeResult.toStreamItem(scraper: PluginScraper): StreamItem
|
|||
infoHash = infoHash,
|
||||
addonName = scraper.name,
|
||||
addonId = "plugin:${scraper.id}",
|
||||
streamType = type,
|
||||
behaviorHints = if (requestHeaders.isEmpty()) {
|
||||
com.nuvio.app.features.streams.StreamBehaviorHints()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ data class StreamItem(
|
|||
val sourceName: String? = null,
|
||||
val addonName: String,
|
||||
val addonId: String,
|
||||
val streamType: String? = null,
|
||||
val behaviorHints: StreamBehaviorHints = StreamBehaviorHints(),
|
||||
val clientResolve: StreamClientResolve? = null,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ object StreamParser {
|
|||
val infoHash = obj.string("infoHash")
|
||||
val externalUrl = obj.string("externalUrl")
|
||||
val clientResolve = obj.objectValue("clientResolve")?.toClientResolve()
|
||||
val streamType = obj.string("type")
|
||||
|
||||
// Must have at least one playable source
|
||||
if (url == null && infoHash == null && externalUrl == null && clientResolve == null) return@mapNotNull null
|
||||
|
|
@ -44,6 +45,7 @@ object StreamParser {
|
|||
fileIdx = obj.int("fileIdx"),
|
||||
externalUrl = externalUrl,
|
||||
sources = obj.stringList("sources"),
|
||||
streamType = streamType,
|
||||
addonName = addonName,
|
||||
addonId = addonId,
|
||||
clientResolve = clientResolve,
|
||||
|
|
|
|||
|
|
@ -649,6 +649,7 @@ private fun PluginRuntimeResult.toStreamItem(
|
|||
sourceName = scraper.name,
|
||||
addonName = addonName,
|
||||
addonId = addonId,
|
||||
streamType = type,
|
||||
behaviorHints = if (requestHeaders.isEmpty()) {
|
||||
StreamBehaviorHints()
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue