Fix live tv & recorded tv playback (#1770)

## Description
#1654 broke playback for both live TV and recorded programs in `.ts`
format.

This PR fixes both issues! Jellyfin returns `NoCompatibleStream` for
live tv if the source ID is passed in, so that is now skipped for live
tv. `.ts` files may have IDs formatted with a slash instead of colon, so
the logic is updated to handle that plus fallbacks for potentially
unknown future formats.

### Related issues
Fixes #1767

### Testing
Emulator & nvidia shield, plus unit tests

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Damontecres 2026-08-02 11:11:32 -04:00 • committed by GitHub
parent 7c6ab39436
commit 6ad96865e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 116 additions and 33 deletions

View file

@ -296,4 +296,61 @@ class TestSelectionTrackExamples {
Assert.assertFalse(result.bothSelected)
}
}
@Test
fun `test exo track IDs with slashes`() {
val testTracks =
TestTracks(
listOf(
// .ts files (eg recorded TV) report IDs like this in ExoPlayer
TestTrack("3/49", 0, MediaStreamType.VIDEO, false),
TestTrack("3/52", 1, MediaStreamType.AUDIO, false),
TestTrack("", 2, MediaStreamType.DATA, false),
TestTrack("3/53", 3, MediaStreamType.AUDIO, false),
),
)
val mediaSource =
TestTracks
.Builder()
.addVideo()
.addAudio()
.addTrack(1, MediaStreamType.DATA, false, C.FORMAT_HANDLED)
.addAudio()
.buildMediaSourceInfo()
val trackSelectionParameters = TrackSelectionParameters.Builder().build()
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = testTracks.toTracks(),
audioIndex = 1,
subtitleIndex = null,
source = mediaSource,
).let { result ->
Assert.assertEquals(TrackSelected.SELECTED, result.audio)
Assert.assertTrue(result.bothSelected)
}
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = testTracks.toTracks(),
audioIndex = 2,
subtitleIndex = null,
source = mediaSource,
).let { result ->
Assert.assertEquals(TrackSelected.NOT_FOUND, result.audio)
Assert.assertFalse(result.bothSelected)
}
TrackSelectionUtils
.createTrackSelections(
trackSelectionParams = trackSelectionParameters,
tracks = testTracks.toTracks(),
audioIndex = 3,
subtitleIndex = null,
source = mediaSource,
).let { result ->
Assert.assertEquals(TrackSelected.SELECTED, result.audio)
Assert.assertTrue(result.bothSelected)
}
}
}

View file

@ -2,6 +2,7 @@ package com.github.damontecres.wholphin.test
import androidx.media3.common.C
import androidx.media3.common.Format
import androidx.media3.common.MimeTypes
import androidx.media3.common.TrackGroup
import androidx.media3.common.Tracks
import org.jellyfin.sdk.model.api.MediaProtocol
@ -35,6 +36,7 @@ class TestTracks(
MediaStreamType.VIDEO -> "video/sample"
MediaStreamType.AUDIO -> "audio/default"
MediaStreamType.SUBTITLE -> "text/sample"
MediaStreamType.DATA -> MimeTypes.APPLICATION_ID3
else -> throw UnsupportedOperationException("${it.type}")
}
val format =