Merge pull request #139 from cewert/add-continue-watching

Add "Continue Watching" to home screen
This commit is contained in:
Charles Ewert 2020-03-07 11:00:03 -05:00 committed by GitHub
commit 03ea7a8323
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 261 additions and 140 deletions

View File

@ -5,5 +5,9 @@ sub setData()
m.top.id = datum.id
m.top.name = datum.name
m.top.type = datum.CollectionType
if datum.CollectionType = invalid then
m.top.type = datum.type
else
m.top.type = datum.CollectionType
end if
end sub

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="LibraryData" extends="ContentNode">
<component name="HomeData" extends="ContentNode">
<interface>
<field id="id" type="string" />
<field id="name" type="string" />
@ -7,5 +7,5 @@
<field id="type" type="string" />
<field id="json" type="associativearray" onChange="setData" />
</interface>
<script type="text/brightscript" uri="library.brs" />
<script type="text/brightscript" uri="HomeData.brs" />
</component>

4
components/home/Home.brs Normal file
View File

@ -0,0 +1,4 @@
sub init()
m.tracker=m.top.createChild("TrackerTask")
m.top.overhangTitle = "Home"
end sub

13
components/home/Home.xml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="Home" extends="JFGroup">
<children>
<HomeRows id="homeRows" />
<OptionsSlider id="options" />
</children>
<interface>
<field id="libraries" alias="homeRows.libList" />
<field id="continueWatching" alias="homeRows.continueList" />
<field id="homeSelection" alias="homeRows.itemSelected" />
</interface>
<script type="text/brightscript" uri="Home.brs" />
</component>

View File

@ -0,0 +1,50 @@
sub init()
end sub
function itemContentChanged() as void
itemData = m.top.itemContent
if itemData = invalid then return
itemText = m.top.findNode("itemText")
itemPoster = m.top.findNode("itemPoster")
if itemData.json.CollectionType = invalid then
itemPoster.uri = itemData.imageURL
itemText.height = 34
itemText.font.size = 25
itemText.horizAlign = "left"
itemText.vertAlign = "bottom"
itemTextExtra = m.top.findNode("itemTextExtra")
itemTextExtra.font.size = 24
itemTextExtra.visible = true
if itemData.type = "Episode" then
itemText.text = itemData.json.SeriesName
itemTextExtra.text = "S" + StrI(itemData.json.ParentIndexNumber).trim() + "E" + StrI(itemData.json.IndexNumber).trim() + " - " + itemData.name
else if itemData.type = "Movie" then
itemText.text = itemData.name
itemTextExtra.text = StrI(itemData.json.ProductionYear).trim() + " - " + itemData.json.OfficialRating
end if
else
' handle libraries with no picture
if itemData.type = "livetv" then
itemPoster.width = "96"
itemPoster.height = "96"
itemPoster.translation = "[192, 88]"
itemPoster.uri = "pkg:/images/baseline_live_tv_white_48dp.png"
itemText.text = itemData.name
else if itemData.type = "music" then
itemPoster.width = "96"
itemPoster.height = "96"
itemPoster.translation = "[192, 88]"
itemPoster.uri = "pkg:/images/baseline_library_music_white_48dp.png"
itemText.text = itemData.name
else
itemPoster.uri = itemData.imageURL
itemText.text = itemData.name
end if
end if
end function

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="HomeItem" extends="Group">
<children>
<Poster id="itemPoster" width="464" height="261" translation="[8,5]" />
<Label id="itemText" horizAlign="center" vertAlign="center" font="font:SmallBoldSystemFont" height="64" width="456" translation="[8,267]" />
<Label id="itemTextExtra" horizAlign="left" vertAlign="center" font="font:SmallBoldSystemFont" height="32" width="456" translation="[8,300]" visible="false" />
</children>
<interface>
<field id="itemContent" type="node" onChange="itemContentChanged"/>
</interface>
<script type="text/brightscript" uri="HomeItem.brs" />
</component>

View File

