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:
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