From 0f15d6983fc94eda153f3afbfd6e0351c18dea16 Mon Sep 17 00:00:00 2001 From: Nick Bisby Date: Mon, 4 Mar 2019 22:59:31 -0600 Subject: [PATCH] Some heavy restructuring --- components/LibItem.xml | 1 + components/VideoPlayer.xml | 13 --- source/JellyfinAPI.brs | 191 ++++++++++++++++++------------------- source/Main.brs | 87 ++++++++--------- source/VideoPlayer.brs | 16 ++-- 5 files changed, 136 insertions(+), 172 deletions(-) delete mode 100644 components/VideoPlayer.xml diff --git a/components/LibItem.xml b/components/LibItem.xml index 268d7504..5b81e2ec 100644 --- a/components/LibItem.xml +++ b/components/LibItem.xml @@ -15,6 +15,7 @@ end sub function itemContentChanged() as void + m.itemText = m.top.findNode("itemText") itemData = m.top.itemContent m.itemText.text = itemData.labelText end function diff --git a/components/VideoPlayer.xml b/components/VideoPlayer.xml deleted file mode 100644 index 1a732bb5..00000000 --- a/components/VideoPlayer.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/source/JellyfinAPI.brs b/source/JellyfinAPI.brs index 8813ab34..861f851a 100644 --- a/source/JellyfinAPI.brs +++ b/source/JellyfinAPI.brs @@ -1,42 +1,42 @@ ' Functions for making requests to the API function APIRequest(url as String, params={} as Object) - req = createObject("roUrlTransfer") + req = createObject("roUrlTransfer") - if server_is_https() then - req.setCertificatesFile("common:/certs/ca-bundle.crt") - end if + if server_is_https() then + req.setCertificatesFile("common:/certs/ca-bundle.crt") + end if - full_url = get_base_url() + "/emby/" + url - if params.count() > 0 - full_url = full_url + "?" + full_url = get_base_url() + "/emby/" + url + if params.count() > 0 + full_url = full_url + "?" - param_array = [] - for each field in params.items() - if type(field.value) = "String" then - item = field.key + "=" + req.escape(field.value.trim()) - else if type(field.value) = "roInteger" then - item = field.key + "=" + req.escape(str(field.value).trim()) - else - item = field.key + "=" + req.escape(field.value) - end if - param_array.push(item) - end for - full_url = full_url + param_array.join("&") - end if + param_array = [] + for each field in params.items() + if type(field.value) = "String" then + item = field.key + "=" + req.escape(field.value.trim()) + else if type(field.value) = "roInteger" then + item = field.key + "=" + req.escape(str(field.value).trim()) + else + item = field.key + "=" + req.escape(field.value) + end if + param_array.push(item) + end for + full_url = full_url + param_array.join("&") + end if - req.setUrl(full_url) + req.setUrl(full_url) - req = authorize_request(req) + req = authorize_request(req) - return req + return req end function function parseRequest(req) - 'req.retainBodyOnError(True) - 'print req.GetToString() - json = ParseJson(req.GetToString()) - return json + 'req.retainBodyOnError(True) + 'print req.GetToString() + json = ParseJson(req.GetToString()) + return json end function function get_base_url() @@ -49,70 +49,76 @@ function get_base_url() end function function server_is_https() as Boolean - server = get_setting("server") + server = get_setting("server") - i = server.Instr(":") + i = server.Instr(":") - ' No protocol found - if i = 0 then - return False - end if - - protocol = Left(server, i) - if protocol = "https" then - return True - end if + ' No protocol found + if i = 0 then return False + end if + + protocol = Left(server, i) + if protocol = "https" then + return True + end if + return False end function function get_token(user as String, password as String) - bytes = createObject("roByteArray") - bytes.FromAsciiString(password) - digest = createObject("roEVPDigest") - digest.setup("sha1") - hashed_pass = digest.process(bytes) + bytes = createObject("roByteArray") + bytes.FromAsciiString(password) + digest = createObject("roEVPDigest") + digest.setup("sha1") + hashed_pass = digest.process(bytes) - url = "Users/AuthenticateByName?format=json" + url = "Users/AuthenticateByName?format=json" - req = APIRequest(url) + req = APIRequest(url) - ' BrightScript will only return a POST body if you call post asynch - ' and then wait for the response - req.setMessagePort(CreateObject("roMessagePort")) - req.AsyncPostFromString("Username=" + user + "&Password=" + hashed_pass) - resp = wait(5000, req.GetMessagePort()) - if type(resp) <> "roUrlEvent" - return invalid - end if + ' BrightScript will only return a POST body if you call post asynch + ' and then wait for the response + req.setMessagePort(CreateObject("roMessagePort")) + req.AsyncPostFromString("Username=" + user + "&Password=" + hashed_pass) + resp = wait(5000, req.GetMessagePort()) + if type(resp) <> "roUrlEvent" + return invalid + end if - json = ParseJson(resp.GetString()) + json = ParseJson(resp.GetString()) - set_setting("active_user", json.User.id) - set_user_setting("id", json.User.id) ' redundant, but could come in handy - set_user_setting("token", json.AccessToken) - set_user_setting("response", json) - return json + set_setting("active_user", json.User.id) + set_user_setting("id", json.User.id) ' redundant, but could come in handy + set_user_setting("token", json.AccessToken) + set_user_setting("response", json) + return json end function function authorize_request(request) - auth = "MediaBrowser" - auth = auth + " Client=" + Chr(34) + "Jellyfin Roku" + Chr(34) - auth = auth + ", Device=" + Chr(34) + "Roku Model" + Chr(34) - auth = auth + ", DeviceId=" + Chr(34) + "12345" + Chr(34) - auth = auth + ", Version=" + Chr(34) + "10.1.0" + Chr(34) + auth = "MediaBrowser" + auth = auth + " Client=" + Chr(34) + "Jellyfin Roku" + Chr(34) + auth = auth + ", Device=" + Chr(34) + "Roku Model" + Chr(34) + auth = auth + ", DeviceId=" + Chr(34) + "12345" + Chr(34) + auth = auth + ", Version=" + Chr(34) + "10.1.0" + Chr(34) - user = get_setting("active_user") - if user <> invalid and user <> "" then - auth = auth + ", UserId=" + Chr(34) + user + Chr(34) - end if + user = get_setting("active_user") + if user <> invalid and user <> "" then + auth = auth + ", UserId=" + Chr(34) + user + Chr(34) + end if - token = get_user_setting("token") - if token <> invalid and token <> "" then - auth = auth + ", Token=" + Chr(34) + token + Chr(34) - end if + token = get_user_setting("token") + if token <> invalid and token <> "" then + auth = auth + ", Token=" + Chr(34) + token + Chr(34) + end if - request.AddHeader("X-Emby-Authorization", auth) - return request + request.AddHeader("X-Emby-Authorization", auth) + return request +end function + +function AboutMe() + url = Substitute("Users/{0}", get_setting("active_user")) + resp = APIRequest(url) + return parseRequest(resp) end function @@ -122,45 +128,30 @@ end function ' Params: None ' Returns { Items, TotalRecordCount } function LibraryList() - url = Substitute("Users/{0}/Views/", get_setting("active_user")) - resp = APIRequest(url) - return parseRequest(resp) + url = Substitute("Users/{0}/Views/", get_setting("active_user")) + resp = APIRequest(url) + return parseRequest(resp) end function ' Search for a string ' Params: Search Query ' Returns: { SearchHints, TotalRecordCount } function SearchMedia(query as String) - resp = APIRequest("Search/Hints", {"searchTerm": query}) - return parseRequest(resp) + resp = APIRequest("Search/Hints", {"searchTerm": query}) + return parseRequest(resp) end function ' List items from within a Library ' Params: Library ID, Limit, Offset, SortBy, SortOrder, IncludeItemTypes, Fields, EnableImageTypes ' Returns { Items, TotalRecordCount } function ItemList(library_id=invalid as String) - url = Substitute("Users/{0}/Items/", get_setting("active_user")) - resp = APIRequest(url, {"parentid": library_id, "limit": 30}) - return parseRequest(resp) + url = Substitute("Users/{0}/Items/", get_setting("active_user")) + resp = APIRequest(url, {"parentid": library_id, "limit": 30}) + return parseRequest(resp) end function function ItemMetaData(id as String) - url = Substitute("Users/{0}/Items/{1}", get_setting("active_user"), id) - resp = APIRequest(url) - return parseRequest(resp) -end function - -' Video - -function VideoStream(id as String) - - player = createObject("roVideoPlayer") - - content = VideoContent(id) - - player.addContent(content) - - player.play() - - return player + url = Substitute("Users/{0}/Items/{1}", get_setting("active_user"), id) + resp = APIRequest(url) + return parseRequest(resp) end function diff --git a/source/Main.brs b/source/Main.brs index c7c46386..cf7d66d0 100644 --- a/source/Main.brs +++ b/source/Main.brs @@ -1,49 +1,50 @@ sub Main() - m.port = CreateObject("roMessagePort") - - ' This facade keeps the app open when maybe other screens close - facade = CreateObject("roSGScreen") - facade.show() + ' First thing to do is validate the ability to use the API if get_setting("server") = invalid then print "Get server details" - ' TODO - make this into a dialog ' TODO - be able to submit server info ShowServerSelect() end if if get_setting("active_user") = invalid then print "Get user login" - ' TODO - make this into a dialog - ' screen.CreateScene("UserSignIn") - ' TODO - sign in here + ' TODO - be able to submit user info + ' ShowSigninSelect() end if - ' TODO - something here to validate that the active_user is still - ' valid. + ' Confirm the configured server and user work + m.user = AboutMe() + if m.user.id <> get_setting("active_user") + ' TODO - proper handling of the scenario where things have gone wrong + print "OH NO!" + end if - selected = ShowLibrarySelect() - - await_response() + ShowLibrarySelect() end sub sub ShowServerSelect() port = CreateObject("roMessagePort") screen = CreateObject("roSGScreen") screen.setMessagePort(port) - scene = screen.CreateScene("ServerSelection") screen.show() - await_response() + while(true) + msg = wait(0, port) + if msg.isScreenClosed() then + return + else + print(msgType) + end if + end while end sub sub ShowLibrarySelect() port = CreateObject("roMessagePort") screen = CreateObject("roSGScreen") screen.setMessagePort(port) - scene = screen.CreateScene("Library") screen.show() @@ -56,32 +57,23 @@ sub ShowLibrarySelect() while(true) msg = wait(0, port) - print msg if type(msg) = "roSGScreenEvent" and msg.isScreenClosed() then exit while else if itemSelectedQ(msg) - target = getRowTarget(msg) - ShowLibraryOptions(target.libraryID) + target = getMsgRowTarget(msg) + if target.libraryType = "movies" + ShowMovieOptions(target.libraryID) + else + print "NOT YET IMPLEMENTED" + end if end if end while end sub -sub itemSelectedQ(msg) as boolean - return type(msg) = "roSGNodeEvent" and msg.getField() = "itemSelected" -end sub - -sub getRowTarget(msg) as object - node = msg.getRoSGNode() - coords = node.rowItemSelected - target = node.content.getChild(coords[0]).getChild(coords[1]) - return target -end sub - -sub ShowLibraryOptions(library_id) +sub ShowMovieOptions(library_id) port = CreateObject("roMessagePort") screen = CreateObject("roSGScreen") screen.setMessagePort(port) - scene = screen.CreateScene("Movies") screen.show() @@ -97,9 +89,8 @@ sub ShowLibraryOptions(library_id) if type(msg) = "roSGScreenEvent" and msg.isScreenClosed() then return else if itemSelectedQ(msg) - target = getRowTarget(msg) + target = getMsgRowTarget(msg) showVideoPlayer(target.movieID) - print msg end if end while end sub @@ -108,8 +99,7 @@ sub showVideoPlayer(id) port = CreateObject("roMessagePort") screen = CreateObject("roSGScreen") screen.setMessagePort(port) - - scene = screen.CreateScene("VideoScene") + scene = screen.CreateScene("Scene") screen.show() @@ -124,16 +114,15 @@ sub showVideoPlayer(id) end sub -sub await_response(port=invalid) - if port = invalid then - port = m.port - end if - while(true) - msg = wait(0, port) - if msg.isScreenClosed() then - return - else - print(msgType) - end if - end while +sub itemSelectedQ(msg) as boolean + ' "Q" stands for "Question mark" since itemSelected? wasn't acceptable + ' Probably needs a better name, but unique for now + return type(msg) = "roSGNodeEvent" and msg.getField() = "itemSelected" +end sub + +sub getMsgRowTarget(msg) as object + node = msg.getRoSGNode() + coords = node.rowItemSelected + target = node.content.getChild(coords[0]).getChild(coords[1]) + return target end sub diff --git a/source/VideoPlayer.brs b/source/VideoPlayer.brs index da19c27a..689b0e13 100644 --- a/source/VideoPlayer.brs +++ b/source/VideoPlayer.brs @@ -1,19 +1,15 @@ function VideoPlayer(scene, id) + video = scene.createChild("Video") + content = VideoContent(id) - content = VideoContent(id) + video.content = content - video = scene.findNode("VideoPlayer") - - video.content = content - - video.setFocus(true) - video.control = "play" - - return video + video.setFocus(true) + video.control = "play" + return video end function - function VideoContent(id) as object content = createObject("RoSGNode", "ContentNode")