@ -0,0 +1,73 @@
sub init()
m.top.itemComponentName = "HomeItem"
' My media row should always exist
m.top.numRows = 1
m.top.content = CreateObject("roSGNode", "ContentNode")
m.top.rowFocusAnimationStyle = "fixedFocusWrap"
m.top.vertFocusAnimationStyle = "floatingFocus"
m.top.showRowLabel = [true]
m.top.rowLabelOffset = [0, 20]
m.top.showRowCounter = [true]
updateSize()
m.top.setfocus(true)
end sub
sub updateSize()
sideborder = 100
m.top.translation = [111, 155]
itemWidth = 480
itemHeight = 330
m.top.itemSize = [1920 - 111 - 27, itemHeight]
' spacing between rows
m.top.itemSpacing = [0, 105]
' size of the item in the row
m.top.rowItemSize = [itemWidth, itemHeight]
' spacing between items in a row
m.top.rowItemSpacing = [20, 0]
m.top.visible = true
end sub
sub showLibraryRow()
libs = m.top.libList
libraryRow = CreateObject("roSGNode", "ContentNode")
libraryRow.title = "My Media"
for i = 1 to libs.TotalRecordCount
item = libs.Items[i - 1]
libraryRow.appendChild(item)
end for
m.top.content.appendChild(libraryRow)
end sub
sub showContinueRow()
continueItems = m.top.continueList
if continueItems.TotalRecordCount > 0 then
continueRow = CreateObject("roSGNode", "ContentNode")
continueRow.title = "Continue Watching"
for i = 1 to continueItems.TotalRecordCount
item = continueItems.Items[i - 1]
continueRow.appendChild(item)
end for
m.top.numRows++
m.top.content.appendChild(continueRow)
end if
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
return false
end function

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="HomeRows" extends="RowList">
<interface>
<field id="libList" type="associativearray" onChange="showLibraryRow" />
<field id="continueList" type="associativearray" onChange="showContinueRow" />
</interface>
<script type="text/brightscript" uri="HomeRows.brs"/>
</component>

View File

@ -1,26 +0,0 @@
sub init()
itemText = m.top.findNode("itemText")
itemText.text = "Loading..."
end sub
function itemContentChanged() as void
itemData = m.top.itemContent
if itemData = invalid then return
itemText = m.top.findNode("itemText")
itemText.text = itemData.name
itemPoster = m.top.findNode("itemPoster")
if itemData.type = "livetv" then
itemPoster.width = "96"
itemPoster.height = "96"
itemPoster.translation = "[192, 88]"
itemPoster.uri = "pkg:/images/baseline_live_tv_white_48dp.png"
else if itemData.type = "music" then
itemPoster.width = "96"
itemPoster.height = "96"
itemPoster.translation = "[192, 88]"
itemPoster.uri = "pkg:/images/baseline_library_music_white_48dp.png"
else
itemPoster.uri = itemData.imageURL
end if
end function

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="LibItem" extends="Group">
<children>
<Poster id="itemPoster" width="464" height="261" translation="[8,5]" />
<Label id="itemText" horizAlign="center" vertAlign="center" font="font:MediumSystemFont" height="64" width="480" translation="[0,266]" />
</children>
<interface>
<field id="itemContent" type="node" onChange="itemContentChanged"/>
</interface>
<script type="text/brightscript" uri="item.brs" />
</component>

View File

@ -1,64 +0,0 @@
sub init()
m.top.itemComponentName = "LibItem"
m.top.content = getData()
m.top.rowFocusAnimationStyle = "fixedFocusWrap"
m.top.vertFocusAnimationStyle = "fixedFocusWrap"
m.top.showRowLabel = [true]
m.top.rowLabelOffset = [0, 20]
m.top.showRowCounter = [true]
updateSize()
m.top.setfocus(true)
end sub
sub updateSize()
' real border is border - rowlist title and rowlist padding
topborder = 40
sideborder = 119
' 115 is the overhang height
m.top.translation = [sideborder, topborder + 115]
itemWidth = 480
itemHeight = 330
m.top.visible = true
' size of the whole row
m.top.itemSize = [1920 - sideborder * 2, itemHeight]
' spacing between rows
m.top.itemSpacing = [0, 30]
' size of the item in the row
m.top.rowItemSize = [itemWidth, itemHeight]
' spacing between items in a row
m.top.rowItemSpacing = [20, 0]
end sub
function getData()
if m.top.libList = invalid then
data = CreateObject("roSGNode", "ContentNode")
m.top.content = data
return data
end if
libs = m.top.libList
data = CreateObject("roSGNode", "ContentNode")
row = data.CreateChild("ContentNode")
row.title = "My Media"
for i = 1 to libs.TotalRecordCount
item = libs.Items[i - 1]
row.appendChild(item)
end for
m.top.content = data
return data
end function
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
return false
end function

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="LibraryRow" extends="RowList">
<interface>
<field id="libList" type="associativearray" onChange="getData" />
</interface>
<script type="text/brightscript" uri="rowlist.brs"/>
</component>

View File

@ -1,4 +0,0 @@
sub init()
m.tracker=m.top.createChild("TrackerTask")
m.top.overhangTitle = "Home"
end sub

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="Library" extends="JFGroup">
<children>
<LibraryRow id="LibrarySelect" />
<OptionsSlider id="options" />
</children>
<interface>
<field id="libraries" alias="LibrarySelect.libList" />
<field id="librarySelected" alias="LibrarySelect.itemSelected" />
</interface>
<script type="text/brightscript" uri="scene.brs" />
</component>

