components_manager_ViewCreator.bs
' Play Audio
sub CreateAudioPlayerView()
m.view = CreateObject("roSGNode", "AudioPlayerView")
m.view.observeField("state", "onStateChange")
m.global.sceneManager.callFunc("pushScene", m.view)
end sub
' Play Video
sub CreateVideoPlayerView()
m.playbackData = {}
m.selectedSubtitle = {}
m.view = CreateObject("roSGNode", "VideoPlayerView")
m.view.observeField("state", "onStateChange")
m.view.observeField("selectPlaybackInfoPressed", "onSelectPlaybackInfoPressed")
m.view.observeField("selectSubtitlePressed", "onSelectSubtitlePressed")
m.view.observeField("selectAudioPressed", "onSelectAudioPressed")
mediaSourceId = m.global.queueManager.callFunc("getCurrentItem").mediaSourceId
if not isValid(mediaSourceId) or mediaSourceId = ""
mediaSourceId = m.global.queueManager.callFunc("getCurrentItem").id
end if
m.getPlaybackInfoTask = createObject("roSGNode", "GetPlaybackInfoTask")
m.getPlaybackInfoTask.videoID = mediaSourceId
m.getPlaybackInfoTask.observeField("data", "onPlaybackInfoLoaded")
m.global.sceneManager.callFunc("pushScene", m.view)
end sub
' -----------------
' Event Handlers
' -----------------
' onSelectAudioPressed: Display audio selection dialog
'
sub onSelectAudioPressed()
audioData = {
data: []
}
for each item in m.view.fullAudioData
audioStreamItem = {
"Index": item.Index,
"IsExternal": item.IsExternal,
"Track": {
"description": item.DisplayTitle
},
"Type": "audioselection"
}
if m.view.audioIndex = item.Index
audioStreamItem.selected = true
end if
audioData.data.push(audioStreamItem)
end for
m.global.sceneManager.callFunc("radioDialog", tr("Select Audio"), audioData)
m.global.sceneManager.observeField("returnData", "onSelectionMade")
end sub
' User requested subtitle selection popup
sub onSelectSubtitlePressed()
subtitleData = {
data: []
}
for each item in m.view.fullSubtitleData
item.type = "subtitleselection"
if m.view.selectedSubtitle <> -1
' Subtitle is a track within the file
if item.index = m.view.selectedSubtitle
item.selected = true
end if
else
' Subtitle is from an external source
availableSubtitleTrackIndex = availSubtitleTrackIdx(item.track.TrackName)
if availableSubtitleTrackIndex <> -1
' Convert Jellyfin subtitle track name to Roku track name
subtitleFullTrackName = m.view.availableSubtitleTracks[availableSubtitleTrackIndex].TrackName
if subtitleFullTrackName = m.view.subtitleTrack
item.selected = true
end if
end if
end if
' Put the selected item at the top of the option list
if isValid(item.selected) and item.selected
subtitleData.data.Unshift(item)
else
subtitleData.data.push(item)
end if
end for
' Manually create the None option and place at top
subtitleData.data.Unshift({
"Index": -1,
"IsExternal": false,
"Track": {
"description": "None"
},
"Type": "subtitleselection"
})
m.global.sceneManager.callFunc("radioDialog", tr("Select Subtitles"), subtitleData)
m.global.sceneManager.observeField("returnData", "onSelectionMade")
end sub
' User has selected something from the radioDialog popup
sub onSelectionMade()
m.global.sceneManager.unobserveField("returnData")
if not isValid(m.global.sceneManager.returnData) then return
if not isValid(m.global.sceneManager.returnData.type) then return
if LCase(m.global.sceneManager.returnData.type) = "subtitleselection"
processSubtitleSelection()
return
end if
if LCase(m.global.sceneManager.returnData.type) = "audioselection"
processAudioSelection()
return
end if
end sub
' processAudioSelection: Audio track selection handler
'
sub processAudioSelection()
selectedAudioTrack = m.global.sceneManager.returnData
if isValid(selectedAudioTrack)
if isValid(selectedAudioTrack.index)
m.view.audioIndex = selectedAudioTrack.index
end if
end if
end sub
sub processSubtitleSelection()
m.selectedSubtitle = m.global.sceneManager.returnData
' The selected encoded subtitle did not change.
if m.view.selectedSubtitle <> -1 or m.selectedSubtitle.index <> -1
if m.view.selectedSubtitle = m.selectedSubtitle.index then return
end if
' The playbackData is now outdated and must be refreshed
m.playbackData = invalid
' Find previously selected subtitle and identify if it was encoded
for each item in m.view.fullSubtitleData
if item.index = m.view.selectedSubtitle
m.view.previousSubtitleWasEncoded = item.IsEncoded
exit for
end if
end for
if LCase(m.selectedSubtitle.track.description) = "none"
m.view.globalCaptionMode = "Off"
m.view.subtitleTrack = ""
if m.view.selectedSubtitle <> -1
m.view.selectedSubtitle = -1
end if
return
end if
if m.selectedSubtitle.IsEncoded
' Roku can not natively display these subtitles, so turn off the caption mode on the device
m.view.globalCaptionMode = "Off"
else
' Roku can natively display these subtitles, ensure the caption mode on the device is on
m.view.globalCaptionMode = "On"
' Roku may rearrange subtitle tracks. Look up track based on name to ensure we get the correct index
availableSubtitleTrackIndex = availSubtitleTrackIdx(m.selectedSubtitle.Track.TrackName)
if availableSubtitleTrackIndex = -1 then return
m.view.subtitleTrack = m.view.availableSubtitleTracks[availableSubtitleTrackIndex].TrackName
end if
m.view.selectedSubtitle = m.selectedSubtitle.Index
end sub
' User requested playback info
sub onSelectPlaybackInfoPressed()
' Check if we already have playback info and show it in a popup
if isValid(m.playbackData) and isValid(m.playbackData.playbackinfo)
m.global.sceneManager.callFunc("standardDialog", tr("Playback Info"), m.playbackData.playbackinfo)
return
end if
m.getPlaybackInfoTask.control = "RUN"
end sub
' The playback info task has returned data
sub onPlaybackInfoLoaded()
m.playbackData = m.getPlaybackInfoTask.data
' Check if we have playback info and show it in a popup
if isValid(m.playbackData) and isValid(m.playbackData.playbackinfo)
m.global.sceneManager.callFunc("standardDialog", tr("Playback Info"), m.playbackData.playbackinfo)
end if
end sub
' Playback state change event handlers
sub onStateChange()
if LCase(m.view.state) = "finished"
' Close any open dialogs
if m.global.sceneManager.callFunc("isDialogOpen")
m.global.sceneManager.callFunc("dismissDialog")
end if
' If there is something next in the queue, play it
if m.global.queueManager.callFunc("getPosition") < m.global.queueManager.callFunc("getCount") - 1
m.global.sceneManager.callFunc("clearPreviousScene")
m.global.queueManager.callFunc("moveForward")
m.global.queueManager.callFunc("playQueue")
return
end if
' Playback completed, return user to previous screen
m.global.sceneManager.callFunc("popScene")
m.global.audioPlayer.loopMode = ""
end if
end sub
' Roku translates the info provided in subtitleTracks into availableSubtitleTracks
' Including ignoring tracks, if they are not understood, thus making indexing unpredictable.
' This function translates between our internel selected subtitle index
' and the corresponding index in availableSubtitleTracks.
function availSubtitleTrackIdx(tracknameToFind as string) as integer
idx = 0
for each availTrack in m.view.availableSubtitleTracks
' The TrackName must contain the URL we supplied originally, though
' Roku mangles the name a bit, so we check if the URL is a substring, rather
' than strict equality
if Instr(1, availTrack.TrackName, tracknameToFind)
return idx
end if
idx = idx + 1
end for
return -1
end function