51c629ced1
Fix login bugs, enable support for saving user's credentials, and add "Change User" and "Remember Me?" options
721 lines
34 KiB
Plaintext
721 lines
34 KiB
Plaintext
sub Main (args as dynamic) as void
|
|
' The main function that runs when the application is launched.
|
|
m.screen = CreateObject("roSGScreen")
|
|
' Set global constants
|
|
setConstants()
|
|
' Write screen tracker for screensaver
|
|
WriteAsciiFile("tmp:/scene.temp", "")
|
|
MoveFile("tmp:/scene.temp", "tmp:/scene")
|
|
|
|
m.port = CreateObject("roMessagePort")
|
|
m.screen.setMessagePort(m.port)
|
|
' Set any initial Global Variables
|
|
m.global = m.screen.getGlobalNode()
|
|
SaveAppToGlobal()
|
|
SaveDeviceToGlobal()
|
|
session.Init()
|
|
|
|
m.scene = m.screen.CreateScene("JFScene")
|
|
m.screen.show() ' vscode_rale_tracker_entry
|
|
|
|
playstateTask = CreateObject("roSGNode", "PlaystateTask")
|
|
playstateTask.id = "playstateTask"
|
|
|
|
sceneManager = CreateObject("roSGNode", "SceneManager")
|
|
sceneManager.observeField("dataReturned", m.port)
|
|
|
|
m.global.addFields({ app_loaded: false, playstateTask: playstateTask, sceneManager: sceneManager })
|
|
m.global.addFields({ queueManager: CreateObject("roSGNode", "QueueManager") })
|
|
m.global.addFields({ audioPlayer: CreateObject("roSGNode", "AudioPlayer") })
|
|
|
|
app_start:
|
|
' First thing to do is validate the ability to use the API
|
|
if not LoginFlow() then return
|
|
' tell jellyfin server about device capabilities
|
|
PostDeviceProfile()
|
|
' remove previous scenes from the stack
|
|
sceneManager.callFunc("clearScenes")
|
|
|
|
' load home page
|
|
sceneManager.currentUser = m.global.session.user.name
|
|
group = CreateHomeGroup()
|
|
group.callFunc("loadLibraries")
|
|
sceneManager.callFunc("pushScene", group)
|
|
|
|
m.scene.observeField("exit", m.port)
|
|
|
|
' Downloads and stores a fallback font to tmp:/
|
|
configEncoding = api.system.GetConfigurationByName("encoding")
|
|
|
|
if isValid(configEncoding) and isValid(configEncoding.EnableFallbackFont)
|
|
if configEncoding.EnableFallbackFont
|
|
re = CreateObject("roRegex", "Name.:.(.*?).,.Size", "s")
|
|
filename = APIRequest("FallbackFont/Fonts").GetToString()
|
|
if isValid(filename)
|
|
filename = re.match(filename)
|
|
if isValid(filename) and filename.count() > 0
|
|
filename = filename[1]
|
|
APIRequest("FallbackFont/Fonts/" + filename).gettofile("tmp:/font")
|
|
end if
|
|
end if
|
|
end if
|
|
end if
|
|
|
|
' Only show the Whats New popup the first time a user runs a new client version.
|
|
appLastRunVersion = get_setting("LastRunVersion")
|
|
if m.global.app.version <> appLastRunVersion
|
|
' Ensure the user hasn't disabled Whats New popups
|
|
if m.global.session.user.settings["load.allowwhatsnew"] = true
|
|
set_setting("LastRunVersion", m.global.app.version)
|
|
dialog = createObject("roSGNode", "WhatsNewDialog")
|
|
m.scene.dialog = dialog
|
|
m.scene.dialog.observeField("buttonSelected", m.port)
|
|
end if
|
|
end if
|
|
|
|
' Registry migrations
|
|
if isValid(appLastRunVersion) and not versionChecker(appLastRunVersion, "1.7.0")
|
|
' last app version used less than 1.7.0
|
|
' no longer saving raw password to registry
|
|
' auth token and username are now stored in user settings and not global settings
|
|
print "Running 1.7.0 registry migrations"
|
|
' remove global settings
|
|
unset_setting("token")
|
|
unset_setting("username")
|
|
unset_setting("password")
|
|
' remove user settings
|
|
unset_user_setting("password")
|
|
' remove saved credentials from saved_servers
|
|
saved = get_setting("saved_servers")
|
|
if isValid(saved)
|
|
savedServers = ParseJson(saved)
|
|
if isValid(savedServers.serverList) and savedServers.serverList.Count() > 0
|
|
newServers = { serverList: [] }
|
|
for each item in savedServers.serverList
|
|
item.Delete("username")
|
|
item.Delete("password")
|
|
newServers.serverList.Push(item)
|
|
end for
|
|
set_setting("saved_servers", FormatJson(newServers))
|
|
end if
|
|
end if
|
|
end if
|
|
|
|
' Handle input messages
|
|
input = CreateObject("roInput")
|
|
input.SetMessagePort(m.port)
|
|
|
|
device = CreateObject("roDeviceInfo")
|
|
device.setMessagePort(m.port)
|
|
device.EnableScreensaverExitedEvent(true)
|
|
device.EnableAppFocusEvent(true)
|
|
device.EnableLowGeneralMemoryEvent(true)
|
|
device.EnableLinkStatusEvent(true)
|
|
device.EnableCodecCapChangedEvent(true)
|
|
device.EnableAudioGuideChangedEvent(true)
|
|
|
|
' Check if we were sent content to play with the startup command (Deep Link)
|
|
if isValidAndNotEmpty(args.mediaType) and isValidAndNotEmpty(args.contentId)
|
|
|
|
deepLinkVideo = {
|
|
id: args.contentId,
|
|
type: "video"
|
|
}
|
|
|
|
m.global.queueManager.callFunc("push", deepLinkVideo)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
end if
|
|
|
|
' This is the core logic loop. Mostly for transitioning between scenes
|
|
' This now only references m. fields so could be placed anywhere, in theory
|
|
' "group" is always "whats on the screen"
|
|
' m.scene's children is the "previous view" stack
|
|
while true
|
|
msg = wait(0, m.port)
|
|
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed()
|
|
print "CLOSING SCREEN"
|
|
return
|
|
else if isNodeEvent(msg, "exit")
|
|
return
|
|
else if isNodeEvent(msg, "closeSidePanel")
|
|
group = sceneManager.callFunc("getActiveScene")
|
|
if group.lastFocus <> invalid
|
|
group.lastFocus.setFocus(true)
|
|
else
|
|
group.setFocus(true)
|
|
end if
|
|
else if isNodeEvent(msg, "quickPlayNode")
|
|
group = sceneManager.callFunc("getActiveScene")
|
|
reportingNode = msg.getRoSGNode()
|
|
itemNode = reportingNode.quickPlayNode
|
|
if isValid(itemNode) and isValid(itemNode.id) and itemNode.id <> ""
|
|
if itemNode.type = "Episode" or itemNode.type = "Movie" or itemNode.type = "Video"
|
|
if isValid(itemNode.selectedVideoStreamId)
|
|
itemNode.id = itemNode.selectedVideoStreamId
|
|
end if
|
|
|
|
audio_stream_idx = 0
|
|
if isValid(itemNode.selectedAudioStreamIndex) and itemNode.selectedAudioStreamIndex > 0
|
|
audio_stream_idx = itemNode.selectedAudioStreamIndex
|
|
end if
|
|
|
|
itemNode.selectedAudioStreamIndex = audio_stream_idx
|
|
|
|
playbackPosition = 0
|
|
|
|
' Display playback options dialog
|
|
if isValid(itemNode.json) and isValid(itemNode.json.userdata) and isValid(itemNode.json.userdata.PlaybackPositionTicks)
|
|
playbackPosition = itemNode.json.userdata.PlaybackPositionTicks
|
|
end if
|
|
|
|
if playbackPosition > 0
|
|
m.global.queueManager.callFunc("hold", itemNode)
|
|
playbackOptionDialog(playbackPosition, itemNode.json)
|
|
else
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("push", itemNode)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
end if
|
|
|
|
' Prevent quick play node from double firing
|
|
reportingNode.quickPlayNode = invalid
|
|
|
|
if LCase(group.subtype()) = "tvepisodes"
|
|
if isValid(group.lastFocus)
|
|
group.lastFocus.setFocus(true)
|
|
end if
|
|
end if
|
|
end if
|
|
end if
|
|
else if isNodeEvent(msg, "selectedItem")
|
|
' If you select a library from ANYWHERE, follow this flow
|
|
selectedItem = msg.getData()
|
|
if isValid(selectedItem)
|
|
selectedItemType = selectedItem.type
|
|
|
|
|
|
if selectedItemType = "CollectionFolder"
|
|
if selectedItem.collectionType = "movies"
|
|
group = CreateMovieLibraryView(selectedItem)
|
|
else if selectedItem.collectionType = "music"
|
|
group = CreateMusicLibraryView(selectedItem)
|
|
else
|
|
group = CreateItemGrid(selectedItem)
|
|
end if
|
|
sceneManager.callFunc("pushScene", group)
|
|
else if selectedItemType = "Folder" and selectedItem.json.type = "Genre"
|
|
' User clicked on a genre folder
|
|
if selectedItem.json.MovieCount > 0
|
|
group = CreateMovieLibraryView(selectedItem)
|
|
else
|
|
group = CreateItemGrid(selectedItem)
|
|
end if
|
|
sceneManager.callFunc("pushScene", group)
|
|
else if selectedItemType = "Folder" and selectedItem.json.type = "MusicGenre"
|
|
group = CreateMusicLibraryView(selectedItem)
|
|
sceneManager.callFunc("pushScene", group)
|
|
else if selectedItemType = "UserView" or selectedItemType = "Folder" or selectedItemType = "Channel" or selectedItemType = "Boxset"
|
|
group = CreateItemGrid(selectedItem)
|
|
sceneManager.callFunc("pushScene", group)
|
|
else if selectedItemType = "Episode"
|
|
' User has selected a TV episode they want us to play
|
|
audio_stream_idx = 0
|
|
if isValid(selectedItem.selectedAudioStreamIndex) and selectedItem.selectedAudioStreamIndex > 0
|
|
audio_stream_idx = selectedItem.selectedAudioStreamIndex
|
|
end if
|
|
|
|
selectedItem.selectedAudioStreamIndex = audio_stream_idx
|
|
|
|
' Display playback options dialog
|
|
if selectedItem.json.userdata.PlaybackPositionTicks > 0
|
|
m.global.queueManager.callFunc("hold", selectedItem)
|
|
playbackOptionDialog(selectedItem.json.userdata.PlaybackPositionTicks, selectedItem.json)
|
|
else
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("push", selectedItem)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
end if
|
|
|
|
else if selectedItemType = "Series"
|
|
group = CreateSeriesDetailsGroup(selectedItem.json.id)
|
|
else if selectedItemType = "Season"
|
|
group = CreateSeasonDetailsGroupByID(selectedItem.json.SeriesId, selectedItem.id)
|
|
else if selectedItemType = "Movie"
|
|
' open movie detail page
|
|
group = CreateMovieDetailsGroup(selectedItem)
|
|
else if selectedItemType = "Person"
|
|
CreatePersonView(selectedItem)
|
|
else if selectedItemType = "TvChannel" or selectedItemType = "Video" or selectedItemType = "Program"
|
|
' User selected a Live TV channel / program
|
|
|
|
' Show Channel Loading spinner
|
|
dialog = createObject("roSGNode", "ProgressDialog")
|
|
dialog.title = tr("Loading Channel Data")
|
|
m.scene.dialog = dialog
|
|
|
|
' User selected a program. Play the channel the program is on
|
|
if LCase(selectedItemType) = "program"
|
|
selectedItem.id = selectedItem.json.ChannelId
|
|
end if
|
|
|
|
' Display playback options dialog
|
|
if selectedItem.json.userdata.PlaybackPositionTicks > 0
|
|
dialog.close = true
|
|
m.global.queueManager.callFunc("hold", selectedItem)
|
|
playbackOptionDialog(selectedItem.json.userdata.PlaybackPositionTicks, selectedItem.json)
|
|
else
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("push", selectedItem)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
dialog.close = true
|
|
end if
|
|
|
|
else if selectedItemType = "Photo"
|
|
' Nothing to do here, handled in ItemGrid
|
|
else if selectedItemType = "MusicArtist"
|
|
group = CreateArtistView(selectedItem.json)
|
|
if not isValid(group)
|
|
message_dialog(tr("Unable to find any albums or songs belonging to this artist"))
|
|
end if
|
|
else if selectedItemType = "MusicAlbum"
|
|
group = CreateAlbumView(selectedItem.json)
|
|
else if selectedItemType = "Playlist"
|
|
group = CreatePlaylistView(selectedItem.json)
|
|
else if selectedItemType = "Audio"
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("push", selectedItem.json)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
else
|
|
' TODO - switch on more node types
|
|
message_dialog("This type is not yet supported: " + selectedItemType + ".")
|
|
end if
|
|
end if
|
|
else if isNodeEvent(msg, "movieSelected")
|
|
' If you select a movie from ANYWHERE, follow this flow
|
|
node = getMsgPicker(msg, "picker")
|
|
group = CreateMovieDetailsGroup(node)
|
|
else if isNodeEvent(msg, "seriesSelected")
|
|
' If you select a TV Series from ANYWHERE, follow this flow
|
|
node = getMsgPicker(msg, "picker")
|
|
group = CreateSeriesDetailsGroup(node.id)
|
|
else if isNodeEvent(msg, "seasonSelected")
|
|
' If you select a TV Season from ANYWHERE, follow this flow
|
|
ptr = msg.getData()
|
|
' ptr is for [row, col] of selected item... but we only have 1 row
|
|
series = msg.getRoSGNode()
|
|
if isValid(ptr) and ptr.count() >= 2 and isValid(ptr[1]) and isValid(series) and isValid(series.seasonData) and isValid(series.seasonData.items)
|
|
node = series.seasonData.items[ptr[1]]
|
|
group = CreateSeasonDetailsGroup(series.itemContent, node)
|
|
end if
|
|
else if isNodeEvent(msg, "musicAlbumSelected")
|
|
' If you select a Music Album from ANYWHERE, follow this flow
|
|
ptr = msg.getData()
|
|
albums = msg.getRoSGNode()
|
|
node = albums.musicArtistAlbumData.items[ptr]
|
|
group = CreateAlbumView(node)
|
|
else if isNodeEvent(msg, "appearsOnSelected")
|
|
' If you select a Music Album from ANYWHERE, follow this flow
|
|
ptr = msg.getData()
|
|
albums = msg.getRoSGNode()
|
|
node = albums.musicArtistAppearsOnData.items[ptr]
|
|
group = CreateAlbumView(node)
|
|
else if isNodeEvent(msg, "playSong")
|
|
' User has selected audio they want us to play
|
|
selectedIndex = msg.getData()
|
|
screenContent = msg.getRoSGNode()
|
|
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("push", screenContent.albumData.items[selectedIndex])
|
|
m.global.queueManager.callFunc("playQueue")
|
|
else if isNodeEvent(msg, "playItem")
|
|
' User has selected audio they want us to play
|
|
selectedIndex = msg.getData()
|
|
screenContent = msg.getRoSGNode()
|
|
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("push", screenContent.albumData.items[selectedIndex])
|
|
m.global.queueManager.callFunc("playQueue")
|
|
else if isNodeEvent(msg, "playAllSelected")
|
|
' User has selected playlist of of audio they want us to play
|
|
screenContent = msg.getRoSGNode()
|
|
m.spinner = screenContent.findNode("spinner")
|
|
m.spinner.visible = true
|
|
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("set", screenContent.albumData.items)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
else if isNodeEvent(msg, "playArtistSelected")
|
|
' User has selected playlist of of audio they want us to play
|
|
screenContent = msg.getRoSGNode()
|
|
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("set", CreateArtistMix(screenContent.pageContent.id).Items)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
|
|
else if isNodeEvent(msg, "instantMixSelected")
|
|
' User has selected instant mix
|
|
' User has selected playlist of of audio they want us to play
|
|
screenContent = msg.getRoSGNode()
|
|
m.spinner = screenContent.findNode("spinner")
|
|
if isValid(m.spinner)
|
|
m.spinner.visible = true
|
|
end if
|
|
|
|
viewHandled = false
|
|
|
|
' Create instant mix based on selected album
|
|
if isValid(screenContent.albumData)
|
|
if isValid(screenContent.albumData.items)
|
|
if screenContent.albumData.items.count() > 0
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("set", CreateInstantMix(screenContent.albumData.items[0].id).Items)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
|
|
viewHandled = true
|
|
end if
|
|
end if
|
|
end if
|
|
|
|
if not viewHandled
|
|
' Create instant mix based on selected artist
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("set", CreateInstantMix(screenContent.pageContent.id).Items)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
end if
|
|
|
|
else if isNodeEvent(msg, "search_value")
|
|
query = msg.getRoSGNode().search_value
|
|
group.findNode("SearchBox").visible = false
|
|
options = group.findNode("SearchSelect")
|
|
options.visible = true
|
|
options.setFocus(true)
|
|
|
|
dialog = createObject("roSGNode", "ProgressDialog")
|
|
dialog.title = tr("Loading Search Data")
|
|
m.scene.dialog = dialog
|
|
results = SearchMedia(query)
|
|
dialog.close = true
|
|
options.itemData = results
|
|
options.query = query
|
|
else if isNodeEvent(msg, "itemSelected")
|
|
' Search item selected
|
|
node = getMsgPicker(msg)
|
|
' TODO - swap this based on target.mediatype
|
|
' types: [ Series (Show), Episode, Movie, Audio, Person, Studio, MusicArtist ]
|
|
if node.type = "Series"
|
|
group = CreateSeriesDetailsGroup(node.id)
|
|
else if node.type = "Movie"
|
|
group = CreateMovieDetailsGroup(node)
|
|
else if node.type = "MusicArtist"
|
|
group = CreateArtistView(node.json)
|
|
else if node.type = "MusicAlbum"
|
|
group = CreateAlbumView(node.json)
|
|
else if node.type = "Audio"
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("push", node.json)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
else if node.type = "Person"
|
|
group = CreatePersonView(node)
|
|
else if node.type = "TvChannel"
|
|
group = CreateVideoPlayerGroup(node.id)
|
|
sceneManager.callFunc("pushScene", group)
|
|
else if node.type = "Episode"
|
|
group = CreateVideoPlayerGroup(node.id)
|
|
sceneManager.callFunc("pushScene", group)
|
|
else if node.type = "Audio"
|
|
selectedIndex = msg.getData()
|
|
screenContent = msg.getRoSGNode()
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("resetShuffle")
|
|
m.global.queueManager.callFunc("push", screenContent.albumData.items[node.id])
|
|
m.global.queueManager.callFunc("playQueue")
|
|
else
|
|
' TODO - switch on more node types
|
|
message_dialog("This type is not yet supported: " + node.type + ".")
|
|
end if
|
|
else if isNodeEvent(msg, "buttonSelected")
|
|
' If a button is selected, we have some determining to do
|
|
btn = getButton(msg)
|
|
group = sceneManager.callFunc("getActiveScene")
|
|
if isValid(btn) and btn.id = "play-button"
|
|
' User chose Play button from movie detail view
|
|
|
|
' Check if a specific Audio Stream was selected
|
|
audio_stream_idx = 0
|
|
if isValid(group) and isValid(group.selectedAudioStreamIndex)
|
|
audio_stream_idx = group.selectedAudioStreamIndex
|
|
end if
|
|
|
|
group.itemContent.selectedAudioStreamIndex = audio_stream_idx
|
|
group.itemContent.id = group.selectedVideoStreamId
|
|
|
|
' Display playback options dialog
|
|
if group.itemContent.json.userdata.PlaybackPositionTicks > 0
|
|
m.global.queueManager.callFunc("hold", group.itemContent)
|
|
playbackOptionDialog(group.itemContent.json.userdata.PlaybackPositionTicks, group.itemContent.json)
|
|
else
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("push", group.itemContent)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
end if
|
|
|
|
if isValid(group) and isValid(group.lastFocus) and isValid(group.lastFocus.id) and group.lastFocus.id = "main_group"
|
|
buttons = group.findNode("buttons")
|
|
if isValid(buttons)
|
|
group.lastFocus = group.findNode("buttons")
|
|
end if
|
|
end if
|
|
|
|
if isValid(group) and isValid(group.lastFocus)
|
|
group.lastFocus.setFocus(true)
|
|
end if
|
|
|
|
else if btn <> invalid and btn.id = "trailer-button"
|
|
' User chose to play a trailer from the movie detail view
|
|
dialog = createObject("roSGNode", "ProgressDialog")
|
|
dialog.title = tr("Loading trailer")
|
|
m.scene.dialog = dialog
|
|
|
|
trailerData = api.users.GetLocalTrailers(m.global.session.user.id, group.id)
|
|
|
|
if isValid(trailerData) and isValid(trailerData[0]) and isValid(trailerData[0].id)
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("set", trailerData)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
dialog.close = true
|
|
end if
|
|
|
|
if isValid(group) and isValid(group.lastFocus)
|
|
group.lastFocus.setFocus(true)
|
|
end if
|
|
else if btn <> invalid and btn.id = "watched-button"
|
|
movie = group.itemContent
|
|
if isValid(movie) and isValid(movie.watched) and isValid(movie.id)
|
|
if movie.watched
|
|
UnmarkItemWatched(movie.id)
|
|
else
|
|
MarkItemWatched(movie.id)
|
|
end if
|
|
movie.watched = not movie.watched
|
|
end if
|
|
else if btn <> invalid and btn.id = "favorite-button"
|
|
movie = group.itemContent
|
|
if movie.favorite
|
|
UnmarkItemFavorite(movie.id)
|
|
else
|
|
MarkItemFavorite(movie.id)
|
|
end if
|
|
movie.favorite = not movie.favorite
|
|
else
|
|
' If there are no other button matches, check if this is a simple "OK" Dialog & Close if so
|
|
dialog = msg.getRoSGNode()
|
|
if dialog.id = "OKDialog"
|
|
dialog.unobserveField("buttonSelected")
|
|
dialog.close = true
|
|
end if
|
|
end if
|
|
else if isNodeEvent(msg, "optionSelected")
|
|
button = msg.getRoSGNode()
|
|
group = sceneManager.callFunc("getActiveScene")
|
|
if button.id = "goto_search" and isValid(group)
|
|
' Exit out of the side panel
|
|
panel = group.findNode("options")
|
|
panel.visible = false
|
|
if isValid(group.lastFocus)
|
|
group.lastFocus.setFocus(true)
|
|
else
|
|
group.setFocus(true)
|
|
end if
|
|
group = CreateSearchPage()
|
|
sceneManager.callFunc("pushScene", group)
|
|
group.findNode("SearchBox").findNode("search_Key").setFocus(true)
|
|
group.findNode("SearchBox").findNode("search_Key").active = true
|
|
else if button.id = "change_server"
|
|
unset_setting("server")
|
|
session.server.Delete()
|
|
SignOut(false)
|
|
sceneManager.callFunc("clearScenes")
|
|
goto app_start
|
|
else if button.id = "change_user"
|
|
SignOut(false)
|
|
sceneManager.callFunc("clearScenes")
|
|
goto app_start
|
|
else if button.id = "sign_out"
|
|
SignOut()
|
|
sceneManager.callFunc("clearScenes")
|
|
goto app_start
|
|
else if button.id = "settings"
|
|
' Exit out of the side panel
|
|
panel = group.findNode("options")
|
|
panel.visible = false
|
|
if isValid(group) and isValid(group.lastFocus)
|
|
group.lastFocus.setFocus(true)
|
|
else
|
|
group.setFocus(true)
|
|
end if
|
|
sceneManager.callFunc("settings")
|
|
end if
|
|
else if isNodeEvent(msg, "selectSubtitlePressed")
|
|
node = m.scene.focusedChild
|
|
if node.focusedChild <> invalid and node.focusedChild.isSubType("JFVideo")
|
|
trackSelected = selectSubtitleTrack(node.Subtitles, node.SelectedSubtitle)
|
|
if trackSelected <> invalid and trackSelected <> -2
|
|
changeSubtitleDuringPlayback(trackSelected)
|
|
end if
|
|
end if
|
|
else if isNodeEvent(msg, "selectPlaybackInfoPressed")
|
|
node = m.scene.focusedChild
|
|
if node.focusedChild <> invalid and node.focusedChild.isSubType("JFVideo")
|
|
info = GetPlaybackInfo()
|
|
show_dialog(tr("Playback Information"), info)
|
|
end if
|
|
else if isNodeEvent(msg, "state")
|
|
node = msg.getRoSGNode()
|
|
if isValid(node) and isValid(node.state)
|
|
if node.selectedItemType = "TvChannel" and node.state = "finished"
|
|
video = CreateVideoPlayerGroup(node.id)
|
|
m.global.sceneManager.callFunc("pushScene", video)
|
|
m.global.sceneManager.callFunc("deleteSceneAtIndex", 2)
|
|
else if node.state = "finished"
|
|
node.control = "stop"
|
|
|
|
' If node allows retrying using Transcode Url, give that shot
|
|
if isValid(node.retryWithTranscoding) and node.retryWithTranscoding
|
|
retryVideo = CreateVideoPlayerGroup(node.Id, invalid, node.audioIndex, true, false)
|
|
m.global.sceneManager.callFunc("popScene")
|
|
if isValid(retryVideo)
|
|
m.global.sceneManager.callFunc("pushScene", retryVideo)
|
|
end if
|
|
else if not isValid(node.showID)
|
|
sceneManager.callFunc("popScene")
|
|
else
|
|
autoPlayNextEpisode(node.id, node.showID)
|
|
end if
|
|
end if
|
|
end if
|
|
else if type(msg) = "roDeviceInfoEvent"
|
|
event = msg.GetInfo()
|
|
|
|
if event.exitedScreensaver = true
|
|
sceneManager.callFunc("resetTime")
|
|
group = sceneManager.callFunc("getActiveScene")
|
|
if isValid(group) and isValid(group.subtype())
|
|
' refresh the current view
|
|
if group.subtype() = "Home"
|
|
currentTime = CreateObject("roDateTime").AsSeconds()
|
|
group.timeLastRefresh = currentTime
|
|
group.callFunc("refresh")
|
|
end if
|
|
' todo: add other screens to be refreshed - movie detail, tv series, episode list etc.
|
|
end if
|
|
else if isValid(event.audioGuideEnabled)
|
|
tmpGlobalDevice = m.global.device
|
|
tmpGlobalDevice.AddReplace("isaudioguideenabled", event.audioGuideEnabled)
|
|
|
|
' update global device array
|
|
m.global.setFields({ device: tmpGlobalDevice })
|
|
else if isValid(event.Mode)
|
|
' Indicates the current global setting for the Caption Mode property, which may be one of the following values:
|
|
' "On"
|
|
' "Off"
|
|
' "Instant replay"
|
|
' "When mute" (Only returned for a TV; this option is not available on STBs).
|
|
print "event.Mode = ", event.Mode
|
|
if isValid(event.Mute)
|
|
print "event.Mute = ", event.Mute
|
|
end if
|
|
else if isValid(event.linkStatus)
|
|
' True if the device currently seems to have an active network connection.
|
|
print "event.linkStatus = ", event.linkStatus
|
|
else if isValid(event.generalMemoryLevel)
|
|
' This event will be sent first when the OS transitions from "normal" to "low" state and will continue to be sent while in "low" or "critical" states.
|
|
' - "normal" means that the general memory is within acceptable levels
|
|
' - "low" means that the general memory is below acceptable levels but not critical
|
|
' - "critical" means that general memory are at dangerously low level and that the OS may force terminate the application
|
|
print "event.generalMemoryLevel = ", event.generalMemoryLevel
|
|
else if isValid(event.audioCodecCapabilityChanged)
|
|
' The audio codec capability has changed if true.
|
|
print "event.audioCodecCapabilityChanged = ", event.audioCodecCapabilityChanged
|
|
|
|
PostDeviceProfile()
|
|
else if isValid(event.videoCodecCapabilityChanged)
|
|
' The video codec capability has changed if true.
|
|
print "event.videoCodecCapabilityChanged = ", event.videoCodecCapabilityChanged
|
|
|
|
PostDeviceProfile()
|
|
else if isValid(event.appFocus)
|
|
' It is set to False when the System Overlay (such as the confirm partner button HUD or the caption control overlay) takes focus and True when the channel regains focus
|
|
print "event.appFocus = ", event.appFocus
|
|
else
|
|
print "Unhandled roDeviceInfoEvent:"
|
|
print msg.GetInfo()
|
|
end if
|
|
else if type(msg) = "roInputEvent"
|
|
if msg.IsInput()
|
|
info = msg.GetInfo()
|
|
if info.DoesExist("mediatype") and info.DoesExist("contentid")
|
|
inputEventVideo = {
|
|
id: info.contentId,
|
|
type: "video"
|
|
}
|
|
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("push", inputEventVideo)
|
|
m.global.queueManager.callFunc("playQueue")
|
|
end if
|
|
end if
|
|
else if isNodeEvent(msg, "dataReturned")
|
|
popupNode = msg.getRoSGNode()
|
|
if isValid(popupNode) and isValid(popupNode.returnData)
|
|
selectedItem = m.global.queueManager.callFunc("getHold")
|
|
m.global.queueManager.callFunc("clearHold")
|
|
|
|
if isValid(selectedItem) and selectedItem.count() > 0 and isValid(selectedItem[0])
|
|
if popupNode.returnData.indexselected = 0
|
|
'Resume video from resume point
|
|
startingPoint = 0
|
|
|
|
if isValid(selectedItem[0].json) and isValid(selectedItem[0].json.UserData) and isValid(selectedItem[0].json.UserData.PlaybackPositionTicks)
|
|
if selectedItem[0].json.UserData.PlaybackPositionTicks > 0
|
|
startingPoint = selectedItem[0].json.UserData.PlaybackPositionTicks
|
|
end if
|
|
end if
|
|
|
|
selectedItem[0].startingPoint = startingPoint
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("push", selectedItem[0])
|
|
m.global.queueManager.callFunc("playQueue")
|
|
else if popupNode.returnData.indexselected = 1
|
|
'Start Over from beginning selected, set position to 0
|
|
selectedItem[0].startingPoint = 0
|
|
m.global.queueManager.callFunc("clear")
|
|
m.global.queueManager.callFunc("push", selectedItem[0])
|
|
m.global.queueManager.callFunc("playQueue")
|
|
else if popupNode.returnData.indexselected = 2
|
|
' User chose Go to series
|
|
CreateSeriesDetailsGroup(selectedItem[0].json.SeriesId)
|
|
else if popupNode.returnData.indexselected = 3
|
|
' User chose Go to season
|
|
CreateSeasonDetailsGroupByID(selectedItem[0].json.SeriesId, selectedItem[0].json.seasonID)
|
|
else if popupNode.returnData.indexselected = 4
|
|
' User chose Go to episode
|
|
CreateMovieDetailsGroup(selectedItem[0])
|
|
end if
|
|
end if
|
|
end if
|
|
else
|
|
print "Unhandled " type(msg)
|
|
print msg
|
|
end if
|
|
end while
|
|
|
|
end sub
|