Update API docs
This commit is contained in:
parent
48c19eda75
commit
9e5bdd4979
File diff suppressed because one or more lines are too long
|
@ -9,6 +9,11 @@ import "pkg:/source/api/Image.bs"
|
|||
import "pkg:/source/api/userauth.bs"
|
||||
import "pkg:/source/utils/deviceCapabilities.bs"
|
||||
|
||||
enum SubtitleSelection
|
||||
notset = -2
|
||||
none = -1
|
||||
end enum
|
||||
|
||||
sub init()
|
||||
m.user = AboutMe()
|
||||
m.top.functionName = "loadItems"
|
||||
|
@ -46,19 +51,18 @@ sub loadItems()
|
|||
id = m.top.itemId
|
||||
mediaSourceId = invalid
|
||||
audio_stream_idx = m.top.selectedAudioStreamIndex
|
||||
subtitle_idx = m.top.selectedSubtitleIndex
|
||||
forceTranscoding = false
|
||||
|
||||
m.top.content = [LoadItems_VideoPlayer(id, mediaSourceId, audio_stream_idx, subtitle_idx, forceTranscoding)]
|
||||
m.top.content = [LoadItems_VideoPlayer(id, mediaSourceId, audio_stream_idx, forceTranscoding)]
|
||||
end sub
|
||||
|
||||
function LoadItems_VideoPlayer(id as string, mediaSourceId = invalid as dynamic, audio_stream_idx = 1 as integer, subtitle_idx = -1 as integer, forceTranscoding = false as boolean) as dynamic
|
||||
function LoadItems_VideoPlayer(id as string, mediaSourceId = invalid as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean) as dynamic
|
||||
|
||||
video = {}
|
||||
video.id = id
|
||||
video.content = createObject("RoSGNode", "ContentNode")
|
||||
|
||||
LoadItems_AddVideoContent(video, mediaSourceId, audio_stream_idx, subtitle_idx, forceTranscoding)
|
||||
LoadItems_AddVideoContent(video, mediaSourceId, audio_stream_idx, forceTranscoding)
|
||||
|
||||
if video.content = invalid
|
||||
return invalid
|
||||
|
@ -67,9 +71,10 @@ function LoadItems_VideoPlayer(id as string, mediaSourceId = invalid as dynamic,
|
|||
return video
|
||||
end function
|
||||
|
||||
sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, subtitle_idx = -1 as integer, forceTranscoding = false as boolean)
|
||||
sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean)
|
||||
|
||||
meta = ItemMetaData(video.id)
|
||||
subtitle_idx = m.top.selectedSubtitleIndex
|
||||
|
||||
if not isValid(meta)
|
||||
video.errorMsg = "Error loading metadata"
|
||||
|
@ -79,6 +84,21 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
|
|||
|
||||
videotype = LCase(meta.type)
|
||||
|
||||
' Check for any Live TV streams coming from other places other than the TV Guide
|
||||
if isValid(meta.json) and isValid(meta.json.ChannelId)
|
||||
if isValid(meta.json.EpisodeTitle)
|
||||
meta.title = meta.json.EpisodeTitle
|
||||
else if isValid(meta.json.Name)
|
||||
meta.title = meta.json.Name
|
||||
end if
|
||||
meta.live = true
|
||||
if LCase(meta.json.type) = "program"
|
||||
video.id = meta.json.ChannelId
|
||||
else
|
||||
video.id = meta.json.id
|
||||
end if
|
||||
end if
|
||||
|
||||
if videotype = "episode" or videotype = "series"
|
||||
video.content.contenttype = "episode"
|
||||
end if
|
||||
|
@ -109,16 +129,41 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
|
|||
if meta.live then mediaSourceId = ""
|
||||
|
||||
m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
|
||||
video.videoId = video.id
|
||||
video.mediaSourceId = mediaSourceId
|
||||
video.audioIndex = audio_stream_idx
|
||||
|
||||
if not isValid(m.playbackInfo)
|
||||
video.errorMsg = "Error loading playback info"
|
||||
video.content = invalid
|
||||
return
|
||||
end if
|
||||
|
||||
addSubtitlesToVideo(video, meta)
|
||||
|
||||
' Enable default subtitle track
|
||||
if subtitle_idx = SubtitleSelection.notset
|
||||
defaultSubtitleIndex = defaultSubtitleTrackFromVid(video.id)
|
||||
|
||||
if defaultSubtitleIndex <> SubtitleSelection.none
|
||||
video.SelectedSubtitle = defaultSubtitleIndex
|
||||
subtitle_idx = defaultSubtitleIndex
|
||||
|
||||
m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
|
||||
if not isValid(m.playbackInfo)
|
||||
video.errorMsg = "Error loading playback info"
|
||||
video.content = invalid
|
||||
return
|
||||
end if
|
||||
|
||||
addSubtitlesToVideo(video, meta)
|
||||
else
|
||||
video.SelectedSubtitle = subtitle_idx
|
||||
end if
|
||||
else
|
||||
video.SelectedSubtitle = subtitle_idx
|
||||
end if
|
||||
|
||||
video.videoId = video.id
|
||||
video.mediaSourceId = mediaSourceId
|
||||
video.audioIndex = audio_stream_idx
|
||||
|
||||
video.PlaySessionId = m.playbackInfo.PlaySessionId
|
||||
|
||||
if meta.live
|
||||
|
@ -132,8 +177,6 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
|
|||
m.playbackInfo = meta.json
|
||||
end if
|
||||
|
||||
addSubtitlesToVideo(video, meta)
|
||||
|
||||
if meta.live
|
||||
video.transcodeParams = {
|
||||
"MediaSourceId": m.playbackInfo.MediaSources[0].Id,
|
||||
|
@ -185,13 +228,106 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
|
|||
setCertificateAuthority(video.content)
|
||||
video.audioTrack = (audio_stream_idx + 1).ToStr() ' Roku's track indexes count from 1. Our index is zero based
|
||||
|
||||
video.SelectedSubtitle = subtitle_idx
|
||||
|
||||
if not fully_external
|
||||
video.content = authRequest(video.content)
|
||||
end if
|
||||
end sub
|
||||
|
||||
' defaultSubtitleTrackFromVid: Identifies the default subtitle track given video id
|
||||
'
|
||||
' @param {dynamic} videoID - id of video user is playing
|
||||
' @return {integer} indicating the default track's server-side index. Defaults to {SubtitleSelection.none} is one is not found
|
||||
function defaultSubtitleTrackFromVid(videoID) as integer
|
||||
if m.global.session.user.configuration.SubtitleMode = "None"
|
||||
return SubtitleSelection.none ' No subtitles desired: return none
|
||||
end if
|
||||
|
||||
meta = ItemMetaData(videoID)
|
||||
|
||||
if not isValid(meta) then return SubtitleSelection.none
|
||||
if not isValid(meta.json) then return SubtitleSelection.none
|
||||
if not isValidAndNotEmpty(meta.json.mediaSources) then return SubtitleSelection.none
|
||||
if not isValidAndNotEmpty(meta.json.MediaSources[0].MediaStreams) then return SubtitleSelection.none
|
||||
|
||||
subtitles = sortSubtitles(meta.id, meta.json.MediaSources[0].MediaStreams)
|
||||
selectedAudioLanguage = meta.json.MediaSources[0].MediaStreams[m.top.selectedAudioStreamIndex].Language ?? ""
|
||||
|
||||
defaultTextSubs = defaultSubtitleTrack(subtitles["all"], selectedAudioLanguage, true) ' Find correct subtitle track (forced text)
|
||||
if defaultTextSubs <> SubtitleSelection.none
|
||||
return defaultTextSubs
|
||||
end if
|
||||
|
||||
if not m.global.session.user.settings["playback.subs.onlytext"]
|
||||
return defaultSubtitleTrack(subtitles["all"], selectedAudioLanguage) ' if no appropriate text subs exist, allow non-text
|
||||
end if
|
||||
|
||||
return SubtitleSelection.none
|
||||
end function
|
||||
|
||||
' defaultSubtitleTrack:
|
||||
'
|
||||
' @param {dynamic} sortedSubtitles - array of subtitles sorted by type and language
|
||||
' @param {string} selectedAudioLanguage - language for selected audio track
|
||||
' @param {boolean} [requireText=false] - indicates if only text subtitles should be considered
|
||||
' @return {integer} indicating the default track's server-side index. Defaults to {SubtitleSelection.none} is one is not found
|
||||
function defaultSubtitleTrack(sortedSubtitles, selectedAudioLanguage as string, requireText = false as boolean) as integer
|
||||
userConfig = m.global.session.user.configuration
|
||||
|
||||
subtitleMode = isValid(userConfig.SubtitleMode) ? LCase(userConfig.SubtitleMode) : ""
|
||||
|
||||
allowSmartMode = false
|
||||
|
||||
' Only evaluate selected audio language if we have a value
|
||||
if selectedAudioLanguage <> ""
|
||||
allowSmartMode = selectedAudioLanguage <> userConfig.SubtitleLanguagePreference
|
||||
end if
|
||||
|
||||
for each item in sortedSubtitles
|
||||
' Only auto-select subtitle if language matches SubtitleLanguagePreference
|
||||
languageMatch = true
|
||||
if userConfig.SubtitleLanguagePreference <> ""
|
||||
languageMatch = (userConfig.SubtitleLanguagePreference = item.Track.Language)
|
||||
end if
|
||||
|
||||
' Ensure textuality of subtitle matches preferenced passed as arg
|
||||
matchTextReq = ((requireText and item.IsTextSubtitleStream) or not requireText)
|
||||
|
||||
if languageMatch and matchTextReq
|
||||
if subtitleMode = "default" and (item.isForced or item.IsDefault)
|
||||
' Return first forced or default subtitle track
|
||||
return item.Index
|
||||
else if subtitleMode = "always"
|
||||
' Return the first found subtitle track
|
||||
return item.Index
|
||||
else if subtitleMode = "onlyforced" and item.IsForced
|
||||
' Return first forced subtitle track
|
||||
return item.Index
|
||||
else if subtitleMode = "smart" and allowSmartMode
|
||||
' Return the first found subtitle track
|
||||
return item.Index
|
||||
end if
|
||||
end if
|
||||
end for
|
||||
|
||||
' User has chosed smart subtitle mode
|
||||
' We already attempted to load subtitles in preferred language, but none were found.
|
||||
' Fall back to default behaviour while ignoring preferredlanguage
|
||||
if subtitleMode = "smart" and allowSmartMode
|
||||
for each item in sortedSubtitles
|
||||
' Ensure textuality of subtitle matches preferenced passed as arg
|
||||
matchTextReq = ((requireText and item.IsTextSubtitleStream) or not requireText)
|
||||
if matchTextReq
|
||||
if item.isForced or item.IsDefault
|
||||
' Return first forced or default subtitle track
|
||||
return item.Index
|
||||
end if
|
||||
end if
|
||||
end for
|
||||
end if
|
||||
|
||||
return SubtitleSelection.none ' Keep current default behavior of "None", if no correct subtitle is identified
|
||||
end function
|
||||
|
||||
sub addVideoContentURL(video, mediaSourceId, audio_stream_idx, fully_external)
|
||||
protocol = LCase(m.playbackInfo.MediaSources[0].Protocol)
|
||||
if protocol <> "file"
|
||||
|
|
|
@ -142,14 +142,12 @@ sub loadInitialItems()
|
|||
m.loadItemsTask.itemId = m.top.parentItem.parentFolder
|
||||
else if LCase(m.view) = "artistspresentation" or LCase(m.options.view) = "artistspresentation"
|
||||
m.loadItemsTask.genreIds = ""
|
||||
m.top.showItemTitles = "hidealways"
|
||||
else if LCase(m.view) = "artistsgrid" or LCase(m.options.view) = "artistsgrid"
|
||||
m.loadItemsTask.genreIds = ""
|
||||
else if LCase(m.view) = "albumartistsgrid" or LCase(m.options.view) = "albumartistsgrid"
|
||||
m.loadItemsTask.genreIds = ""
|
||||
else if LCase(m.view) = "albumartistspresentation" or LCase(m.options.view) = "albumartistspresentation"
|
||||
m.loadItemsTask.genreIds = ""
|
||||
m.top.showItemTitles = "hidealways"
|
||||
else
|
||||
m.loadItemsTask.itemId = m.top.parentItem.Id
|
||||
end if
|
||||
|
|
|
@ -113,19 +113,20 @@ sub processSubtitleSelection()
|
|||
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"
|
||||
end if
|
||||
|
||||
if m.selectedSubtitle.IsExternal
|
||||
' 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
|
||||
else
|
||||
m.view.selectedSubtitle = m.selectedSubtitle.Index
|
||||
end if
|
||||
|
||||
m.view.selectedSubtitle = m.selectedSubtitle.Index
|
||||
end sub
|
||||
|
||||
' User requested playback info
|
||||
|
|
|
@ -95,6 +95,8 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
focusedItem = getFocusedItem()
|
||||
if isValid(focusedItem)
|
||||
m.top.selectedItem = focusedItem
|
||||
'Prevent the selected item event from double firing
|
||||
m.top.selectedItem = invalid
|
||||
end if
|
||||
return true
|
||||
end if
|
||||
|
|
|
@ -258,6 +258,9 @@ end sub
|
|||
|
||||
' Event handler for when selectedSubtitle changes
|
||||
sub onSubtitleChange()
|
||||
' If the global caption mode is on, that means Roku can display the subtitles natively and doesn't need a video stop/start
|
||||
if LCase(m.top.globalCaptionMode) = "on" then return
|
||||
|
||||
' Save the current video position
|
||||
m.global.queueManager.callFunc("setTopStartingPoint", int(m.top.position) * 10000000&)
|
||||
|
||||
|
@ -334,6 +337,29 @@ sub onVideoContentLoaded()
|
|||
m.top.allowCaptions = true
|
||||
end if
|
||||
|
||||
' Allow default subtitles
|
||||
m.top.unobserveField("selectedSubtitle")
|
||||
|
||||
' Set subtitleTrack property is subs are natively supported by Roku
|
||||
selectedSubtitle = invalid
|
||||
for each subtitle in m.top.fullSubtitleData
|
||||
if subtitle.Index = videoContent[0].selectedSubtitle
|
||||
selectedSubtitle = subtitle
|
||||
exit for
|
||||
end if
|
||||
end for
|
||||
|
||||
if isValid(selectedSubtitle)
|
||||
availableSubtitleTrackIndex = availSubtitleTrackIdx(selectedSubtitle.Track.TrackName)
|
||||
if availableSubtitleTrackIndex <> -1
|
||||
m.top.subtitleTrack = m.top.availableSubtitleTracks[availableSubtitleTrackIndex].TrackName
|
||||
end if
|
||||
end if
|
||||
|
||||
m.top.selectedSubtitle = videoContent[0].selectedSubtitle
|
||||
|
||||
m.top.observeField("selectedSubtitle", "onSubtitleChange")
|
||||
|
||||
if isValid(m.top.audioIndex)
|
||||
m.top.audioTrack = (m.top.audioIndex + 1).toStr()
|
||||
else
|
||||
|
@ -581,6 +607,25 @@ function stateAllowsOSD() as boolean
|
|||
return inArray(validStates, m.top.state)
|
||||
end function
|
||||
|
||||
|
||||
' availSubtitleTrackIdx: Returns Roku's index for requested subtitle track
|
||||
'
|
||||
' @param {string} tracknameToFind - TrackName for subtitle we're looking to match
|
||||
' @return {integer} indicating Roku's index for requested subtitle track. Returns -1 if not found
|
||||
function availSubtitleTrackIdx(tracknameToFind as string) as integer
|
||||
idx = 0
|
||||
for each availTrack in m.top.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
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
|
||||
' Keypress handler while user is inside the chapter menu
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -813,7 +813,7 @@ function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string) as
|
|||
group.objects = TVEpisodes(seriesID, seasonID)
|
||||
group.episodeObjects = group.objects
|
||||
' watch for button presses
|
||||
group.observeField("episodeSelected", m.port)
|
||||
group.observeField("selectedItem", m.port)
|
||||
group.observeField("quickPlayNode", m.port)
|
||||
' don't wait for the extras button
|
||||
stopLoadingSpinner()
|
||||
|
|
|
@ -73,11 +73,13 @@ sub runRegistryUserMigrations()
|
|||
' app versions < 2.0.0 didn't save LastRunVersion at the user level
|
||||
' fall back to using the apps lastRunVersion
|
||||
lastRunVersion = m.global.app.lastRunVersion
|
||||
registry_write("LastRunVersion", lastRunVersion, section)
|
||||
if isValid(lastRunVersion)
|
||||
registry_write("LastRunVersion", lastRunVersion, section)
|
||||
end if
|
||||
end if
|
||||
|
||||
' BASE_MIGRATION
|
||||
if not versionChecker(lastRunVersion, CLIENT_VERSION_REQUIRING_BASE_MIGRATION)
|
||||
if isValid(lastRunVersion) and not versionChecker(lastRunVersion, CLIENT_VERSION_REQUIRING_BASE_MIGRATION)
|
||||
m.wasMigrated = true
|
||||
print `Running Registry Migration for ${CLIENT_VERSION_REQUIRING_BASE_MIGRATION} for userid: ${section}`
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user