Members
(static, readonly) JellyfinLanguage
JellyfinLanguage: ISO 639-2 three-letter language codes used by Jellyfin Only includes languages supported by Roku OS locales
| Name | Type | Description |
|---|---|---|
ENGLISH | ||
SPANISH | ||
PORTUGUESE | ||
FRENCH | ||
GERMAN | ||
ITALIAN |
Methods
(static) applyLiveDirectPlayFallback(video, meta) → {void}
applyLiveDirectPlayFallback: Flags that a transcode retry is available for live streams attempting direct play. Live direct play may fail on Roku if the server overestimates compatibility. VideoPlayerView observes isTranscodeAvailable to retry with transcoding on error.
| Name | Type | Description |
|---|---|---|
video | object | Video object to update |
meta | object | Video metadata containing the live stream flag |
- Type:
- void
(static) findBestAudioStreamIndex(streams, playDefault, preferredLanguage, deviceCapabilitiesopt) → {integer}
findBestAudioStreamIndex: Primary function for selecting the best audio stream
Selection priority when playDefault = true (Jellyfin: "Play default audio track regardless of language"):
- Filter by IsDefault = true streams (ignore language preference)
- If multiple IsDefault streams, use language preference as tiebreaker
- If still multiple or no language match, apply hardware optimization
- If no IsDefault streams exist, fall back to language preference → hardware optimization
Selection priority when playDefault = false:
- Filter by language preference (completely ignore IsDefault flag)
- If multiple language matches, apply hardware optimization
- If no language matches, apply hardware optimization to all streams
Roku OS Language Fallback: When preferredLanguage is blank/empty, automatically uses the Roku device's OS language (from m.global.device.locale) as fallback for better out-of-box experience (see issue #179)
Hardware optimization:
- Prefer streams matching device's max channel capability
- Among matches, prefer direct-playable codecs
- Fall back intelligently based on channel counts
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
streams | dynamic | Array of media streams from Jellyfin metadata | ||
playDefault | dynamic | Boolean, if true use IsDefault flag and only use language as tiebreaker | ||
preferredLanguage | dynamic | Three-letter language code (e.g., "eng", "jpn") | ||
deviceCapabilities | dynamic | <optional> | invalid | Optional: Device audio capabilities (for testing). If invalid, will detect automatically. |
- Jellyfin index of best audio stream, or 0 if not found
- Type:
- integer
(static) findBestVideoSource(mediaSources, deviceCapabilitiesopt) → {integer}
findBestVideoSource: Selects the optimal MediaSource for the device's capabilities
Scoring algorithm:
- Resolution fit (primary): Prefer highest resolution at or below device max. Sources exceeding device max receive a penalty (will likely transcode).
- HDR match (secondary): If device supports HDR, prefer HDR sources. If device does NOT support HDR, prefer SDR (avoids tone-mapping transcode).
- Direct play (tertiary): Prefer sources the server says can direct play.
- Bitrate (tiebreaker): Higher bitrate wins among otherwise-equal sources.
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
mediaSources | object | Array of MediaSource objects from Jellyfin API | ||
deviceCapabilities | dynamic | <optional> | invalid | Optional device capabilities (for testing) |
- Array index of the best MediaSource (0-based)
- Type:
- integer
(static) findDefaultSubtitleStreamIndex(mediaStreams, selectedAudioStreamIndex) → {integer}
findDefaultSubtitleStreamIndex: Determine which subtitle stream will be auto-selected during playback Mirrors the selection logic in defaultSubtitleTrackFromVid() and sortSubtitles() (Subtitles.bs) for use on the render thread without requiring playback-specific dependencies (buildURL, etc.)
Selection logic (by subtitleMode): "none" → No subtitles "default" → First forced or default subtitle matching preferred language "always" → First non-forced subtitle matching preferred language; fallback to forced/default "onlyforced"→ First forced subtitle matching preferred language "smart" → First non-forced subtitle when audio language differs from preference; fallback to forced/default
Priority ordering: forced > default > normal; preferred language first within each category Text subtitles are tried first; non-text only used when playbackSubsOnlyText is false
| Name | Type | Description |
|---|---|---|
mediaStreams | object | Array of MediaStream objects from Jellyfin API (all types: video, audio, subtitle) |
selectedAudioStreamIndex | integer | Server-side index of the selected audio stream (for Smart mode) |
- Server-side stream index of the selected subtitle, or -1 if none
- Type:
- integer
(static) findDirectPlayableStreamByChannelCount(audioStreams, targetChannels, deviceCapabilities) → {dynamic}
findDirectPlayableStreamByChannelCount: Finds a direct-playable stream with specific channel count
| Name | Type | Description |
|---|---|---|
audioStreams | object | Array of audio streams |
targetChannels | integer | Desired channel count (2, 6, or 8) |
deviceCapabilities | object | Device capability info |
- Matching stream or invalid
- Type:
- dynamic
(static) getDeviceAudioCapabilities() → {object}
getDeviceAudioCapabilities: Detects device audio codec and channel support
Strategy:
- Use combined check (no PassThru) for 6-channel and below (safe, checks both)
- ALWAYS verify 8-channel with PassThru: 1 (combined check can lie)
- Roku max native decode is 6 channels (varies by model)
- 8-channel requires HDMI passthrough to receiver/soundbar
maxChannels (integer: 2, 6, or 8) - Maximum supported audio channels supports8Channel (boolean) - True if HDMI passthrough supports 8-channel audio
- AssocArray with:
- Type:
- object
(static) getDeviceVideoCapabilities(mockCapabilitiesopt) → {object}
getDeviceVideoCapabilities: Detects device video resolution and HDR support
Strategy:
- Use cached m.global.device values for resolution (render-thread safe, no object creation)
- Use canPlay4k() for 4K eligibility (HDCP 2.2 + HEVC decode check)
- Use roDeviceInfo.GetDisplayProperties() for HDR format detection
maxHeight (integer) - Maximum display height in pixels maxWidth (integer) - Maximum display width in pixels canDecode4k (boolean) - True if device can play 4K content supportsHdr10 (boolean) - True if display supports HDR10 supportsDolbyVision (boolean) - True if display supports Dolby Vision supportsHlg (boolean) - True if display supports HLG
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
mockCapabilities | dynamic | <optional> | invalid | Optional mock for unit testing (must have isMock=true) |
- AssocArray with:
- Type:
- object
(static) getTranscodeReasons(url) → {object}
getTranscodeReasons: Extracts the transcode reason codes from a Jellyfin transcoding URL. The server embeds reasons as a comma-separated TranscodeReasons query parameter. Used to detect VideoLevelNotSupported so a direct-play fallback can be attempted.
| Name | Type | Description |
|---|---|---|
url | string | The transcoding URL containing TranscodeReasons |
Array of reason strings, or empty array if param not present
- Type:
- object
(static) isCommentaryAudioStream(stream) → {boolean}
isCommentaryAudioStream: Determine whether a stream is a commentary/secondary track.
Detection: case-insensitive substring match for "commentary" in the stream Title. Used by findBestAudioStreamIndex() to deprioritize commentary tracks during auto-selection so a director's-commentary stereo track doesn't outrank the main multichannel track on a stereo device (issue #500).
| Name | Type | Description |
|---|---|---|
stream | object | MediaStream object from Jellyfin metadata |
- true if the stream looks like a commentary track
- Type:
- boolean
(static) isSelectedAudioStreamDirectPlayable(jellyfinAudioIndex, audioStreams) → {boolean}
isSelectedAudioStreamDirectPlayable: Whether the audio stream at jellyfinAudioIndex inside audioStreams is decodable by the current device.
Used by mid-playback audio switching to decide whether Roku's native track-switch is viable, or whether a server reload is required (e.g. switching back to an 8ch track on a stereo device — Roku can't decode it natively, so a transcode reload is needed).
| Name | Type | Description |
|---|---|---|
jellyfinAudioIndex | integer | Jellyfin index of the chosen audio stream |
audioStreams | dynamic | Array of audio MediaStream objects (e.g. fullAudioData) |
- true if the chosen stream can be direct-played on this device
- Type:
- boolean
(static) isStreamDirectPlayable(stream, deviceCapabilities) → {boolean}
isStreamDirectPlayable: Checks if an audio stream can be directly played by the device
For 8-channel: MUST use PassThru: 1 (only HDMI passthrough, Roku can't decode 8-channel) For 6-channel and below: Use combined check (no PassThru) - checks both Roku and HDMI
| Name | Type | Description |
|---|---|---|
stream | object | Audio stream object with codec and channels fields |
deviceCapabilities | object | Device capability info |
- True if stream can be direct played
- Type:
- boolean
(static) mapRokuLocaleToJellyfinLanguage(rokuLocale) → {string}
mapRokuLocaleToJellyfinLanguage: Converts Roku locale codes to Jellyfin ISO 639-2 language codes
Maps Roku device locale (e.g., "en_US", "fr_CA") to Jellyfin's 3-letter language codes. Extracts base language from locale and maps to standard ISO 639-2 codes.
Supported Roku locales:
- en_US, en_GB, en_CA, en_AU → eng (English)
- es_ES, es_MX → spa (Spanish)
- pt_BR → por (Portuguese)
- fr_CA → fra (French)
- de_DE → deu (German)
- it_IT → ita (Italian)
| Name | Type | Description |
|---|---|---|
rokuLocale | dynamic | Roku locale string (e.g., "en_US", "fr_CA") |
- Jellyfin ISO 639-2 language code (e.g., "eng", "fra"), or empty string if not recognized
- Type:
- string
(static) resolveAudioLanguagePreference(userSettings, userConfig) → {string}
resolveAudioLanguagePreference: Resolves the audio language preference setting value
Checks JellyRock override setting first, then falls back to web client setting. Returns a 3-letter ISO 639-2 language code (e.g., "eng", "jpn") or empty string.
| Name | Type | Description |
|---|---|---|
userSettings | object | JellyfinUserSettings node (JellyRock settings) |
userConfig | object | JellyfinUserConfiguration node (web client settings) |
- Resolved audio language preference (3-letter code or empty string)
- Type:
- string
(static) resolvePlayDefaultAudioTrack(userSettings, userConfig) → {boolean}
resolvePlayDefaultAudioTrack: Resolves the playDefaultAudioTrack setting value
Checks JellyRock override setting first, then falls back to web client setting. Ensures a valid boolean is always returned.
| Name | Type | Description |
|---|---|---|
userSettings | object | JellyfinUserSettings node (JellyRock settings) |
userConfig | object | JellyfinUserConfiguration node (web client settings) |
- Resolved playDefaultAudioTrack value (guaranteed boolean)
- Type:
- boolean
(static) resolveSubtitleLanguagePreference(userSettings, userConfig) → {string}
resolveSubtitleLanguagePreference: Resolves the subtitle language preference setting value
Checks JellyRock override setting first, then falls back to web client setting. The "audio" option chains to resolveAudioLanguagePreference() so subtitle language follows the user's audio language override.
| Name | Type | Description |
|---|---|---|
userSettings | object | JellyfinUserSettings node (JellyRock settings) |
userConfig | object | JellyfinUserConfiguration node (web client settings) |
- Resolved subtitle language preference (3-letter code or empty string)
- Type:
- string
(static) selectBestStreamByHardware(audioStreams, deviceCapabilities) → {dynamic}
selectBestStreamByHardware: Selects the best audio stream based on device capabilities
Priority:
- Prefer streams matching device's max channel capability (for quality)
- Among matching streams, prefer direct-playable codecs
- Fall back intelligently based on channel counts
| Name | Type | Description |
|---|---|---|
audioStreams | object | Array of audio stream objects |
deviceCapabilities | object | Device capability info from getDeviceAudioCapabilities() |
- Best matching audio stream object, or invalid
- Type:
- dynamic
(static) selectSubtitleByMode(sortedEntries, subtitleMode, prefLang, selectedAudioLanguage, requireText) → {integer}
selectSubtitleByMode: Apply subtitle mode selection logic to sorted subtitle entries Mirrors defaultSubtitleTrack() in Subtitles.bs
| Name | Type | Description |
|---|---|---|
sortedEntries | object | Array of sorted subtitle entries from findDefaultSubtitleStreamIndex |
subtitleMode | string | Lowercase subtitle mode ("default", "always", "onlyforced", "smart") |
prefLang | string | Preferred subtitle language (ISO 639-2 code, e.g., "eng") |
selectedAudioLanguage | string | Language of the selected audio stream |
requireText | boolean | If true, only consider text subtitle entries |
- Server-side stream index of the selected subtitle, or -1 if none
- Type:
- integer