ksplayer sub rendering fix

This commit is contained in:
tapframe 2025-12-26 12:32:33 +05:30
parent 9f461f7091
commit 985d01d5a9
10 changed files with 102 additions and 99 deletions

View file

@ -132,7 +132,7 @@ class MPVView @JvmOverloads constructor(
MPVLib.setOptionString("sub-auto", "fuzzy") MPVLib.setOptionString("sub-auto", "fuzzy")
MPVLib.setOptionString("sub-visibility", "yes") MPVLib.setOptionString("sub-visibility", "yes")
MPVLib.setOptionString("sub-font-size", "48") MPVLib.setOptionString("sub-font-size", "48")
MPVLib.setOptionString("sub-pos", "95") MPVLib.setOptionString("sub-pos", "100")
MPVLib.setOptionString("sub-color", "#FFFFFFFF") MPVLib.setOptionString("sub-color", "#FFFFFFFF")
MPVLib.setOptionString("sub-border-size", "3") MPVLib.setOptionString("sub-border-size", "3")
MPVLib.setOptionString("sub-border-color", "#FF000000") MPVLib.setOptionString("sub-border-color", "#FF000000")
@ -146,15 +146,15 @@ class MPVView @JvmOverloads constructor(
MPVLib.setOptionString("sub-codepage", "auto") MPVLib.setOptionString("sub-codepage", "auto")
MPVLib.setOptionString("osc", "no")
MPVLib.setOptionString("osd-level", "1")
MPVLib.setOptionString("blend-subtitles", "no") MPVLib.setOptionString("blend-subtitles", "no")
MPVLib.setOptionString("sub-use-margins", "no") MPVLib.setOptionString("sub-use-margins", "yes")
MPVLib.setOptionString("sub-ass-override", "scale") MPVLib.setOptionString("sub-ass-override", "force")
MPVLib.setOptionString("sub-scale", "1.0") MPVLib.setOptionString("sub-scale", "1.0")
MPVLib.setOptionString("sub-fix-timing", "yes") MPVLib.setOptionString("sub-fix-timing", "yes")
MPVLib.setOptionString("osc", "no")
MPVLib.setOptionString("osd-level", "1")
MPVLib.setOptionString("sid", "auto") MPVLib.setOptionString("sid", "auto")
MPVLib.setOptionString("terminal", "no") MPVLib.setOptionString("terminal", "no")

View file

@ -246,20 +246,15 @@ class KSPlayerView: UIView {
} }
private func setupPlayerCallbacks() { private func setupPlayerCallbacks() {
// Configure KSOptions (use static defaults where required) // Configure KSOptions
KSOptions.isAutoPlay = false KSOptions.isAutoPlay = false
#if targetEnvironment(simulator)
// Simulator: disable hardware decode and MEPlayer to avoid VT/Vulkan issues
KSOptions.hardwareDecode = false
KSOptions.asynchronousDecompression = false
KSOptions.secondPlayerType = nil
#else
// PERFORMANCE OPTIMIZATION: Enable asynchronous decompression globally
// This ensures the global default is correct for all player instances
KSOptions.asynchronousDecompression = true KSOptions.asynchronousDecompression = true
// Ensure hardware decode is enabled globally
KSOptions.hardwareDecode = true KSOptions.hardwareDecode = true
#endif
// Set default subtitle font size - use standard size for readability
SubtitleModel.textFontSize = 20.0 // Moderate size that works on most devices
SubtitleModel.textBold = false
print("KSPlayerView: [PERF] Global settings: asyncDecomp=\(KSOptions.asynchronousDecompression), hwDecode=\(KSOptions.hardwareDecode)") print("KSPlayerView: [PERF] Global settings: asyncDecomp=\(KSOptions.asynchronousDecompression), hwDecode=\(KSOptions.hardwareDecode)")
} }
@ -298,21 +293,14 @@ class KSPlayerView: UIView {
// Choose player pipeline based on format // Choose player pipeline based on format
let isMKV = uri.lowercased().contains(".mkv") let isMKV = uri.lowercased().contains(".mkv")
#if targetEnvironment(simulator)
if isMKV { if isMKV {
// MKV not supported on AVPlayer in Simulator and MEPlayer is disabled // Prefer MEPlayer (FFmpeg) for MKV
sendEvent("onError", ["error": "MKV playback is not supported in the iOS Simulator. Test on a real device."])
}
#else
if isMKV {
// Prefer MEPlayer (FFmpeg) for MKV on device
KSOptions.firstPlayerType = KSMEPlayer.self KSOptions.firstPlayerType = KSMEPlayer.self
KSOptions.secondPlayerType = nil KSOptions.secondPlayerType = nil
} else { } else {
KSOptions.firstPlayerType = KSAVPlayer.self KSOptions.firstPlayerType = KSAVPlayer.self
KSOptions.secondPlayerType = KSMEPlayer.self KSOptions.secondPlayerType = KSMEPlayer.self
} }
#endif
// Create KSPlayerResource with validated URL // Create KSPlayerResource with validated URL
let resource = KSPlayerResource(url: url, options: createOptions(with: headers), name: "Video") let resource = KSPlayerResource(url: url, options: createOptions(with: headers), name: "Video")
@ -376,15 +364,9 @@ class KSPlayerView: UIView {
// PERFORMANCE OPTIMIZATION: Hardware decode explicitly enabled // PERFORMANCE OPTIMIZATION: Hardware decode explicitly enabled
// Ensure VideoToolbox hardware acceleration is always preferred for non-simulator // Ensure VideoToolbox hardware acceleration is always preferred for non-simulator
#if targetEnvironment(simulator) // Hardware decode and async decompression
options.hardwareDecode = false options.hardwareDecode = true
options.asynchronousDecompression = false
#else
options.hardwareDecode = true // Explicitly enable hardware decode
// PERFORMANCE OPTIMIZATION: Asynchronous decompression (CRITICAL)
// Offloads VideoToolbox decompression to background threads, preventing main thread stalls
options.asynchronousDecompression = true options.asynchronousDecompression = true
#endif
// HDR handling: Let KSPlayer automatically detect content's native dynamic range // HDR handling: Let KSPlayer automatically detect content's native dynamic range
// Setting destinationDynamicRange to nil allows KSPlayer to use the content's actual HDR/SDR mode // Setting destinationDynamicRange to nil allows KSPlayer to use the content's actual HDR/SDR mode
@ -557,22 +539,21 @@ class KSPlayerView: UIView {
} }
func setTextTrack(_ trackId: Int) { func setTextTrack(_ trackId: Int) {
print("KSPlayerView: [SET TEXT TRACK] Starting setTextTrack with trackId: \(trackId)") NSLog("KSPlayerView: [SET TEXT TRACK] Starting setTextTrack with trackId: %d", trackId)
// Wait slightly longer than the 1-second delay for subtitle data source connection // Small delay to ensure player is ready
// This ensures srtControl.addSubtitle(dataSouce:) has been called in VideoPlayerView DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { [weak self] in
guard let self = self else { guard let self = self else {
print("KSPlayerView: [SET TEXT TRACK] self is nil, aborting") NSLog("KSPlayerView: [SET TEXT TRACK] self is nil, aborting")
return return
} }
print("KSPlayerView: [SET TEXT TRACK] Executing delayed track selection") NSLog("KSPlayerView: [SET TEXT TRACK] Executing track selection")
if let player = self.playerView.playerLayer?.player { if let player = self.playerView.playerLayer?.player {
let textTracks = player.tracks(mediaType: .subtitle) let textTracks = player.tracks(mediaType: .subtitle)
print("KSPlayerView: Available text tracks count: \(textTracks.count)") NSLog("KSPlayerView: Available text tracks count: %d", textTracks.count)
print("KSPlayerView: Requested text track ID: \(trackId)") NSLog("KSPlayerView: Requested text track ID: %d", trackId)
// First try to find track by trackID (proper way) // First try to find track by trackID (proper way)
var selectedTrack: MediaPlayerTrack? = nil var selectedTrack: MediaPlayerTrack? = nil
@ -582,84 +563,57 @@ class KSPlayerView: UIView {
if let track = textTracks.first(where: { Int($0.trackID) == trackId }) { if let track = textTracks.first(where: { Int($0.trackID) == trackId }) {
selectedTrack = track selectedTrack = track
trackIndex = textTracks.firstIndex(where: { $0.trackID == track.trackID }) ?? -1 trackIndex = textTracks.firstIndex(where: { $0.trackID == track.trackID }) ?? -1
print("KSPlayerView: Found text track by trackID \(trackId) at index \(trackIndex)") NSLog("KSPlayerView: Found text track by trackID %d at index %d", trackId, trackIndex)
} }
// Fallback: treat trackId as array index // Fallback: treat trackId as array index
else if trackId >= 0 && trackId < textTracks.count { else if trackId >= 0 && trackId < textTracks.count {
selectedTrack = textTracks[trackId] selectedTrack = textTracks[trackId]
trackIndex = trackId trackIndex = trackId
print("KSPlayerView: Found text track by array index \(trackId) (fallback)") NSLog("KSPlayerView: Found text track by array index %d (fallback)", trackId)
} }
if let track = selectedTrack { if let track = selectedTrack {
print("KSPlayerView: Selecting text track \(trackId) (index: \(trackIndex)): '\(track.name)' (ID: \(track.trackID))") NSLog("KSPlayerView: Selecting text track %d (index: %d): '%@' (ID: %d)", trackId, trackIndex, track.name, track.trackID)
// First disable all tracks to ensure only one is active // Disable all tracks first
for t in textTracks { for t in textTracks {
t.isEnabled = false t.isEnabled = false
} }
// Use KSPlayer's select method which properly handles track selection // Enable the selected track
track.isEnabled = true
// Use KSPlayer's select method to update player state
player.select(track: track) player.select(track: track)
// Sync srtControl with player track selection // CRITICAL: Cast MediaPlayerTrack to SubtitleInfo and set on srtControl
// Find the corresponding SubtitleInfo in srtControl and select it // FFmpegAssetTrack conforms to SubtitleInfo via extension
if let matchingSubtitleInfo = self.playerView.srtControl.subtitleInfos.first(where: { subtitleInfo in if let subtitleInfo = track as? SubtitleInfo {
// Try to match by name or track ID self.playerView.srtControl.selectedSubtitleInfo = subtitleInfo
subtitleInfo.name.lowercased() == track.name.lowercased() || NSLog("KSPlayerView: Set srtControl.selectedSubtitleInfo to track '%@'", track.name)
subtitleInfo.subtitleID == String(track.trackID)
}) {
print("KSPlayerView: Found matching SubtitleInfo: \(matchingSubtitleInfo.name) (ID: \(matchingSubtitleInfo.subtitleID))")
self.playerView.srtControl.selectedSubtitleInfo = matchingSubtitleInfo
print("KSPlayerView: Set srtControl.selectedSubtitleInfo to: \(matchingSubtitleInfo.name)")
} else { } else {
print("KSPlayerView: No matching SubtitleInfo found for track '\(track.name)' (ID: \(track.trackID))") NSLog("KSPlayerView: Warning - track could not be cast to SubtitleInfo")
print("KSPlayerView: Available SubtitleInfos:")
for (index, info) in self.playerView.srtControl.subtitleInfos.enumerated() {
print("KSPlayerView: [\(index)] name='\(info.name)', subtitleID='\(info.subtitleID)'")
}
} }
// Ensure subtitle views are visible after selection // Ensure subtitle views are visible
self.playerView.subtitleLabel.isHidden = false self.playerView.subtitleLabel.isHidden = false
self.playerView.subtitleBackView.isHidden = false self.playerView.subtitleBackView.isHidden = false
// Debug: Check the enabled state of all tracks after selection NSLog("KSPlayerView: Successfully selected and enabled text track %d", trackId)
print("KSPlayerView: Track states after selection:")
for (index, t) in textTracks.enumerated() {
print("KSPlayerView: Track \(index): ID=\(t.trackID), Name='\(t.name)', Enabled=\(t.isEnabled)")
}
// Verify the selection worked after a delay
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let tracksAfter = player.tracks(mediaType: .subtitle)
print("KSPlayerView: Verification after subtitle selection:")
for (index, track) in tracksAfter.enumerated() {
print("KSPlayerView: Track \(index) (ID: \(track.trackID)) isEnabled: \(track.isEnabled)")
}
// Also verify srtControl selection
if let selectedInfo = self.playerView.srtControl.selectedSubtitleInfo {
print("KSPlayerView: srtControl.selectedSubtitleInfo: \(selectedInfo.name) (ID: \(selectedInfo.subtitleID))")
} else {
print("KSPlayerView: srtControl.selectedSubtitleInfo is nil")
}
}
print("KSPlayerView: Successfully selected text track \(trackId)")
} else if trackId == -1 { } else if trackId == -1 {
// Disable all subtitles // Disable all subtitles
for track in textTracks { track.isEnabled = false } for track in textTracks {
// Clear srtControl selection and hide subtitle views track.isEnabled = false
}
self.playerView.srtControl.selectedSubtitleInfo = nil self.playerView.srtControl.selectedSubtitleInfo = nil
self.playerView.subtitleLabel.isHidden = true self.playerView.subtitleLabel.isHidden = true
self.playerView.subtitleBackView.isHidden = true self.playerView.subtitleBackView.isHidden = true
print("KSPlayerView: Disabled all text tracks and cleared srtControl selection") NSLog("KSPlayerView: Disabled all text tracks")
} else { } else {
print("KSPlayerView: Text track \(trackId) not found. Available track IDs: \(textTracks.map { Int($0.trackID) }), array indices: 0..\(textTracks.count - 1)") NSLog("KSPlayerView: Text track %d not found. Available count: %d", trackId, textTracks.count)
} }
} else { } else {
print("KSPlayerView: No player available for text track selection") NSLog("KSPlayerView: No player available for text track selection")
} }
} }
} }
@ -1015,7 +969,18 @@ extension KSPlayerView: KSPlayerLayerDelegate {
if let part = playerView.srtControl.parts.first { if let part = playerView.srtControl.parts.first {
print("KSPlayerView: [SUBTITLE RENDER] time=\(currentTime), text='\(part.text?.string ?? "nil")', hasImage=\(part.image != nil)") print("KSPlayerView: [SUBTITLE RENDER] time=\(currentTime), text='\(part.text?.string ?? "nil")', hasImage=\(part.image != nil)")
playerView.subtitleBackView.image = part.image playerView.subtitleBackView.image = part.image
playerView.subtitleLabel.attributedText = part.text
// Normalize font size for all subtitles to ensure consistent display
if let originalText = part.text {
let mutableText = NSMutableAttributedString(attributedString: originalText)
// Apply consistent font across the entire text
let font = UIFont.systemFont(ofSize: 20.0)
mutableText.addAttributes([.font: font], range: NSRange(location: 0, length: mutableText.length))
playerView.subtitleLabel.attributedText = mutableText
} else {
playerView.subtitleLabel.attributedText = nil
}
playerView.subtitleBackView.isHidden = false playerView.subtitleBackView.isHidden = false
playerView.subtitleLabel.isHidden = false playerView.subtitleLabel.isHidden = false
print("KSPlayerView: [SUBTITLE RENDER] Set subtitle text and made views visible") print("KSPlayerView: [SUBTITLE RENDER] Set subtitle text and made views visible")

View file

@ -87,9 +87,14 @@ class KSPlayerViewManager: RCTViewManager {
} }
@objc func setTextTrack(_ node: NSNumber, trackId: NSNumber) { @objc func setTextTrack(_ node: NSNumber, trackId: NSNumber) {
NSLog("[KSPlayerViewManager] setTextTrack called - node: %@, trackId: %@", node, trackId)
DispatchQueue.main.async { DispatchQueue.main.async {
NSLog("[KSPlayerViewManager] setTextTrack on main queue - looking for view with tag: %@", node)
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView { if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
NSLog("[KSPlayerViewManager] Found view, calling setTextTrack(%d)", Int(truncating: trackId))
view.setTextTrack(Int(truncating: trackId)) view.setTextTrack(Int(truncating: trackId))
} else {
NSLog("[KSPlayerViewManager] ERROR - Could not find KSPlayerView for tag: %@", node)
} }
} }
} }

View file

@ -476,6 +476,7 @@
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app; PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app;
PRODUCT_NAME = Nuvio; PRODUCT_NAME = Nuvio;
SUPPORTS_MACCATALYST = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -508,6 +509,7 @@
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.hub; PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.hub;
PRODUCT_NAME = Nuvio; PRODUCT_NAME = Nuvio;
SUPPORTS_MACCATALYST = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";

View file

@ -19,7 +19,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.3.1</string> <string>1.2.10</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>

2
package-lock.json generated
View file

@ -44,7 +44,7 @@
"expo-brightness": "~14.0.7", "expo-brightness": "~14.0.7",
"expo-clipboard": "~8.0.8", "expo-clipboard": "~8.0.8",
"expo-crypto": "~15.0.7", "expo-crypto": "~15.0.7",
"expo-dev-client": "~6.0.15", "expo-dev-client": "~6.0.20",
"expo-device": "~8.0.9", "expo-device": "~8.0.9",
"expo-document-picker": "~14.0.7", "expo-document-picker": "~14.0.7",
"expo-file-system": "~19.0.17", "expo-file-system": "~19.0.17",

View file

@ -44,7 +44,7 @@
"expo-brightness": "~14.0.7", "expo-brightness": "~14.0.7",
"expo-clipboard": "~8.0.8", "expo-clipboard": "~8.0.8",
"expo-crypto": "~15.0.7", "expo-crypto": "~15.0.7",
"expo-dev-client": "~6.0.15", "expo-dev-client": "~6.0.20",
"expo-device": "~8.0.9", "expo-device": "~8.0.9",
"expo-document-picker": "~14.0.7", "expo-document-picker": "~14.0.7",
"expo-file-system": "~19.0.17", "expo-file-system": "~19.0.17",

View file

@ -120,11 +120,16 @@ const KSPlayer = forwardRef<KSPlayerRef, KSPlayerProps>((props, ref) => {
} }
}, },
setTextTrack: (trackId: number) => { setTextTrack: (trackId: number) => {
console.log('[KSPlayerComponent] setTextTrack called with trackId:', trackId);
if (nativeRef.current) { if (nativeRef.current) {
const node = findNodeHandle(nativeRef.current); const node = findNodeHandle(nativeRef.current);
console.log('[KSPlayerComponent] setTextTrack dispatching command to node:', node);
// @ts-ignore legacy UIManager commands path for Paper // @ts-ignore legacy UIManager commands path for Paper
const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setTextTrack; const commandId = UIManager.getViewManagerConfig('KSPlayerView').Commands.setTextTrack;
console.log('[KSPlayerComponent] setTextTrack commandId:', commandId);
UIManager.dispatchViewManagerCommand(node, commandId, [trackId]); UIManager.dispatchViewManagerCommand(node, commandId, [trackId]);
} else {
console.warn('[KSPlayerComponent] setTextTrack: nativeRef.current is null');
} }
}, },
getTracks: async () => { getTracks: async () => {

View file

@ -389,6 +389,27 @@ const KSPlayerCore: React.FC = () => {
navigation.goBack(); navigation.goBack();
}, [navigation, currentTime, duration, traktAutosync]); }, [navigation, currentTime, duration, traktAutosync]);
// Track selection handlers - update state, prop change triggers native update
const handleSelectTextTrack = useCallback((trackId: number) => {
console.log('[KSPlayerCore] handleSelectTextTrack called with trackId:', trackId);
// Disable custom subtitles when selecting a built-in track
// This ensures the textTrack prop is actually passed to the native player
if (trackId !== -1) {
customSubs.setUseCustomSubtitles(false);
}
// Just update state - the textTrack prop change will trigger native update
tracks.selectTextTrack(trackId);
}, [tracks, customSubs]);
const handleSelectAudioTrack = useCallback((trackId: number) => {
tracks.selectAudioTrack(trackId);
if (ksPlayerRef.current) {
ksPlayerRef.current.setAudioTrack(trackId);
}
}, [tracks, ksPlayerRef]);
// Stream selection handler // Stream selection handler
const handleSelectStream = async (newStream: any) => { const handleSelectStream = async (newStream: any) => {
if (newStream.url === uri) { if (newStream.url === uri) {
@ -498,8 +519,8 @@ const KSPlayerCore: React.FC = () => {
setZoomScale={setZoomScale} setZoomScale={setZoomScale}
lastZoomScale={lastZoomScale} lastZoomScale={lastZoomScale}
setLastZoomScale={setLastZoomScale} setLastZoomScale={setLastZoomScale}
audioTrack={tracks.selectedAudioTrack !== null ? tracks.selectedAudioTrack : undefined} audioTrack={tracks.selectedAudioTrack ?? undefined}
textTrack={customSubs.useCustomSubtitles ? undefined : (tracks.selectedTextTrack !== -1 ? tracks.selectedTextTrack : undefined)} textTrack={customSubs.useCustomSubtitles ? -1 : tracks.selectedTextTrack}
onAudioTracks={(d) => tracks.setKsAudioTracks(d.audioTracks || [])} onAudioTracks={(d) => tracks.setKsAudioTracks(d.audioTracks || [])}
onTextTracks={(d) => tracks.setKsTextTracks(d.textTracks || [])} onTextTracks={(d) => tracks.setKsTextTracks(d.textTracks || [])}
onLoad={onLoad} onLoad={onLoad}
@ -682,7 +703,7 @@ const KSPlayerCore: React.FC = () => {
setShowAudioModal={modals.setShowAudioModal} setShowAudioModal={modals.setShowAudioModal}
ksAudioTracks={tracks.ksAudioTracks} ksAudioTracks={tracks.ksAudioTracks}
selectedAudioTrack={tracks.selectedAudioTrack} selectedAudioTrack={tracks.selectedAudioTrack}
selectAudioTrack={tracks.selectAudioTrack} selectAudioTrack={handleSelectAudioTrack}
/> />
<ErrorModal <ErrorModal
@ -744,10 +765,10 @@ const KSPlayerCore: React.FC = () => {
ksTextTracks={tracks.ksTextTracks} ksTextTracks={tracks.ksTextTracks}
selectedTextTrack={tracks.selectedTextTrack !== null ? tracks.selectedTextTrack : -1} selectedTextTrack={tracks.selectedTextTrack !== null ? tracks.selectedTextTrack : -1}
useCustomSubtitles={customSubs.useCustomSubtitles} useCustomSubtitles={customSubs.useCustomSubtitles}
selectTextTrack={tracks.selectTextTrack} selectTextTrack={handleSelectTextTrack}
disableCustomSubtitles={() => { disableCustomSubtitles={() => {
customSubs.setUseCustomSubtitles(false); customSubs.setUseCustomSubtitles(false);
tracks.selectTextTrack(-1); handleSelectTextTrack(-1);
}} }}
/> />

View file

@ -87,6 +87,11 @@ export const KSPlayerSurface: React.FC<KSPlayerSurfaceProps> = ({
headers headers
}; };
// Debug: log textTrack prop changes
React.useEffect(() => {
console.log('[KSPlayerSurface] textTrack prop changed to:', textTrack);
}, [textTrack]);
// Handle buffering - KSPlayerComponent uses onBuffering callback // Handle buffering - KSPlayerComponent uses onBuffering callback
const handleBuffering = (data: any) => { const handleBuffering = (data: any) => {
onBuffer(data?.isBuffering ?? false); onBuffer(data?.isBuffering ?? false);