HMS

Home Media Server for Roku Players
git clone https://www.brianlane.com/git/HMS
Log | Files | Refs | README | LICENSE

commit ba6bb1628819954419ea0d882b86ad5e1e2da02f
parent 5500447f6a455e0a1f7b8ffbb42f0e8f0ced7b6e
Author: Brian C. Lane <bcl@brianlane.com>
Date:   Sat, 19 Nov 2022 12:19:59 -0800

MainScene: Organize and document the functions

Diffstat:
MHMS/components/MainScene.brs | 264++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 168 insertions(+), 96 deletions(-)

diff --git a/HMS/components/MainScene.brs b/HMS/components/MainScene.brs @@ -10,6 +10,7 @@ sub Init() StartClock() + ' Get the server URL from the registry or a user dialog url = RegRead("ServerURL") if url = invalid then RunSetupServerDialog("") @@ -18,9 +19,17 @@ sub Init() RunValidateURLTask(url) end if + ' Setup the video player node SetupVideoPlayer() end sub + +'**************** +' Clock functions +'**************** + +' StartClock starts displaying the clock in the upper right of the screen +' It calls UpdateClock every 5 seconds sub StartClock() m.clock = m.top.FindNode("clock") m.clockTimer = m.top.FindNode("clockTimer") @@ -29,6 +38,7 @@ sub StartClock() UpdateClock() end sub +' Update the clock, showing HH:MM AM/PM in the upper right of the screen sub UpdateClock() now = CreateObject("roDateTime") now.ToLocalTime() @@ -54,6 +64,14 @@ sub UpdateClock() m.clock.text = now.GetWeekday()+" "+hour+":"+minutes+ampm end sub + +'****************** +' Content functions +'****************** +' +' RunContentTask is called when the server url has been set from the registry or +' entered by the user, and verified to be valid. +' It then starts the task to load the list of categories sub RunContentTask() print "MainScene->RunContentTask()" @@ -63,45 +81,20 @@ sub RunContentTask() m.contentTask.control = "run" end sub -sub GetKeystoreValue(key as string, callback as string) - m.keystoreTask.serverurl = m.top.serverurl - m.keystoreTask.key = key - m.keystoreTask.value = "" - m.keystoreTask.command = "get" - if callback <> "" - m.keystoreTask.ObserveField("done", callback) - end if - m.keystoreTask.control = "run" -end sub - -sub SetKeystoreValue(key as string, value as string, callback as string) - m.keystoreTask.serverurl = m.top.serverurl - m.keystoreTask.key = key - m.keystoreTask.value = value - m.keystoreTask.command = "set" - if callback <> "" - m.keystoreTask.ObserveField("done", callback) - end if - m.keystoreTask.control = "run" -end sub - -sub ResetKeystoreTask() - m.keystoreTask.UNObserveField("done") - m.keystoreTask.done = false -end sub - +' OnCategoriesLoaded is called when the list of categories has been recalled +' from the server. It is returned as a list of strings and is displayed on +' the left side of the screen. sub OnCategoriesLoaded() print "MainScene->OnCategoriesLoaded()" print m.contentTask.categories m.categories = m.contentTask.categories - ' Add these to the list on the left side of the screen... how? + ' Add these to the list on the left side of the screen m.panels = m.top.FindNode("panels") m.listPanel = m.panels.CreateChild("ListPanel") m.listPanel.observeField("createNextPanelIndex", "OnCreateNextPanelIndex") m.labelList = CreateObject("roSGNode", "LabelList") - m.labelList.observeField("focusedItem", "OnLabelListSelected") m.listPanel.list = m.labelList m.listPanel.appendChild(m.labelList) m.listPanel.SetFocus(true) @@ -115,6 +108,8 @@ sub OnCategoriesLoaded() m.labelList.content = ln end sub +' OnCreateNextPanelIndex is called when a new category is selected (up/down) +' It populates the poster grid on the right of the screen sub OnCreateNextPanelIndex() print "MainScene->OnCreateNextPanelIndex()" print m.listPanel.createNextPanelIndex @@ -123,6 +118,8 @@ sub OnCreateNextPanelIndex() RunCategoryLoadTask(m.categories[m.listPanel.createNextPanelIndex]) end sub +' RunCategoryLoadTask runs a task to get the metadata for the selected category +' It calls OnMetadataLoaded when it is done sub RunCategoryLoadTask(category as string) print "MainScene->RunCategoryLoadTask()" print category @@ -134,6 +131,9 @@ sub RunCategoryLoadTask(category as string) m.metadataTask.control = "run" end sub +' OnMetadataLoaded is called when it has retrieved the metadata for the category +' It creates one GridPanel and one PosterGrid then re-populates them with each +' new batch of metadata. sub OnMetadataLoaded() print "MainScene->OnMetadataLoaded()" m.metadata = m.metadataTask.metadata @@ -179,12 +179,46 @@ sub OnMetadataLoaded() m.posterGrid.content = cn end sub +' OnPosterSelected it called when OK is hit on the selected poster +' It starts the video player sub OnPosterSelected() print "MainScene->OnPosterSelected()" print m.posterGrid.itemSelected StartVideoPlayer(m.posterGrid.itemSelected) end sub +' OnPosterFocused updates the information at the top of the screen with the +' category name and the name of the selected video +sub OnPosterFocused() + print "MainScene->OnPosterFocused()" + print m.posterGrid.itemFocused + print m.metadata[m.posterGrid.itemFocused].ShortDescriptionLine1 + m.details.text = m.categories[m.listPanel.createNextPanelIndex] + " | " + m.metadata[m.posterGrid.itemFocused].ShortDescriptionLine1 +end sub + + +'*********************** +' Video player functions +'*********************** + +' SetupVideoPlayer sets up the observers for the video node +' and how often it will report the playback position +sub SetupVideoPlayer() + ' Setup the video player + m.video = m.top.FindNode("player") + m.video.observeField("state", "OnVideoStateChange") + m.video.observeField("position", "OnVideoPositionChange") + m.video.notificationInterval = 5 + ' map of events that should be handled on state change + m.statesToHandle = { + finished: "" + error: "" + } +end sub + +' StartVideoPlayer is called with the index of the video to play +' It runs a keystore task to retrieve the last playback position for the +' selected video and then calls StartPlayback sub StartVideoPlayer(index as integer) print "MainScene->StartVideoPlayer()" print m.metadata[index].ShortDescriptionLine1 @@ -194,8 +228,8 @@ sub StartVideoPlayer(index as integer) GetKeystoreValue(m.video.content.Title, "StartPlayback") end sub - -' Called by GetKeystoreValue +' StartPlayback is called by GetKeystoreValue which may have a starting +' position. If so, it is set, and playback is started. sub StartPlayback() print "MainScene->StartPlayback()" ResetKeystoreTask() @@ -210,71 +244,8 @@ sub StartPlayback() m.video.control = "play" end sub -sub OnPosterFocused() - print "MainScene->OnPosterFocused()" - print m.posterGrid.itemFocused - print m.metadata[m.posterGrid.itemFocused].ShortDescriptionLine1 - m.details.text = m.categories[m.listPanel.createNextPanelIndex] + " | " + m.metadata[m.posterGrid.itemFocused].ShortDescriptionLine1 -end sub - -sub OnLabelListSelected() - print "MainScene->OnLabelListSelected()" -end sub - -sub RunValidateURLTask(url as string) - print "MainScene->RunValidateURLTask()" - - m.validateTask = CreateObject("roSGNode", "ValidateURLTask") - m.validateTask.serverurl = url - m.validateTask.ObserveField("valid", "OnValidateChanged") - m.validateTask.control = "run" -end sub - -sub OnValidateChanged() - print "MainScene->OnValidateChanged" - print "server url = "; m.validateTask.serverurl - print "valid? "; m.validateTask.valid - print "keystore? "; m.validateTask.keystore - if not m.validateTask.valid then - ' Still invalid, run it again - RunSetupServerDialog(m.validateTask.serverurl) - else - ' Valid url, trigger the content load - m.top.serverurl = m.validateTask.serverurl - ' And save it for next time - RegWrite("ServerURL", m.validateTask.serverurl) - m.keystoreTask.has_keystore = m.validateTask.keystore - end if -end sub - -sub RunSetupServerDialog(url as string) - print "MainScene->RunSetupServerDialog()" - m.serverDialog = createObject("roSGNode", "SetupServerDialog") - m.serverDialog.ObserveField("serverurl", "OnSetupServerURL") - m.serverDialog.text = url - m.top.dialog = m.serverDialog -end sub - -sub OnSetupServerURL() - print "MainScene->OnSetupServerURL()" - print m.serverDialog.serverurl - - RunValidateURLTask(m.serverDialog.serverurl) -end sub - -sub SetupVideoPlayer() - ' Setup the video player - m.video = m.top.FindNode("player") - m.video.observeField("state", "OnVideoStateChange") - m.video.observeField("position", "OnVideoPositionChange") - m.video.notificationInterval = 5 - ' map of events that should be handled on state change - m.statesToHandle = { - finished: "" - error: "" - } -end sub - +' OnVideoStateChanged is called when the playback is finished or there is an error +' it will save the last playback position and close the video player sub OnVideoStateChange() print "MainScene->OnVideoStateChange()" ? "video state: " + m.video.state @@ -290,6 +261,8 @@ sub OnVideoStateChange() end if end sub +' CloseVideoPlayer coses the player and stops playback, returning focus to the +' poster grid. sub CloseVideoPlayer() print "MainScene->CloseVideoPlayer()" m.video.visible = false @@ -297,6 +270,8 @@ sub CloseVideoPlayer() m.posterGrid.SetFocus(true) end sub +' OnVideoPositionChange is called every 5 seconds and it sends the position +' to the keystore server sub OnVideoPositionChange() print "MainScene->OnVideoPositionChange()" if m.video.positionInfo = invalid @@ -306,6 +281,8 @@ sub OnVideoPositionChange() SetKeystoreValue(m.video.content.Title, m.video.positionInfo.video.ToStr(), "ResetKeystoreTask") end sub +' onKeyEvent handles hitting 'back' during playback and play when selecting a poster grid +' which normally doesn't start playback. function onKeyEvent(key as String, press as Boolean) as Boolean if press if key = "back" 'If the back button is pressed @@ -320,3 +297,98 @@ function onKeyEvent(key as String, press as Boolean) as Boolean end if end if end Function + + +'*********************** +' Server setup functions +'*********************** + +' RunSetupServerDialog runs the dialog prompting the user for the server url +sub RunSetupServerDialog(url as string) + print "MainScene->RunSetupServerDialog()" + m.serverDialog = createObject("roSGNode", "SetupServerDialog") + m.serverDialog.ObserveField("serverurl", "OnSetupServerURL") + m.serverDialog.text = url + m.top.dialog = m.serverDialog +end sub + +' OnSetupServerURL is called when the user has entered a url, it then validates it +' by calling RunValidateURLTask +sub OnSetupServerURL() + print "MainScene->OnSetupServerURL()" + print m.serverDialog.serverurl + + RunValidateURLTask(m.serverDialog.serverurl) +end sub + +' RunValidateURLTask is called to validate the url that the user entered in the dialog +' it starts a task and calls OnValidateChanged when done. +sub RunValidateURLTask(url as string) + print "MainScene->RunValidateURLTask()" + + m.validateTask = CreateObject("roSGNode", "ValidateURLTask") + m.validateTask.serverurl = url + m.validateTask.ObserveField("valid", "OnValidateChanged") + m.validateTask.control = "run" +end sub + +' OnValidateChanged checks the result of validating the URL and either runs the setup +' dialog again, or sets the serverurl which triggers loading the categories and the +' rest of the screen. +sub OnValidateChanged() + print "MainScene->OnValidateChanged" + print "server url = "; m.validateTask.serverurl + print "valid? "; m.validateTask.valid + print "keystore? "; m.validateTask.keystore + if not m.validateTask.valid then + ' Still invalid, run it again + RunSetupServerDialog(m.validateTask.serverurl) + else + ' Valid url, trigger the content load + m.top.serverurl = m.validateTask.serverurl + ' And save it for next time + RegWrite("ServerURL", m.validateTask.serverurl) + m.keystoreTask.has_keystore = m.validateTask.keystore + end if +end sub + + +' ****************** +' Keystore functions +' ****************** + +' GetKeystoreValue retrieves a string from the keystore server +' It calls the callback when it is done (or has failed) +' The callback needs to call ResetKeystoreTask to clear the +' done field. +sub GetKeystoreValue(key as string, callback as string) + m.keystoreTask.serverurl = m.top.serverurl + m.keystoreTask.key = key + m.keystoreTask.value = "" + m.keystoreTask.command = "get" + if callback <> "" + m.keystoreTask.ObserveField("done", callback) + end if + m.keystoreTask.control = "run" +end sub + +' SetKeystoreValue sets a key to a string on the keystore server +' It calls the callback when it is done (or has failed) +' The callback needs to call ResetKeystoreTask to clear the +' done field. +sub SetKeystoreValue(key as string, value as string, callback as string) + m.keystoreTask.serverurl = m.top.serverurl + m.keystoreTask.key = key + m.keystoreTask.value = value + m.keystoreTask.command = "set" + if callback <> "" + m.keystoreTask.ObserveField("done", callback) + end if + m.keystoreTask.control = "run" +end sub + +' ResetKeystoreTask clears the observer and sets done back to false +sub ResetKeystoreTask() + m.keystoreTask.UNObserveField("done") + m.keystoreTask.done = false +end sub