source_utils_versionPick.bs
import "pkg:/source/utils/misc.bs"
import "pkg:/source/utils/streamSelection.bs"
import "pkg:/source/utils/versionLabels.bs"
' The version a viewer explicitly picked, carried to the items a queue arrives at.
'
' A pick is made in the details screen's Video menu or by a switch in the player, and lives on
' the queue (QueueManager.setVersionPreference), so a new queue starts without one. An item the
' queue arrives at resumes nothing, so no position has a say in its version: it plays the one
' with the same video (resolution, codec, HDR range) when it has it, otherwise the best for the
' device. Matched on the stream, never the name: a name is the file's, so across items it only
' lines up when every item's versions share one naming scheme (jellyfin-web's rule), and it
' would pin a viewer to 1080p when the next item also has 4K. Only an explicit pick carries;
' an automatic choice stays automatic, so upgrades still happen.
namespace versionPick
' preferenceFor: What an explicit version pick carries to later items — the video fields a
' version is told apart by, written as the version labels write them.
'
' @param {dynamic} source - the MediaSource the viewer picked
' @return {dynamic} { resolution, codec, range }, or invalid when the source has no video
function preferenceFor(source as dynamic) as dynamic
if not isValid(source) then return invalid
fields = versionLabels.videoFields(source)
if fields.resolution = "" and fields.codec = "" then return invalid
return fields
end function
' matchIndex: The first source whose video matches the preference, or -1.
'
' @param {dynamic} preference - from preferenceFor(), or invalid for "no explicit pick"
' @param {dynamic} mediaSources - the candidate MediaSources
' @return {integer}
function matchIndex(preference as dynamic, mediaSources as dynamic) as integer
if not isValid(preference) or not isValid(mediaSources) then return -1
for i = 0 to mediaSources.count() - 1
if not isValid(mediaSources[i]) then continue for
fields = versionLabels.videoFields(mediaSources[i])
if fields.resolution = preference.resolution and fields.codec = preference.codec and fields.range = preference.range then return i
end for
return -1
end function
' sourceIndexFor: Which version plays — the explicit pick's match, else the best for the device.
'
' @param {dynamic} preference - from preferenceFor(), or invalid
' @param {object} mediaSources - the item's MediaSources
' @param {dynamic} deviceCapabilities - for tests; invalid reads the device
' @return {integer}
function sourceIndexFor(preference as dynamic, mediaSources as object, deviceCapabilities = invalid as dynamic) as integer
index = matchIndex(preference, mediaSources)
if index >= 0 then return index
return findBestVideoSource(mediaSources, deviceCapabilities)
end function
end namespace