View File

@ -20,11 +20,10 @@ sub Main()
' First thing to do is validate the ability to use the API
LoginFlow()
' Confirm the configured server and user work
group = CreateLibraryGroup()
m.overhang.title = group.overhangTitle
' load home page
m.overhang.title = "Home"
m.overhang.currentUser = m.user.Name
group = CreateHomeGroup()
m.scene.appendChild(group)
m.scene.observeField("backPressed", m.port)
@ -71,9 +70,9 @@ sub Main()
else
group.setFocus(true)
end if
else if isNodeEvent(msg, "librarySelected")
else if isNodeEvent(msg, "homeSelection")
' If you select a library from ANYWHERE, follow this flow
node = getMsgPicker(msg, "LibrarySelect")
node = getMsgPicker(msg, "homeRows")
if node.type = "movies"
group.lastFocus = group.focusedChild
group.setFocus(false)
@ -100,6 +99,31 @@ sub Main()
group = CreateCollectionsList(node)
group.overhangTitle = node.name
m.scene.appendChild(group)
else if node.type = "Episode" then
' play episode
' todo: create an episode page to link here
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
video_id = node.id
group = CreateVideoPlayerGroup(video_id)
m.scene.appendChild(group)
group.setFocus(true)
group.control = "play"
ReportPlayback(group, "start")
m.overhang.visible = false
else if node.type = "Movie" then
' open movie detail page
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
m.overhang.title = node.name
m.overhang.showOptions = false
group = CreateMovieDetailsGroup(node)
group.overhangTitle = node.name
m.scene.appendChild(group)
else
' TODO - switch on more node types
message_dialog("This library type is not yet implemented: " + node.type + ".")

View File

@ -97,16 +97,16 @@ sub CreateSigninGroup()
group.visible = false
end sub
function CreateLibraryGroup()
function CreateHomeGroup()
' Main screen after logging in. Shows the user's libraries
group = CreateObject("roSGNode", "Library")
group = CreateObject("roSGNode", "Home")
libs = LibraryList()
group.libraries = libs
group.observeField("librarySelected", m.port)
con = HomeItemList("continue")
group.continueWatching = con
library = group.findNode("LibrarySelect")
group.observeField("homeSelection", m.port)
sidepanel = group.findNode("options")
sidepanel.observeField("closeSidePanel", m.port)

View File

@ -42,7 +42,7 @@ function LibraryList()
data = getJson(resp)
results = []
for each item in data.Items
tmp = CreateObject("roSGNode", "LibraryData")
tmp = CreateObject("roSGNode", "HomeData")
tmp.json = item
params = { "Tag" : tmp.json.ImageTags.Primary, "maxHeight" : 261, "maxWidth" : 464 }
tmp.imageURL = ImageURL(tmp.json.id, "Primary", params)
@ -140,6 +140,64 @@ function ItemList(library_id = invalid as string, params = {})
return data
end function
' Return items for use on home screen (homeRows)
function HomeItemList(row = "" as string, params = {})
if params["limit"] = invalid
params["limit"] = 20
end if
if row = "continue" then
params["recursive"] = true
params["SortBy"] = "DatePlayed"
params["SortOrder"] = "Descending"
params["Filters"] = "IsResumable"
end if
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
resp = APIRequest(url, params)
data = getJson(resp)
results = []
for each item in data.Items
tmp = CreateObject("roSGNode", "HomeData")
imgParams = {}
param = { "AddPlayedIndicator": item.UserData.Played }
imgParams.Append(param)
if item.UserData.PlayedPercentage <> invalid then
param = { "PercentPlayed": item.UserData.PlayedPercentage }
imgParams.Append(param)
end if
param = { "maxHeight": 261 }
imgParams.Append(param)
param = { "maxWidth": 464 }
imgParams.Append(param)
if item.type = "Movie"
if item.ImageTags.Thumb <> invalid then
param = { "Tag" : item.ImageTags.Thumb }
imgParams.Append(param)
tmp.imageURL = ImageURL(item.id, "Thumb", imgParams)
else
param = { "Tag" : item.ImageTags.Primary }
imgParams.Append(param)
tmp.imageURL = ImageURL(item.id, "Primary", imgParams)
end if
else if item.type = "Episode"
if item.ImageTags.Primary <> invalid then
param = { "Tag" : item.ImageTags.Primary }
imgParams.Append(param)
end if
tmp.imageURL = ImageURL(item.id, "Primary", imgParams)
end if
tmp.json = item
results.push(tmp)
end for
data.items = results
return data
end function
' MetaData about an item
function ItemMetaData(id as string)
url = Substitute("Users/{0}/Items/{1}", get_setting("active_user"), id)