fix: ios player not recreating after switching source/episode

This commit is contained in:
tapframe 2026-05-02 14:05:47 +05:30
parent c890cc3400
commit 4f62be91b8

View file

@ -34,180 +34,182 @@ actual fun PlatformPlayerSurface(
onError: (String?) -> Unit,
) {
sanitizePlaybackResponseHeaders(sourceResponseHeaders)
val latestOnControllerReady = rememberUpdatedState(onControllerReady)
val latestOnSnapshot = rememberUpdatedState(onSnapshot)
val latestOnError = rememberUpdatedState(onError)
val bridge = remember(sourceUrl) {
val bridge = remember {
NuvioPlayerBridgeFactory.create()
}
if (bridge == null) {
LaunchedEffect(Unit) {
onError("MPV player engine not available. Please rebuild the app.")
latestOnError.value("MPV player engine not available. Please rebuild the app.")
}
return
}
// Create controller
LaunchedEffect(bridge) {
onControllerReady(
object : PlayerEngineController {
override fun play() {
bridge.play()
}
val controller = remember(bridge) {
object : PlayerEngineController {
override fun play() {
bridge.play()
}
override fun pause() {
bridge.pause()
}
override fun pause() {
bridge.pause()
}
override fun seekTo(positionMs: Long) {
bridge.seekTo(positionMs)
}
override fun seekTo(positionMs: Long) {
bridge.seekTo(positionMs)
}
override fun seekBy(offsetMs: Long) {
bridge.seekBy(offsetMs)
}
override fun seekBy(offsetMs: Long) {
bridge.seekBy(offsetMs)
}
override fun retry() {
bridge.retry()
}
override fun retry() {
bridge.retry()
}
override fun setPlaybackSpeed(speed: Float) {
bridge.setPlaybackSpeed(speed)
}
override fun setPlaybackSpeed(speed: Float) {
bridge.setPlaybackSpeed(speed)
}
override fun getAudioTracks(): List<AudioTrack> {
val count = bridge.getAudioTrackCount()
return (0 until count).map { i ->
AudioTrack(
index = bridge.getAudioTrackIndex(i),
id = bridge.getAudioTrackId(i),
label = bridge.getAudioTrackLabel(i),
language = bridge.getAudioTrackLang(i),
isSelected = bridge.isAudioTrackSelected(i),
)
}
override fun getAudioTracks(): List<AudioTrack> {
val count = bridge.getAudioTrackCount()
return (0 until count).map { i ->
AudioTrack(
index = bridge.getAudioTrackIndex(i),
id = bridge.getAudioTrackId(i),
label = bridge.getAudioTrackLabel(i),
language = bridge.getAudioTrackLang(i),
isSelected = bridge.isAudioTrackSelected(i),
)
}
}
override fun getSubtitleTracks(): List<SubtitleTrack> {
val count = bridge.getSubtitleTrackCount()
val tracks = (0 until count).map { i ->
val trackId = bridge.getSubtitleTrackId(i)
val trackLabel = bridge.getSubtitleTrackLabel(i)
val trackLanguage = bridge.getSubtitleTrackLang(i)
SubtitleTrack(
index = bridge.getSubtitleTrackIndex(i),
id = trackId,
override fun getSubtitleTracks(): List<SubtitleTrack> {
val count = bridge.getSubtitleTrackCount()
val tracks = (0 until count).map { i ->
val trackId = bridge.getSubtitleTrackId(i)
val trackLabel = bridge.getSubtitleTrackLabel(i)
val trackLanguage = bridge.getSubtitleTrackLang(i)
SubtitleTrack(
index = bridge.getSubtitleTrackIndex(i),
id = trackId,
label = trackLabel,
language = trackLanguage,
isSelected = bridge.isSubtitleTrackSelected(i),
isForced = inferForcedSubtitleTrack(
label = trackLabel,
language = trackLanguage,
isSelected = bridge.isSubtitleTrackSelected(i),
isForced = inferForcedSubtitleTrack(
label = trackLabel,
language = trackLanguage,
trackId = trackId,
),
)
}
Logger.d(TAG) { "getSubtitleTracks: found ${tracks.size} tracks" }
return tracks
trackId = trackId,
),
)
}
Logger.d(TAG) { "getSubtitleTracks: found ${tracks.size} tracks" }
return tracks
}
override fun selectAudioTrack(index: Int) {
// Convert from logical track index to mpv track id
val count = bridge.getAudioTrackCount()
override fun selectAudioTrack(index: Int) {
// Convert from logical track index to mpv track id
val count = bridge.getAudioTrackCount()
if (count <= 0) return
val trackId = (0 until count)
.firstNotNullOfOrNull { at ->
if (bridge.getAudioTrackIndex(at) == index) {
bridge.getAudioTrackId(at).toIntOrNull()
} else {
null
}
}
?: if (index in 0 until count) {
bridge.getAudioTrackId(index).toIntOrNull() ?: (index + 1)
} else {
null
}
if (trackId != null) {
bridge.selectAudioTrack(trackId)
}
}
override fun selectSubtitleTrack(index: Int) {
if (index < 0) {
bridge.selectSubtitleTrack(-1) // disable
} else {
val count = bridge.getSubtitleTrackCount()
if (count <= 0) return
val trackId = (0 until count)
.firstNotNullOfOrNull { at ->
if (bridge.getAudioTrackIndex(at) == index) {
bridge.getAudioTrackId(at).toIntOrNull()
if (bridge.getSubtitleTrackIndex(at) == index) {
bridge.getSubtitleTrackId(at).toIntOrNull()
} else {
null
}
}
?: if (index in 0 until count) {
bridge.getAudioTrackId(index).toIntOrNull() ?: (index + 1)
bridge.getSubtitleTrackId(index).toIntOrNull() ?: (index + 1)
} else {
null
}
if (trackId != null) {
bridge.selectAudioTrack(trackId)
bridge.selectSubtitleTrack(trackId)
}
}
}
override fun selectSubtitleTrack(index: Int) {
if (index < 0) {
bridge.selectSubtitleTrack(-1) // disable
override fun setSubtitleUri(url: String) {
Logger.d(TAG) { "setSubtitleUri: $url" }
bridge.setSubtitleUrl(url)
}
override fun clearExternalSubtitle() {
bridge.clearExternalSubtitle()
}
override fun clearExternalSubtitleAndSelect(trackIndex: Int) {
val trackId = if (trackIndex < 0) {
-1
} else {
val count = bridge.getSubtitleTrackCount()
if (count <= 0) {
trackIndex + 1
} else {
val count = bridge.getSubtitleTrackCount()
if (count <= 0) return
val trackId = (0 until count)
(0 until count)
.firstNotNullOfOrNull { at ->
if (bridge.getSubtitleTrackIndex(at) == index) {
if (bridge.getSubtitleTrackIndex(at) == trackIndex) {
bridge.getSubtitleTrackId(at).toIntOrNull()
} else {
null
}
}
?: if (index in 0 until count) {
bridge.getSubtitleTrackId(index).toIntOrNull() ?: (index + 1)
?: if (trackIndex in 0 until count) {
bridge.getSubtitleTrackId(trackIndex).toIntOrNull() ?: (trackIndex + 1)
} else {
null
trackIndex + 1
}
if (trackId != null) {
bridge.selectSubtitleTrack(trackId)
}
}
}
override fun setSubtitleUri(url: String) {
Logger.d(TAG) { "setSubtitleUri: $url" }
bridge.setSubtitleUrl(url)
}
override fun clearExternalSubtitle() {
bridge.clearExternalSubtitle()
}
override fun clearExternalSubtitleAndSelect(trackIndex: Int) {
val trackId = if (trackIndex < 0) {
-1
} else {
val count = bridge.getSubtitleTrackCount()
if (count <= 0) {
trackIndex + 1
} else {
(0 until count)
.firstNotNullOfOrNull { at ->
if (bridge.getSubtitleTrackIndex(at) == trackIndex) {
bridge.getSubtitleTrackId(at).toIntOrNull()
} else {
null
}
}
?: if (trackIndex in 0 until count) {
bridge.getSubtitleTrackId(trackIndex).toIntOrNull() ?: (trackIndex + 1)
} else {
trackIndex + 1
}
}
}
bridge.clearExternalSubtitleAndSelect(trackId)
}
override fun applySubtitleStyle(style: SubtitleStyleState) {
bridge.applySubtitleStyle(
textColor = style.textColor.toMpvColorString(),
outlineSize = if (style.outlineEnabled) 1.65f else 0f,
fontSize = style.toMpvSubtitleFontSize(),
subPos = style.toMpvSubtitlePosition(),
)
}
bridge.clearExternalSubtitleAndSelect(trackId)
}
)
override fun applySubtitleStyle(style: SubtitleStyleState) {
bridge.applySubtitleStyle(
textColor = style.textColor.toMpvColorString(),
outlineSize = if (style.outlineEnabled) 1.65f else 0f,
fontSize = style.toMpvSubtitleFontSize(),
subPos = style.toMpvSubtitlePosition(),
)
}
}
}
LaunchedEffect(controller, sourceUrl, sourceAudioUrl, sourceHeaders, sourceResponseHeaders) {
latestOnControllerReady.value(controller)
}
// Load file and set initial state