HMS

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

commit dd518fa5668c04e2564626982dacda779efdd690
parent d18c167600cd4772c556b464d4dd60ffa5eb9c05
Author: Brian C. Lane <bcl@ibrianlane.com>
Date:   Wed, 21 Nov 2018 14:27:26 -0800

Switch back to using roGridScreen

roPosterScreen loads quickly, but with 800+ movies the selection slows
way down. roGridScreen works quickly once everything is loaded, so in
this version it only loads a few rows at a time.

When the used stops scrolling at a new row it will load that row, the
next 2 rows, and the previous row. It now also displays a Loading ...
message in the upper right while it is busy.

If the system is idle for 30 seconds it will load one unloaded row every
30s until everything has been loaded.

Diffstat:
MHMS/source/appMain.brs | 4+++-
MHMS/source/appMediaServer.brs | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
MHMS/source/getDirectoryListing.brs | 29+++++++++++++++++------------
3 files changed, 182 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,147 @@ 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 + while true + msg = wait(30000, port) + if type(msg) = "roGridScreenEvent" then + 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) + + 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 +365,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 +471,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 ' ***********************************