Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add media source selection #3566

Merged
merged 1 commit into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.jellyfin.playback.core.mediastream.PlayableMediaStream
import org.jellyfin.playback.core.queue.QueueEntry
import org.jellyfin.playback.core.support.PlaySupportReport
import org.jellyfin.playback.jellyfin.queue.baseItem
import org.jellyfin.playback.jellyfin.queue.mediaSourceId
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.audioApi
import org.jellyfin.sdk.api.client.extensions.dynamicHlsApi
Expand Down Expand Up @@ -54,7 +55,7 @@ class AudioMediaStreamResolver(
val baseItem = queueEntry.baseItem
if (baseItem == null || baseItem.mediaType != MediaType.Audio) return null

val mediaInfo = getPlaybackInfo(baseItem)
val mediaInfo = getPlaybackInfo(baseItem, queueEntry.mediaSourceId)

// Test for direct play support
val directPlayStream = mediaInfo.getDirectPlayStream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.mediaInfoApi
import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.api.DeviceProfile
import org.jellyfin.sdk.model.api.MediaProtocol
import org.jellyfin.sdk.model.api.MediaSourceInfo
import org.jellyfin.sdk.model.api.PlaybackInfoDto

Expand All @@ -19,41 +20,41 @@ abstract class JellyfinStreamResolver(

protected suspend fun getPlaybackInfo(
item: BaseItemDto,
mediaSource: MediaSourceInfo? = null,
mediaSourceId: String? = null,
): MediaInfo {
val response by api.mediaInfoApi.getPostedPlaybackInfo(
itemId = item.id,
data = PlaybackInfoDto(
userId = api.userId,
maxStreamingBitrate = profile.maxStreamingBitrate,
mediaSourceId = mediaSource?.id,
liveStreamId = mediaSource?.liveStreamId,
mediaSourceId = mediaSourceId,
deviceProfile = profile,
enableDirectPlay = true,
enableDirectStream = true,
enableTranscoding = true,
allowVideoStreamCopy = true,
allowAudioStreamCopy = true,
autoOpenLiveStream = true,
autoOpenLiveStream = false,
)
)

if (response.errorCode != null) {
error("Failed to get media info for item ${item.id} source ${mediaSource?.id}: ${response.errorCode}")
error("Failed to get media info for item ${item.id} source ${mediaSourceId}: ${response.errorCode}")
}

val responseMediaSource = when (mediaSource) {
null -> response.mediaSources.firstOrNull()
else -> response.mediaSources.firstOrNull { it.id === mediaSource.id }
}
val mediaSource = response.mediaSources
// Filter out invalid streams (like strm files)
.filter { it.protocol == MediaProtocol.FILE && !it.isRemote }
// Select first media source
.firstOrNull { mediaSourceId == null || it.id == mediaSourceId }

requireNotNull(responseMediaSource) {
"Failed to get media info for item ${item.id} source ${mediaSource?.id}: media source missing in response"
requireNotNull(mediaSource) {
"Failed to get media info for item ${item.id} source ${mediaSourceId}: media source missing in response"
}

return MediaInfo(
playSessionId = response.playSessionId.orEmpty(),
mediaSource = responseMediaSource
mediaSource = mediaSource
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.jellyfin.playback.core.mediastream.PlayableMediaStream
import org.jellyfin.playback.core.queue.QueueEntry
import org.jellyfin.playback.core.support.PlaySupportReport
import org.jellyfin.playback.jellyfin.queue.baseItem
import org.jellyfin.playback.jellyfin.queue.mediaSourceId
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.universalAudioApi
import org.jellyfin.sdk.model.api.BaseItemKind
Expand All @@ -22,7 +23,7 @@ class UniversalAudioMediaStreamResolver(
val baseItem = queueEntry.baseItem
if (baseItem == null || baseItem.type != BaseItemKind.AUDIO) return null

val mediaInfo = getPlaybackInfo(baseItem)
val mediaInfo = getPlaybackInfo(baseItem, queueEntry.mediaSourceId)

val url = api.universalAudioApi.getUniversalAudioStreamUrl(
itemId = baseItem.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.jellyfin.playback.core.mediastream.PlayableMediaStream
import org.jellyfin.playback.core.queue.QueueEntry
import org.jellyfin.playback.core.support.PlaySupportReport
import org.jellyfin.playback.jellyfin.queue.baseItem
import org.jellyfin.playback.jellyfin.queue.mediaSourceId
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.dynamicHlsApi
import org.jellyfin.sdk.api.client.extensions.videosApi
Expand Down Expand Up @@ -54,7 +55,7 @@ class VideoMediaStreamResolver(
val baseItem = queueEntry.baseItem
if (baseItem == null || baseItem.mediaType != MediaType.Video) return null

val mediaInfo = getPlaybackInfo(baseItem)
val mediaInfo = getPlaybackInfo(baseItem, queueEntry.mediaSourceId)

// Test for direct play support
val directPlayStream = mediaInfo.getDirectPlayStream()
Expand Down
13 changes: 13 additions & 0 deletions playback/jellyfin/src/main/kotlin/queue/mediaSourceIdElement.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.jellyfin.playback.jellyfin.queue

import org.jellyfin.playback.core.element.ElementKey
import org.jellyfin.playback.core.element.element
import org.jellyfin.playback.core.queue.QueueEntry

private val mediaSourceIdKey = ElementKey<String>("MediaSource")

/**
* Get or set the id of the MediaSource to use during playback. Or null for the default selection
* behavior.
*/
var QueueEntry.mediaSourceId by element(mediaSourceIdKey)