Skip to content

Commit

Permalink
Subtitles: Auto-select default option
Browse files Browse the repository at this point in the history
On other players (web/andriod app) the user perferences for
subtitle behavior are taken into account, and used to make an
assumption about subtitle behavior.

This patch ports most of that logic here. "Smart" selection is not
yet fully-featured, as it requires additional knowledge about audio
language preferences. Rather it uses the fallback mechanism, which
emulates the "Default" subtitle option.

The logic for the different options was based on the main jellyfin
repo (specifically sha 49d5fdb33fc9792147c1df03e1d1b051e6b7ec79 in
file Emby.Server.Implementations/Library/MediaStreamSelector.cs)

Additionally, this implementation specifically prefers text-based
subtitles (assuming they match the user's preference) as they are
the only ones natively supported by Roku.

Also, the subtitle changing mechanism is reworked slightly to make
use of the new implementation herein
  • Loading branch information
cthelight committed May 21, 2022
1 parent f7652c2 commit 14451f0
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 18 deletions.
2 changes: 1 addition & 1 deletion source/ShowScenes.brs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ end sub

function CreateVideoPlayerGroup(video_id, mediaSourceId = invalid, audio_stream_idx = 1)
' Video is Playing
video = VideoPlayer(video_id, mediaSourceId, audio_stream_idx)
video = VideoPlayer(video_id, mediaSourceId, audio_stream_idx, defaultSubtitleTrackFromVid(video_id))
if video = invalid then return invalid
video.observeField("selectSubtitlePressed", m.port)
video.observeField("state", m.port)
Expand Down
5 changes: 4 additions & 1 deletion source/VideoPlayer.brs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
video.content.SubtitleTracks = subtitles["text"]

' 'TODO: allow user selection of subtitle track before playback initiated, for now set to no subtitles
video.SelectedSubtitle = -1

video.directPlaySupported = playbackInfo.MediaSources[0].SupportsDirectPlay
fully_external = false
Expand Down Expand Up @@ -235,6 +234,10 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
video.content.setCertificatesFile("common:/certs/ca-bundle.crt")
video.audioTrack = (audio_stream_idx + 1).ToStr() ' Roku's track indexes count from 1. Our index is zero based

' Perform relevant setup work for selected subtitle, and return the index of the subtitle
' is enabled/will be enabled, indexed on the provided list of subtitles
video.SelectedSubtitle = setupSubtitle(video, video.Subtitles, subtitle_idx)

if not fully_external
video.content = authorize_request(video.content)
end if
Expand Down
106 changes: 90 additions & 16 deletions source/utils/Subtitles.brs
Original file line number Diff line number Diff line change
@@ -1,3 +1,90 @@
' Identify the default subtitle track for a given video id
' returns the server-side track index for the appriate subtitle
function defaultSubtitleTrackFromVid(video_id) as integer
meta = ItemMetaData(video_id)
if meta = invalid then return invalid
subtitles = sortSubtitles(meta.id, meta.json.MediaSources[0].MediaStreams)
default_text_subs = defaultSubtitleTrack(subtitles["all"], true) ' Find correct subtitle track (forced text)
if default_text_subs <> -1
return default_text_subs
else
return defaultSubtitleTrack(subtitles["all"]) ' if no appropriate text subs exist, allow non-text
end if
end function


' Identify the default subtitle track
' if "requires_text" is true, only return a track if it is textual
' This allows forcing text subs, since roku requires transcoding of non-text subs
' returns the server-side track index for the appriate subtitle
function defaultSubtitleTrack(sorted_subtitles, require_text = false) as integer
if m.user.Configuration.SubtitleMode = "None"
return -1 ' No subtitles desired: select none
end if

for each item in sorted_subtitles
' Only auto-select subtitle if language matches preference
languageMatch = (m.user.Configuration.SubtitleLanguagePreference = item.Track.Language)
' Ensure textuality of subtitle matches preferenced passed as arg
matchTextReq = ((require_text and item.IsTextSubtitleStream) or not require_text)
if languageMatch and matchTextReq
if m.user.Configuration.SubtitleMode = "Default" and (item.isForced or item.IsDefault or item.IsExternal)
return item.Index ' Finds first forced, or default, or external subs in sorted list
else if m.user.Configuration.SubtitleMode = "Always" and not item.IsForced
return item.Index ' Select the first non-forced subtitle option in the sorted list
else if m.user.Configuration.SubtitleMode = "OnlyForced" and item.IsForced
return item.Index ' Select the first forced subtitle option in the sorted list
else if m.user.Configuration.SubtitlePlaybackMode = "Smart" and (item.isForced or item.IsDefault or item.IsExternal)
' Simplified "Smart" logic here mimics Default (as that is fallback behavior normally)
' Avoids detecting preferred audio language (as is utilized in main client)
return item.Index
end if
end if
end for
return -1 ' Keep current default behavior of "None", if no correct subtitle is identified
end function

' Given a set of subtitles, and a subtitle index (the index on the server, not in the list provided)
' this will set all relevant settings for roku (mainly closed captions) and return the index of the
' subtitle track specified, but indexed based on the provided list of subtitles
function setupSubtitle(video, subtitles, subtitle_idx = -1) as integer
if subtitle_idx = -1
' If we are not using text-based subtitles, turn them off
video.globalCaptionMode = "Off"
return -1
end if

' Translate the raw index to one relative to the provided list
subtitleSelIdx = getSubtitleSelIdxFromSubIdx(subtitles, subtitle_idx)

selectedSubtitle = subtitles[subtitleSelIdx]

if selectedSubtitle.IsEncoded
' With encoded subtitles, turn off captions
video.globalCaptionMode = "Off"
else
' If this is a text-based subtitle, set relevant settings for roku captions
video.globalCaptionMode = "On"
video.subtitleTrack = selectedSubtitle.Track.TrackName
end if

return subtitleSelIdx

end function

' The subtitle index on the server differs from the index we track locally
' This function converts the former into the latter
function getSubtitleSelIdxFromSubIdx(subtitles, sub_idx) as integer
selIdx = 0
if sub_idx = -1 then return -1
for each item in subtitles
if item.Index = sub_idx
return selIdx
end if
selIdx = selIdx + 1
end for
return -1
end function

function selectSubtitleTrack(tracks, current = -1) as integer
video = m.scene.focusedChild.focusedChild
Expand Down Expand Up @@ -45,26 +132,13 @@ sub changeSubtitleDuringPlayback(newid)
currentSubtitles = video.Subtitles[video.SelectedSubtitle]
newSubtitles = video.Subtitles[newid]

if newSubtitles.IsEncoded

' Switching to Encoded Subtitle stream
if newSubtitles.IsEncoded or (currentSubtitles <> invalid and currentSubtitles.IsEncoded)
' With encoded subtitles we need to stop/start playback
video.control = "stop"
AddVideoContent(video, video.mediaSourceId, video.audioIndex, newSubtitles.Index, video.position * 10000000)
video.control = "play"
video.globalCaptionMode = "Off" ' Using encoded subtitles - so turn off text subtitles

else if currentSubtitles <> invalid and currentSubtitles.IsEncoded

' Switching from an Encoded stream to a text stream
video.control = "stop"
AddVideoContent(video, video.mediaSourceId, video.audioIndex, -1, video.position * 10000000)
video.control = "play"
video.globalCaptionMode = "On"
video.subtitleTrack = video.availableSubtitleTracks[newSubtitles.TextIndex].TrackName

else

' Switch to Text Subtitle Track
' Switching from text to text (or none to text) does not require stopping playback
video.globalCaptionMode = "On"
video.subtitleTrack = video.availableSubtitleTracks[newSubtitles.TextIndex].TrackName
end if
Expand Down

0 comments on commit 14451f0

Please sign in to comment.