From 18fa11fd88f0aedcf9f2ab257c747e8e78bf1059 Mon Sep 17 00:00:00 2001 From: tapframe Date: Tue, 23 Dec 2025 17:42:45 +0530 Subject: [PATCH] added header support --- .../main/java/com/nuvio/app/mpv/MPVView.kt | 34 ++++++++++++++++--- .../com/nuvio/app/mpv/MpvPlayerViewManager.kt | 17 ++++++++++ mpv-android | 1 + src/components/player/AndroidVideoPlayer.tsx | 1 + src/components/player/android/MpvPlayer.tsx | 2 ++ .../android/components/VideoSurface.tsx | 3 ++ 6 files changed, 53 insertions(+), 5 deletions(-) create mode 160000 mpv-android diff --git a/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt b/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt index c983ccc..6f5727e 100644 --- a/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt +++ b/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt @@ -22,6 +22,7 @@ class MPVView @JvmOverloads constructor( private var pendingDataSource: String? = null private var isPaused: Boolean = true private var surface: Surface? = null + private var httpHeaders: Map? = null // Event listener for React Native var onLoadCallback: ((duration: Double, width: Int, height: Int) -> Unit)? = null @@ -51,6 +52,7 @@ class MPVView @JvmOverloads constructor( // If a data source was set before surface was ready, load it now pendingDataSource?.let { url -> + applyHttpHeaders() loadFile(url) pendingDataSource = null } @@ -93,7 +95,7 @@ class MPVView @JvmOverloads constructor( // Hardware decoding - use mediacodec-copy to allow subtitle overlay // 'mediacodec-copy' copies frames to CPU memory which enables subtitle blending - MPVLib.setOptionString("hwdec", "mediacodec-copy") + MPVLib.setOptionString("hwdec", "auto") MPVLib.setOptionString("hwdec-codecs", "all") // Audio output @@ -105,6 +107,9 @@ class MPVView @JvmOverloads constructor( MPVLib.setOptionString("cache", "yes") MPVLib.setOptionString("cache-secs", "30") + // Network options + MPVLib.setOptionString("network-timeout", "60") // 60 second timeout + // Subtitle configuration - CRITICAL for Android MPVLib.setOptionString("sub-auto", "fuzzy") // Auto-load subtitles MPVLib.setOptionString("sub-visibility", "yes") // Make subtitles visible by default @@ -155,7 +160,7 @@ class MPVView @JvmOverloads constructor( val MPV_FORMAT_DOUBLE = 5 MPVLib.observeProperty("time-pos", MPV_FORMAT_DOUBLE) - MPVLib.observeProperty("duration", MPV_FORMAT_DOUBLE) + MPVLib.observeProperty("duration/full", MPV_FORMAT_DOUBLE) // Use /full for complete HLS duration MPVLib.observeProperty("pause", MPV_FORMAT_FLAG) MPVLib.observeProperty("paused-for-cache", MPV_FORMAT_FLAG) MPVLib.observeProperty("eof-reached", MPV_FORMAT_FLAG) @@ -179,12 +184,31 @@ class MPVView @JvmOverloads constructor( fun setDataSource(url: String) { if (isMpvInitialized) { + // Apply headers before loading the file + applyHttpHeaders() loadFile(url) } else { pendingDataSource = url } } + fun setHeaders(headers: Map?) { + httpHeaders = headers + Log.d(TAG, "Headers set: $headers") + } + + private fun applyHttpHeaders() { + httpHeaders?.let { headers -> + if (headers.isNotEmpty()) { + // Format headers for MPV: comma-separated "Key: Value" pairs + val headerList = headers.map { (key, value) -> "$key: $value" } + val headerString = headerList.joinToString(",") + Log.d(TAG, "Applying HTTP headers: $headerString") + MPVLib.setOptionString("http-header-fields", headerString) + } + } + } + fun setPaused(paused: Boolean) { isPaused = paused if (isMpvInitialized) { @@ -341,10 +365,10 @@ class MPVView @JvmOverloads constructor( Log.d(TAG, "Property $property = $value (Double)") when (property) { "time-pos" -> { - val duration = MPVLib.getPropertyDouble("duration") ?: 0.0 + val duration = MPVLib.getPropertyDouble("duration/full") ?: MPVLib.getPropertyDouble("duration") ?: 0.0 onProgressCallback?.invoke(value, duration) } - "duration" -> { + "duration/full", "duration" -> { val width = MPVLib.getPropertyInt("width") ?: 0 val height = MPVLib.getPropertyInt("height") ?: 0 onLoadCallback?.invoke(value, width, height) @@ -384,7 +408,7 @@ class MPVView @JvmOverloads constructor( Log.d(TAG, "MPV_EVENT_END_FILE") // Heuristic: If duration is effectively 0 at end of file, it's a load error - val duration = MPVLib.getPropertyDouble("duration") ?: 0.0 + val duration = MPVLib.getPropertyDouble("duration/full") ?: MPVLib.getPropertyDouble("duration") ?: 0.0 val timePos = MPVLib.getPropertyDouble("time-pos") ?: 0.0 val eofReached = MPVLib.getPropertyBoolean("eof-reached") ?: false diff --git a/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt b/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt index db5426e..27d4852 100644 --- a/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt +++ b/android/app/src/main/java/com/nuvio/app/mpv/MpvPlayerViewManager.kt @@ -163,4 +163,21 @@ class MpvPlayerViewManager( fun setResizeMode(view: MPVView, resizeMode: String?) { view.setResizeMode(resizeMode ?: "contain") } + + @ReactProp(name = "headers") + fun setHeaders(view: MPVView, headers: com.facebook.react.bridge.ReadableMap?) { + if (headers != null) { + val headerMap = mutableMapOf() + val iterator = headers.keySetIterator() + while (iterator.hasNextKey()) { + val key = iterator.nextKey() + headers.getString(key)?.let { value -> + headerMap[key] = value + } + } + view.setHeaders(headerMap) + } else { + view.setHeaders(null) + } + } } diff --git a/mpv-android b/mpv-android new file mode 160000 index 0000000..118cd1e --- /dev/null +++ b/mpv-android @@ -0,0 +1 @@ +Subproject commit 118cd1ed3d498265e44230e5dbb015bdd59f9dad diff --git a/src/components/player/AndroidVideoPlayer.tsx b/src/components/player/AndroidVideoPlayer.tsx index 9eea59a..568a9c4 100644 --- a/src/components/player/AndroidVideoPlayer.tsx +++ b/src/components/player/AndroidVideoPlayer.tsx @@ -487,6 +487,7 @@ const AndroidVideoPlayer: React.FC = () => { ((props, ref) => { ref={nativeRef} style={[styles.container, props.style]} source={props.source} + headers={props.headers} paused={props.paused ?? true} volume={props.volume ?? 1.0} rate={props.rate ?? 1.0} diff --git a/src/components/player/android/components/VideoSurface.tsx b/src/components/player/android/components/VideoSurface.tsx index 2e05f7f..c6d0d86 100644 --- a/src/components/player/android/components/VideoSurface.tsx +++ b/src/components/player/android/components/VideoSurface.tsx @@ -7,6 +7,7 @@ import { ResizeModeType } from '../../utils/playerTypes'; interface VideoSurfaceProps { processedStreamUrl: string; + headers?: { [key: string]: string }; volume: number; playbackSpeed: number; resizeMode: ResizeModeType; @@ -35,6 +36,7 @@ interface VideoSurfaceProps { export const VideoSurface: React.FC = ({ processedStreamUrl, + headers, volume, playbackSpeed, resizeMode, @@ -100,6 +102,7 @@ export const VideoSurface: React.FC = ({