HMS

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

commit 0264f5922039a13188ec51cc4084e10c1815cb5c
parent d18c167600cd4772c556b464d4dd60ffa5eb9c05
Author: Brian C. Lane <bcl@users.noreply.github.com>
Date:   Sat,  1 Jan 2022 15:05:33 -0800

Merge pull request #8 from bcl/master-load-on-demand

roGridScreen load on demand
Diffstat:
MHMS/source/appMain.brs | 4+++-
MHMS/source/appMediaServer.brs | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
MHMS/source/getDirectoryListing.brs | 29+++++++++++++++++------------
3 files changed, 188 insertions(+), 17 deletions(-)

diff --git a/HMS/source/appMain.brs b/HMS/source/appMain.brs @@ -21,7 +21,9 @@ Sub Main() ' Check to see if the server supports keystore has_keystore = isUrlValid("http://"+RegRead("ServerURL")+"/keystore/version") - mediaServer("http://"+RegRead("ServerURL"), has_keystore) + roGridMediaServer("http://"+RegRead("ServerURL"), has_keystore) +' TODO NEED TO FIX SETUP +' roPosterMediaServer("http://"+RegRead("ServerURL"), has_keystore) End Sub diff --git a/HMS/source/appMediaServer.brs b/HMS/source/appMediaServer.brs @@ -6,7 +6,7 @@ '****************************************************** '** Display a scrolling grid of everything on the server '****************************************************** -Function mediaServer( url As String, has_keystore As Boolean ) As Object +Function roPosterMediaServer( url As String, has_keystore As Boolean ) As Object print "url: ";url print "has_keystore: "; has_keystore @@ -104,6 +104,153 @@ Function mediaServer( url As String, has_keystore As Boolean ) As Object end while End Function +'****************************************************** +'** Display a roGridScreen of the available media +'****************************************************** +Function roGridMediaServer( url As String, has_keystore As Boolean ) As Object + print "url: ";url + print "has_keystore: "; has_keystore + + port=CreateObject("roMessagePort") + grid = CreateObject("roGridScreen") + grid.SetMessagePort(port) + grid.SetDisplayMode("scale-to-fit") + grid.SetGridStyle("flat-movie") + + categories = getSortedCategoryTitles(url) + print categories + grid.SetupLists(categories.Count()) + grid.SetListNames(categories) + ' Keep track of which rows have been populated + populated_rows = CreateObject("roArray", categories.Count(), false) + for i = 0 to categories.Count()-1 + populated_rows[i] = false + end for + + ' Add a utility row (just setup for now) + utilityRow = getSetupRow(url) + grid.SetContentList(0, utilityRow) + + showTimeBreadcrumb(grid, true) + grid.Show() + + grid.SetFocusedListItem(1, 0) + + ' Hold all the movie objects + screen = CreateObject("roArray", categories.Count(), false) + screen[0] = "unused" + + populated_row = 1 + play_focused = -1 + focus_row = -1 + focus_col = 0 + last_row = -1 + idle_timeout = 30000 + while true + msg = wait(idle_timeout, port) + if type(msg) = "roGridScreenEvent" then + idle_timeout = 30000 + if msg.isRemoteKeyPressed() and msg.getIndex() = 13 then + wasPlayPressed = true + else + wasPlayPressed = false + end if + + if msg.isScreenClosed() then + return -1 + elseif msg.isListItemFocused() then + focus_row = msg.getIndex() + focus_col = msg.getData() + + print "row, col = "; focus_row, focus_col + if focus_row <> last_row then + last_row = focus_row + + ' Here is where we start fetching things for the next/previous rows and columns + ' focus can skip intermediate rows, need to fill them all in. + for each i in [focus_row, focus_row+1, focus_row+2, focus_row-1] + if i > 0 and i < populated_rows.Count() and populated_rows[i] = false then + grid.SetBreadcrumbText("Loading " + categories[i], "") + metadata = getCategoryMetadata(url, categories[i]) + screen[i] = metadata + grid.setContentList(i, metadata) + populated_rows[i] = true + last_col = getFocusedItem(url, has_keystore, categories[i], metadata.Count()) + grid.SetListOffset(i, last_col) + end if + end for + showTimeBreadcrumb(grid, true) + end if + elseif msg.isListItemSelected() or wasPlayPressed then + if focus_row = 0 and focus_col = 0 then + checkServerUrl(true) + else + print "row, col = "; focus_row, focus_col + if has_keystore = true then + setKeyValue(url, categories[focus_row], tostr(focus_col)) + end if + result = playMovie(screen[focus_row][focus_col], url, has_keystore) + if result = true and focus_col < screen[focus_row].Count() then + ' Advance to the next video and save it + grid.SetFocusedListitem(focus_row, focus_col+1) + if has_keystore = true then + setKeyValue(url, categories[focus_row], tostr(focus_col+1)) + end if + end if + end if + else + print msg + endif + else if msg = invalid then + showTimeBreadcrumb(grid, true) + + ' If the screen has been idle for 30 seconds go and load an un-populated row + for i = 1 to populated_rows.Count() + if populated_rows[i] = false then + print "Idle, populating "; categories[i] + grid.SetBreadcrumbText("Loading " + categories[i], "") + metadata = getCategoryMetadata(url, categories[i]) + screen[i] = metadata + grid.setContentList(i, metadata) + populated_rows[i] = true + showTimeBreadcrumb(grid, true) + last_col = getFocusedItem(url, has_keystore, categories[i], metadata.Count()) + grid.SetListOffset(i, last_col) + + ' Speed up loading while it remains idle + if idle_timeout > 5000 then + idle_timeout = idle_timeout - 5000 + end if + exit for + end if + end for + end if + end while +End Function + +'************************************* +'** Get the utility row (Setup, Search) +'************************************* +Function getUtilRow(url As String) As Object + ' Setup the Search/Setup entries for first row + search = CreateObject("roArray", 2, true) + o = CreateObject("roAssociativeArray") + o.ContentType = "episode" + o.Title = "Setup" + o.SDPosterUrl = url+"/Setup-SD.png" + o.HDPosterUrl = url+"/Setup-HD.png" + search.Push(o) + + o = CreateObject("roAssociativeArray") + o.ContentType = "episode" + o.Title = "Search" + o.SDPosterUrl = url+"/Search-SD.png" + o.HDPosterUrl = url+"/Search-HD.png" + search.Push(o) + + return search +End Function + '************************************* '** Get the Setup row @@ -224,7 +371,7 @@ Function showTimeBreadcrumb(screen As Object, use_ampm As Boolean) else minutes = tostr(minutes) end if - bc = now.AsDateStringNoParam()+" "+hour+":"+minutes+ampm + bc = now.AsDateString("short-month-short-weekday")+" "+hour+":"+minutes+ampm screen.SetBreadcrumbText(bc, "") End Function @@ -330,21 +477,38 @@ End Function '****************************************************** ' Return a roArray of just the category names +' include the Setup row as the first entry '****************************************************** Function catTitles(categories As Object) As Object titles = CreateObject("roArray", categories.Count()+1, false) + titles.Push("Setup") for i = 0 to categories.Count()-1 titles.Push(getLastElement(categories[i][0])) end for - titles.Push("Setup") return titles End Function +'****************************************************** +'** Get a sorted roArray of category titles +'****************************************************** +Function getSortedCategoryTitles(url as String) As Object + ' Build list of Category Names from the top level directories + listing = getDirectoryListing(url) + if listing = invalid then + return invalid + end if + categories = displayFiles(listing, {}, true) + Sort(categories, function(k) + return LCase(k[0]) + end function) + return catTitles(categories) +End Function + '******************************************************************* ' Return a roArray of roAssociativeArrays for the selected category '******************************************************************* Function getCategoryMetadata(url As String, category As String) As Object - cat_url = url + "/" + category + cat_url = url + "/" + category + "/" listing = getDirectoryListing(cat_url) listing_hash = CreateObject("roAssociativeArray") for each f in listing diff --git a/HMS/source/getDirectoryListing.brs b/HMS/source/getDirectoryListing.brs @@ -5,7 +5,7 @@ Function getDirectoryListing(url As String) As Object result = getHTMLWithTimeout(url, 60) - if result.error then + if result.error or result.str = invalid then title = "Directory Listing Error" text = "There was an error fetching the directory listing." print text @@ -14,19 +14,24 @@ Function getDirectoryListing(url As String) As Object return invalid end if - ' Split it into lines, assume one entry per-line - r1 = CreateObject("roRegex", "\n", "") - ' Extract the href entry from the line - r2 = CreateObject("roRegex", "href=.(.*?).>", "") + ' NOTE: I can't find a way to escape a " character with Instr so we have to ASSume it's there. dir = CreateObject("roArray", 10, true) - for each l in r1.Split(result.str) - if r2.isMatch(l) then - m = r2.Match(l) -' print m[1] - dir.Push(m[1]) + next_href = 0 + next_quote = -1 + while true + next_href = result.str.Instr(next_href, "href=") + if next_href = -1 then + return dir + end if + next_href = next_href + 6 + next_quote = result.str.Instr(next_href, ">") + if next_quote = -1 then + return dir end if - end for - return dir + next_quote = next_quote - 1 + dir.Push(result.str.Mid(next_href, next_quote-next_href)) + next_href = next_quote + 2 + end while End Function ' ***********************************