jf-roku/source/Main.brs

434 lines
14 KiB
Plaintext

sub Main()
' If the Rooibos files are included in deployment, run tests
if (type(Rooibos__Init) = "Function") then Rooibos__Init()
' The main function that runs when the application is launched.
m.screen = CreateObject("roSGScreen")
m.port = CreateObject("roMessagePort")
m.screen.setMessagePort(m.port)
m.scene = m.screen.CreateScene("JFScene")
m.screen.show()
m.overhang = CreateObject("roSGNode", "JFOverhang")
m.scene.insertChild(m.overhang, 0)
m.page_size = 50
app_start:
m.overhang.title = ""
' First thing to do is validate the ability to use the API
LoginFlow()
' load home page
m.overhang.title = "Home"
m.overhang.currentUser = m.user.Name
group = CreateHomeGroup()
m.scene.appendChild(group)
m.scene.observeField("backPressed", m.port)
m.scene.observeField("optionsPressed", m.port)
m.scene.observeField("mutePressed", m.port)
m.device = CreateObject("roDeviceInfo")
m.device.SetMessagePort(m.port)
' 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() then
print "CLOSING SCREEN"
return
else if isNodeEvent(msg, "backPressed")
if msg.getRoSGNode().focusedChild <> invalid and msg.getRoSGNode().focusedChild.isSubtype("JFVideo")
stopPlayback()
end if
' Pop a group off the stack and expose what's below
n = m.scene.getChildCount() - 1
if n = 1
' Overhang + last scene... this is the end
return
end if
m.scene.removeChildIndex(n)
prevOptionsAvailable = group.optionsAvailable
group = m.scene.getChild(n - 1)
m.overhang.title = group.overhangTitle
m.overhang.showOptions = group.optionsAvailable
if group.optionsAvailable <> prevOptionsAvailable then
if group.optionsAvailable = false then
m.scene.unobserveField("optionsPressed")
else
m.scene.observeField("optionsPressed", m.port)
end if
end if
m.overhang.visible = true
if group.lastFocus <> invalid
group.lastFocus.setFocus(true)
else
group.setFocus(true)
end if
group.visible = true
else if isNodeEvent(msg, "optionsPressed")
group.lastFocus = group.focusedChild
panel = group.findNode("options")
panel.visible = true
panel.findNode("panelList").setFocus(true)
else if isNodeEvent(msg, "closeSidePanel")
if group.lastFocus <> invalid
group.lastFocus.setFocus(true)
else
group.setFocus(true)
end if
else if isNodeEvent(msg, "selectedItem")
' If you select a library from ANYWHERE, follow this flow
selectedItem = msg.getData()
if selectedItem.type = "CollectionFolder" AND selectedItem.collectionType = "movies"
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = selectedItem.name
group = CreateMovieListGroup(selectedItem.Id)
group.overhangTitle = selectedItem.name
m.scene.appendChild(group)
else if selectedItem.type = "CollectionFolder" AND selectedItem.collectionType = "tvshows"
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = selectedItem.name
group = CreateSeriesListGroup(selectedItem.Id)
group.overhangTitle = selectedItem.name
m.scene.appendChild(group)
else if selectedItem.type = "CollectionFolder" AND selectedItem.collectionType = "boxsets"
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = selectedItem.name
group = CreateCollectionsList(selectedItem.Id)
group.overhangTitle = selectedItem.name
m.scene.appendChild(group)
else if selectedItem.type = "Episode" then
' play episode
' todo: create an episode page to link here
video_id = selectedItem.id
video = CreateVideoPlayerGroup(video_id)
if video <> invalid then
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
group = video
m.scene.appendChild(group)
group.setFocus(true)
group.control = "play"
ReportPlayback(group, "start")
m.overhang.visible = false
end if
else if selectedItem.type = "Series" then
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = selectedItem.title
m.overhang.showOptions = false
m.scene.unobserveField("optionsPressed")
group = CreateSeriesDetailsGroup(selectedItem.json)
group.overhangTitle = selectedItem.title
m.scene.appendChild(group)
else if selectedItem.type = "Movie" then
' open movie detail page
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = selectedItem.name
m.overhang.showOptions = false
m.scene.unobserveField("optionsPressed")
group = CreateMovieDetailsGroup(selectedItem)
group.overhangTitle = selectedItem.name
m.scene.appendChild(group)
else
' TODO - switch on more node types
if selectedItem.type = "CollectionFolder" then
message_dialog("This library type is not yet implemented: " + selectedItem.collectionType + ".")
else
message_dialog("This library type is not yet implemented: " + selectedItem.type + ".")
end if
selectedItem = invalid
end if
else if isNodeEvent(msg, "collectionSelected")
node = getMsgPicker(msg, "picker")
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = node.title
group = CreateMovieListGroup(node)
group.overhangTitle = node.title
m.scene.appendChild(group)
else if isNodeEvent(msg, "movieSelected")
' If you select a movie from ANYWHERE, follow this flow
node = getMsgPicker(msg, "picker")
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = node.title
m.overhang.showOptions = false
m.scene.unobserveField("optionsPressed")
group = CreateMovieDetailsGroup(node)
group.overhangTitle = node.title
m.scene.appendChild(group)
else if isNodeEvent(msg, "seriesSelected")
' If you select a TV Series from ANYWHERE, follow this flow
node = getMsgPicker(msg, "picker")
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = node.title
m.overhang.showOptions = false
m.scene.unobserveField("optionsPressed")
group = CreateSeriesDetailsGroup(node)
group.overhangTitle = node.title
m.scene.appendChild(group)
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()
node = series.seasonData.items[ptr[1]]
group.lastFocus = group.focusedChild.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = series.overhangTitle + " - " + node.title
m.overhang.showOptions = false
m.scene.unobserveField("optionsPressed")
group = CreateSeasonDetailsGroup(series.itemContent, node)
m.scene.appendChild(group)
else if isNodeEvent(msg, "episodeSelected")
' If you select a TV Episode from ANYWHERE, follow this flow
node = getMsgPicker(msg, "picker")
video_id = node.id
video = CreateVideoPlayerGroup(video_id)
if video <> invalid then
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
group = video
m.scene.appendChild(group)
group.setFocus(true)
group.control = "play"
ReportPlayback(group, "start")
m.overhang.visible = false
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)
results = SearchMedia(query)
options.itemData = results
options.query = query
else if isNodeEvent(msg, "pageSelected")
group.pageNumber = msg.getRoSGNode().pageSelected
collectionType = group.subType()
if collectionType = "Movies"
MovieLister(group, m.page_size)
else if collectionType = "Collections"
CollectionLister(group, m.page_size)
else if collectionType = "TVShows"
SeriesLister(group, m.page_size)
end if
' TODO - abstract away the "picker" node
group.findNode("picker").setFocus(true)
else if isNodeEvent(msg, "itemSelected")
' Search item selected
node = getMsgPicker(msg)
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
' TODO - swap this based on target.mediatype
' types: [ Episode, Movie, Audio, Person, Studio, MusicArtist ]
group = CreateMovieDetailsGroup(node)
m.scene.appendChild(group)
m.overhang.title = group.overhangTitle
else if isNodeEvent(msg, "buttonSelected")
' If a button is selected, we have some determining to do
btn = getButton(msg)
if btn.id = "play-button"
' TODO - Do a better job of picking the last focus
' This is currently page layout Group, button Group, then button
video_id = group.id
video = CreateVideoPlayerGroup(video_id)
if video <> invalid then
group.lastFocus = group.focusedChild.focusedChild.focusedChild
group.setFocus(false)
group.visible = false
group = video
m.scene.appendChild(group)
group.setFocus(true)
group.control = "play"
ReportPlayback(group, "start")
m.overhang.visible = false
end if
else if btn.id = "watched-button"
movie = group.itemContent
if movie.watched
UnmarkItemWatched(movie.id)
else
MarkItemWatched(movie.id)
end if
movie.watched = not movie.watched
else if btn.id = "favorite-button"
movie = group.itemContent
if movie.favorite
UnmarkItemFavorite(movie.id)
else
MarkItemFavorite(movie.id)
end if
movie.favorite = not movie.favorite
end if
else if isNodeEvent(msg, "optionSelected")
button = msg.getRoSGNode()
if button.id = "goto_search"
' Exit out of the side panel
panel.visible = false
if group.lastFocus <> invalid
group.lastFocus.setFocus(true)
else
group.setFocus(true)
end if
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
group = CreateSearchPage()
m.scene.appendChild(group)
m.overhang.title = group.overhangTitle
group.findNode("SearchBox").findNode("search-input").setFocus(true)
group.findNode("SearchBox").findNode("search-input").active = true
else if button.id = "change_server"
unset_setting("server")
unset_setting("port")
SignOut()
wipe_groups()
goto app_start
else if button.id = "sign_out"
SignOut()
wipe_groups()
goto app_start
else if button.id = "add_user"
unset_setting("active_user")
unset_setting("server")
unset_setting("port")
wipe_groups()
goto app_start
end if
else if isNodeEvent(msg, "position")
video = msg.getRoSGNode()
if video.position >= video.duration then
video.control = "stop"
video.state = "finished"
end if
else if isNodeEvent(msg, "fire")
ReportPlayback(group, "update")
else if isNodeEvent(msg, "state")
node = msg.getRoSGNode()
if node.state = "finished" then
node.backPressed = "true"
else if node.state = "playing" or node.state = "paused" then
ReportPlayback(group, "update")
end if
else if type(msg) = "roDeviceInfoEvent" then
event = msg.GetInfo()
if event.appFocused <> invalid then
child = m.scene.focusedChild
if child <> invalid and child.isSubType("JFVideo") then
child.systemOverlay = not event.appFocused
if event.AppFocused = true then
systemOverlayClosed()
end if
end if
else if event.Mute <> invalid then
m.mute = event.Mute
child = m.scene.focusedChild
if child <> invalid and child.isSubType("JFVideo") and child.systemOverlay = false then
'Event will be called on caption change which includes the current mute status, but we do not want to call until the overlay is closed
reviewSubtitleDisplay()
end if
else
print "Unhandled roDeviceInfoEvent:"
print msg.GetInfo()
end if
else
print type(msg)
print msg
end if
end while
end sub
sub LoginFlow()
'Collect Jellyfin server and user information
start_login:
if get_setting("server") = invalid or ServerInfo() = invalid then
print "Get server details"
CreateServerGroup()
end if
if get_setting("active_user") = invalid then
print "Get user login"
CreateSigninGroup()
end if
m.user = AboutMe()
if m.user = invalid or m.user.id <> get_setting("active_user")
print "Login failed, restart flow"
unset_setting("active_user")
goto start_login
end if
wipe_groups()
end sub
sub RunScreenSaver()
print "Starting screensaver..."
screen = createObject("roSGScreen")
m.port = createObject("roMessagePort")
screen.setMessagePort(m.port)
scene = screen.createScene("Screensaver")
screen.Show()
while(true)
msg = wait(8000, m.port)
if (msg <> invalid)
msgType = type(msg)
if msgType = "roSGScreenEvent"
if msg.isScreenClosed() then return
end if
end if
end while
end sub
sub wipe_groups()
' The 1 remaining child should be the overhang
while(m.scene.getChildCount() > 1)
m.scene.removeChildIndex(1)
end while
end sub