Handle external subtitles

This commit is contained in:
Damontecres 2025-10-04 20:25:43 -04:00
parent 64e881f51c
commit 81d0279cce
No known key found for this signature in database
3 changed files with 60 additions and 5 deletions

View file

@ -6,6 +6,7 @@ data class SubtitleStream(
val title: String?,
val codec: String?,
val codecTag: String?,
val external: Boolean,
) {
val displayName: String
get() =

View file

@ -22,6 +22,7 @@ import com.github.damontecres.dolphin.util.TrackActivityPlaybackListener
import com.github.damontecres.dolphin.util.TrackSupport
import com.github.damontecres.dolphin.util.checkForSupport
import com.github.damontecres.dolphin.util.profile.PlaybackListener
import com.github.damontecres.dolphin.util.subtitleMimeTypes
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
@ -142,7 +143,14 @@ class PlaybackViewModel
base.mediaStreams
?.filter { it.type == MediaStreamType.SUBTITLE }
?.map {
SubtitleStream(it.index, it.language, it.title, it.codec, it.codecTag)
SubtitleStream(
it.index,
it.language,
it.title,
it.codec,
it.codecTag,
it.isExternal,
)
}.orEmpty()
val audioStreams =
base.mediaStreams
@ -226,7 +234,7 @@ class PlaybackViewModel
allowAudioStreamCopy = true,
autoOpenLiveStream = true,
mediaSourceId = null,
alwaysBurnInSubtitleWhenTranscoding = false,
alwaysBurnInSubtitleWhenTranscoding = null,
),
).content
val source = response.mediaSources.firstOrNull()
@ -254,6 +262,33 @@ class PlaybackViewModel
val decision = StreamDecision(itemId, transcodeType, mediaUrl)
Timber.v("Playback decision: $decision")
val externalSubtitle =
source.mediaStreams
?.firstOrNull { it.type == MediaStreamType.SUBTITLE && it.index == subtitleIndex && it.isExternal }
?.let {
it.deliveryUrl?.let { deliveryUrl ->
var flags = 0
if (it.isForced) flags = flags.and(C.SELECTION_FLAG_FORCED)
if (it.isDefault) flags = flags.and(C.SELECTION_FLAG_DEFAULT)
MediaItem.SubtitleConfiguration
.Builder(
api.createUrl(deliveryUrl).toUri(),
).setId("${it.index + 1}") // Indexes are 1 based
.setMimeType(subtitleMimeTypes[it.codec])
.setLanguage(it.language)
.setSelectionFlags(flags)
.build()
}
}
val mediaItem =
MediaItem
.Builder()
.setMediaId(itemId.toString())
.setUri(mediaUrl.toUri())
.setSubtitleConfigurations(listOfNotNull(externalSubtitle))
.build()
val playback =
CurrentPlayback(
itemId,
@ -268,7 +303,7 @@ class PlaybackViewModel
stream.value = decision
currentPlayback.value = playback
player.setMediaItem(
decision.mediaItem,
mediaItem,
positionMs,
)
if (audioIndex != null || subtitleIndex != null) {
@ -377,7 +412,7 @@ private fun applyTrackSelections(
(0..<group.mediaTrackGroup.length)
.map {
group.getTrackFormat(it).idAsInt
}.contains(subtitleIndex + 1)
}.contains(subtitleIndex + 1) // Indexes are 1 based
}
Timber.v("Chosen subtitle track: $chosenTrack")
chosenTrack?.let {
@ -406,7 +441,7 @@ private fun applyTrackSelections(
(0..<group.mediaTrackGroup.length)
.map {
group.getTrackFormat(it).idAsInt
}.contains(audioIndex + 1)
}.contains(audioIndex + 1) // Indexes are 1 based
}
Timber.v("Chosen audio track: $chosenTrack")
chosenTrack?.let {

View file

@ -0,0 +1,19 @@
package com.github.damontecres.dolphin.util
import androidx.media3.common.MimeTypes
import com.github.damontecres.dolphin.util.profile.Codec
val subtitleMimeTypes =
mapOf(
Codec.Subtitle.ASS to MimeTypes.TEXT_SSA,
Codec.Subtitle.DVBSUB to MimeTypes.APPLICATION_VOBSUB,
Codec.Subtitle.DVBSUB to MimeTypes.APPLICATION_VOBSUB,
Codec.Subtitle.PGS to MimeTypes.APPLICATION_PGS,
Codec.Subtitle.PGSSUB to MimeTypes.APPLICATION_PGS,
Codec.Subtitle.SRT to MimeTypes.APPLICATION_SUBRIP,
Codec.Subtitle.SSA to MimeTypes.TEXT_SSA,
Codec.Subtitle.SUBRIP to MimeTypes.APPLICATION_SUBRIP,
Codec.Subtitle.TTML to MimeTypes.APPLICATION_TTML,
Codec.Subtitle.VTT to MimeTypes.TEXT_VTT,
Codec.Subtitle.WEBVTT to MimeTypes.TEXT_VTT,
)