mirror of
https://github.com/jellyfin/jellyfin-roku.git
synced 2024-11-28 00:40:39 +00:00
Merge pull request #139 from cewert/add-continue-watching
Add "Continue Watching" to home screen
This commit is contained in:
commit
03ea7a8323
@ -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
|
@ -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
4
components/home/Home.brs
Normal 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
13
components/home/Home.xml
Normal 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>
|
50
components/home/HomeItem.brs
Normal file
50
components/home/HomeItem.brs
Normal 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
|
12
components/home/HomeItem.xml
Normal file
12
components/home/HomeItem.xml
Normal 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>
|
73
components/home/HomeRows.brs
Normal file
73
components/home/HomeRows.brs
Normal 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
|
8
components/home/HomeRows.xml
Normal file
8
components/home/HomeRows.xml
Normal 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>
|
@ -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
|
@ -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>
|
@ -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
|
@ -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>
|
@ -1,4 +0,0 @@
|
||||
sub init()
|
||||
m.tracker=m.top.createChild("TrackerTask")
|
||||
m.top.overhangTitle = "Home"
|
||||
end sub
|
@ -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>
|
@ -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 + ".")
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user