HMS

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

commit 476730f04baa0b92d177e9f52156ee4037ee6e14
parent b0941e10e853d1f37a7c4b175a64bb7bf02cdc12
Author: Brian C. Lane <bcl@ibrianlane.com>
Date:   Sun, 28 Dec 2014 07:12:26 -0800

Add support for using a keystore

This implements support for saving and recalling the last selected video
in a row and the last playback position to a remote keyserver. It must
be aliased to the movie server url + /keystore/<key> and values are set
by POST with value=<value>

Diffstat:
MHMS/source/appMain.brs | 5++++-
MHMS/source/appMediaServer.brs | 61++++++++++++++++++++++++++++++++++++++++++++++++-------------
MHMS/source/getDirectoryListing.brs | 23+++++++++++++++++++++++
MHMS/source/urlUtils.brs | 49++++++++++++++++++++++++++++++++++++++++++-------
4 files changed, 117 insertions(+), 21 deletions(-)

diff --git a/HMS/source/appMain.brs b/HMS/source/appMain.brs @@ -18,7 +18,10 @@ Sub Main() end if end while - mediaServer("http://"+RegRead("ServerURL")) + ' Check to see if the server supports keystore + has_keystore = isUrlValid("http://"+RegRead("ServerURL")+"/keystore/version") + + mediaServer("http://"+RegRead("ServerURL"), has_keystore) End Sub diff --git a/HMS/source/appMediaServer.brs b/HMS/source/appMediaServer.brs @@ -6,8 +6,9 @@ '****************************************************** '** Display a scrolling grid of everything on the server '****************************************************** -Function mediaServer( url As String ) As Object +Function mediaServer( url As String, has_keystore As Boolean ) As Object print "url: ";url + print "has_keystore: "; has_keystore port=CreateObject("roMessagePort") grid = CreateObject("roGridScreen") @@ -44,7 +45,6 @@ Function mediaServer( url As String ) As Object ' run the grid showTimeBreadcrumb(grid, true) - grid.SetFocusedListitem(0, 1) grid.Show() loadingDialog = ShowPleaseWait("Loading Movies...", "") @@ -81,13 +81,26 @@ Function mediaServer( url As String ) As Object list.Push(MovieObject(displayList[j], cat_url, listing_hash)) end for grid.SetContentList(1+i, list) + grid.SetFocusedListitem(1+i, 0) screen.Push(list) + if has_keystore = true then + ' Get the last selected video on this row + last_pos = getKeyValue(url, getLastElement(categories[i][0])) + if last_pos <> "" and last_pos.toint() < list.Count() then + grid.SetFocusedListitem(1+i, last_pos.toint()) + end if + end if + if displayList.Count() = 0 then + grid.SetListVisible(1+i, false) + end if else grid.SetContentList(1+i, []) screen.Push([]) + grid.SetListVisible(1+i, false) end if end for loadingDialog.Close() + grid.SetFocusedListitem(0, 1) while true msg = wait(30000, port) @@ -116,7 +129,17 @@ Function mediaServer( url As String ) As Object grid.SetContentList(0, searchRow) grid.SetFocusedListitem(0, 2) else - playMovie(screen[msg.GetIndex()][msg.GetData()]) + if has_keystore = true then + setKeyValue(url, getLastElement(categories[msg.GetIndex()-1][0]), tostr(msg.GetData())) + end if + result = playMovie(screen[msg.GetIndex()][msg.GetData()], url, has_keystore) + if result = true and msg.GetData() < screen[msg.GetIndex()].Count() then + ' Advance to the next video and save it + grid.SetFocusedListitem(msg.GetIndex(), msg.GetData()+1) + if has_keystore = true then + setKeyValue(url, getLastElement(categories[msg.GetIndex()-1][0]), tostr(msg.GetData()+1)) + end if + end if end if endif else if msg = invalid then @@ -249,12 +272,18 @@ End Function '************************************* '** Get the last position for the movie '************************************* -Function getLastPosition(movie As Object) As Integer +Function getLastPosition(title As String, url As String, has_keystore As Boolean) As Integer ' use movie.Title as the filename - lastPos = ReadAsciiFile("tmp:/"+movie.Title) - print "Last position of ";movie.Title;" is ";lastPos - if lastPos <> "" then - return strtoi(lastPos) + last_pos = ReadAsciiFile("tmp:/"+title) + if last_pos <> "" then + return last_pos.toint() + end if + ' No position stored on local filesystem, query keystore + if has_keystore = true then + last_pos = getKeyValue(url, title) + if last_pos <> "" then + return last_pos.toint() + end if end if return 0 End Function @@ -285,27 +314,31 @@ End Function '** Play the video using the data from the movie '** metadata object passed to it '****************************************************** -Sub playMovie(movie as Object) +Sub playMovie(movie As Object, url As String, has_keystore As Boolean) As Boolean p = CreateObject("roMessagePort") video = CreateObject("roVideoScreen") video.setMessagePort(p) video.SetPositionNotificationPeriod(15) - movie.PlayStart = getLastPosition(movie) + movie.PlayStart = getLastPosition(movie.Title, url, has_keystore) video.SetContent(movie) video.show() - lastPos = 0 + last_pos = 0 while true msg = wait(0, video.GetMessagePort()) if type(msg) = "roVideoScreenEvent" if msg.isScreenClosed() then 'ScreenClosed event exit while else if msg.isPlaybackPosition() then - lastPos = msg.GetIndex() - WriteAsciiFile("tmp:/"+movie.Title, tostr(lastPos)) + last_pos = msg.GetIndex() + WriteAsciiFile("tmp:/"+movie.Title, tostr(last_pos)) + if has_keystore = true then + setKeyValue(url, movie.Title, tostr(last_pos)) + end if else if msg.isfullresult() then DeleteFile("tmp:/"+movie.Title) + return true else if msg.isRequestFailed() then print "play failed: "; msg.GetMessage() else @@ -313,6 +346,8 @@ Sub playMovie(movie as Object) end if end if end while + + return false End Sub '****************************************************** diff --git a/HMS/source/getDirectoryListing.brs b/HMS/source/getDirectoryListing.brs @@ -44,3 +44,26 @@ Function getUrls(array as Object, element as Object) As Object return array End Function +' *********************************** +' * Get a value for a key +' *********************************** +Function getKeyValue(url As String, key As String) As String + result = getHTMLWithTimeout(url+"/keystore/"+key, 60) + if result.error and result.response <> 404 then + print "Error ";result.response;" getting key ";key;": ";result.reason + return "" + elseif result.error and result.response = 404 then + return "" + end if + return result.str +End Function + +' *********************************** +' * Set a value for a key +' *********************************** +Function setKeyValue(url As String, key As String, value As String) + result = postHTMLWithTimeout(url+"/keystore/"+key, "value="+value, 60) + if result.error then + print "Error ";result.response;" setting key ";key;"=";value;": ";result.reason + end if +End Function diff --git a/HMS/source/urlUtils.brs b/HMS/source/urlUtils.brs @@ -47,7 +47,7 @@ Function CreateURLTransferObject2(url As String, contentHeader As String) as Obj obj = CreateObject("roUrlTransfer") obj.SetPort(CreateObject("roMessagePort")) obj.SetUrl(url) - obj.AddHeader("Content-Type", contentHeader) + obj.AddHeader("Content-Type", contentHeader) obj.EnableEncodings(true) return obj End Function @@ -144,7 +144,7 @@ Function http_get_to_string_with_retry() as String event = wait(timeout%, m.Http.GetPort()) if type(event) = "roUrlEvent" str = event.GetString() - exit while + exit while elseif event = invalid m.Http.AsyncCancel() REM reset the connection on timeouts @@ -199,14 +199,14 @@ Function http_post_from_string_with_timeout(val As String, seconds as Integer) a if (m.Http.AsyncPostFromString(val)) event = wait(timeout%, m.Http.GetPort()) if type(event) = "roUrlEvent" - print "1" - str = event.GetString() + print "1" + str = event.GetString() elseif event = invalid - print "2" + print "2" Dbg("AsyncPostFromString timeout") m.Http.AsyncCancel() else - print "3" + print "3" Dbg("AsyncPostFromString unknown event", event) endif endif @@ -238,7 +238,7 @@ Function getHTMLWithTimeout(url As String, seconds As Integer) as Object end if elseif event = invalid Dbg("AsyncGetToString timeout") - htp.AsyncCancel() + http.AsyncCancel() else Dbg("AsyncGetToString unknown event", event) endif @@ -250,3 +250,38 @@ Function getHTMLWithTimeout(url As String, seconds As Integer) as Object return result End Function +' ******************************************************************** +' POST a value to a URL and get the response +' ******************************************************************** +Function postHTMLWithTimeout(url As String, content As String, seconds As Integer) as Object + timeout% = 1000 * seconds + + result = { str : "", error : false, response : 0, reason : "" } + + http = CreateObject("roUrlTransfer") + http.SetUrl(url) + http.SetPort(CreateObject("roMessagePort")) + http.EnableFreshConnection(true) 'Don't reuse existing connections + if (http.AsyncPostFromString(content)) + event = wait(timeout%, http.GetPort()) + if type(event) = "roUrlEvent" + if event.GetResponseCode() = 200 then + result.str = event.GetString() + else + result.error = true + result.response = event.GetResponseCode() + result.reason = event.GetFailureReason() + end if + elseif event = invalid + Dbg("AsyncGetToString timeout") + http.AsyncCancel() + else + Dbg("AsyncGetToString unknown event", event) + endif + endif + +' print "HTTP result: " +' print result + + return result +End Function