Fix Collections for Movies, Episodes & Series

This commit is contained in:
Neil Burrows 2020-05-13 20:36:38 +01:00
parent 93c64f7422
commit 43bdf508cd
13 changed files with 251 additions and 137 deletions

View File

@ -1,120 +1,116 @@
sub init()
main = m.top.findNode("main_group")
dimensions = m.top.getScene().currentDesignResolution
main.translation=[50, 50]
m.rowList = m.top.findNode("RowList") ' createObject("roSGNode", "RowList")
' m.top.appendChild(m.rowList)
m.rowList.itemComponentName = "HomeItem"
formatRowList()
m.rowList.setfocus(true)
m.rowList.observeField("rowItemSelected", "itemSelected")
m.top.findNode("buttons").setFocus(true)
end sub
sub itemContentChanged()
' Updates video metadata
item = m.top.itemContent
itemData = item.json
sub formatRowList()
m.top.findNode("poster").uri = m.top.itemContent.posterURL
' how many rows are visible on the screen
m.rowList.numRows = 2
' Handle all "As Is" fields
m.top.overhangTitle = itemData.name
setFieldText("releaseYear", itemData.productionYear)
setFieldText("officialRating", itemData.officialRating)
setFieldText("communityRating", str(itemData.communityRating))
setFieldText("overview", itemData.overview)
m.rowList.rowFocusAnimationStyle = "fixedFocusWrap"
m.rowList.vertFocusAnimationStyle = "fixedFocus"
setFieldText("runtime", stri(getRuntime()) + " mins")
setFieldText("ends-at", tr("Ends at %1").Replace("%1", getEndTime()))
m.rowList.showRowLabel = [true]
m.rowList.rowLabelOffset = [0, 20]
m.rowList.showRowCounter = [true]
if itemData.genres.count() > 0
setFieldText("genres", itemData.genres.join(", "))
end if
director = invalid
for each person in itemData.people
if person.type = "Director"
director = person.name
exit for
sideborder = 100
m.rowList.translation = [111, 155]
m.rowItemSizes = []
itemWidth = 480
itemHeight = 330
m.rowList.itemSize = [1920 - 111 - 27, itemHeight]
' spacing between rows
m.rowList.itemSpacing = [0, 105]
' spacing between items in a row
m.rowList.rowItemSpacing = [20, 0]
m.rowList.visible = true
end sub
sub setupRows()
for each item in m.top.objects.Items
homeItem = CreateObject("roSGNode", "HomeData")
homeItem.json = item.json
if homeItem.Type = "Video" or homeItem.Type = "Movie" or homeItem.Type = "Episode" then
if m.videoRow = invalid then
m.videoRow = CreateObject("roSGNode", "HomeRow")
m.videoRow.title = tr("Videos")
m.videoRow.usePoster = true
m.videoRow.imageWidth = 180
end if
m.videoRow.appendChild(homeItem)
else if homeItem.Type = "MusicAlbum"
if m.albumRow = invalid then
m.albumRow = CreateObject("roSGNode", "HomeRow")
m.albumRow.imageWidth = 261
m.albumRow.title = tr("Albums")
m.albumRow.usePoster = true
end if
m.albumRow.appendChild(homeItem)
else if homeItem.Type = "Series"
if m.seriesRow = invalid then
m.seriesRow = CreateObject("roSGNode", "HomeRow")
m.seriesRow.title = tr("Series")
m.seriesRow.usePoster = true
m.seriesRow.imageWidth = 180
end if
m.seriesRow.appendChild(homeItem)
else
print "Collection - Unknown Type ", homeItem.Type
end if
end for
if director <> invalid
setFieldText("director", "Director: " + director)
data = CreateObject("roSGNode", "ContentNode")
if m.videoRow <> invalid then
data.appendChild(m.videoRow)
m.rowItemSizes.push([188, 331])
end if
setFieldText("video_codec", "Video: " + itemData.mediaStreams[0].displayTitle)
setFieldText("audio_codec", "Audio: " + itemData.mediaStreams[1].displayTitle)
' TODO - cmon now. these are buttons, not words
if itemData.taglines.count() > 0
setFieldText("tagline", itemData.taglines[0])
if m.seriesRow <> invalid then
data.appendChild(m.seriesRow)
m.rowItemSizes.push([188, 331])
end if
setFavoriteColor()
setWatchedColor()
if m.albumRow <> invalid then
data.appendChild(m.albumRow)
m.rowItemSizes.push([261, 331])
end if
m.rowList.rowItemSize = m.rowItemSizes
m.rowList.content = data
end sub
sub setFieldText(field, value)
node = m.top.findNode(field)
if node = invalid or value = invalid then return
' Handle non strings... Which _shouldn't_ happen, but hey
if type(value) = "roInt" or type(value) = "Integer" then
value = str(value)
else if type(value) <> "roString" and type(value) <> "String" then
value = ""
end if
node.text = value
end sub
function getRuntime() as integer
itemData = m.top.itemContent.json
' A tick is .1ms, so 1/10,000,000 for ticks to seconds,
' then 1/60 for seconds to minutess... 1/600,000,000
return round(itemData.RunTimeTicks / 600000000.0)
function itemSelected()
m.top.selectedItem = m.rowList.content.getChild(m.rowList.rowItemSelected[0]).getChild(m.rowList.rowItemSelected[1])
end function
function getEndTime() as string
itemData = m.top.itemContent.json
date = CreateObject("roDateTime")
duration_s = int(itemData.RunTimeTicks / 10000000.0)
date.fromSeconds(date.asSeconds() + duration_s)
date.toLocalTime()
return formatTime(date)
end function
sub setFavoriteColor()
fave = m.top.itemContent.favorite
fave_button = m.top.findNode("favorite-button")
if fave
fave_button.textColor = "#00ff00ff"
fave_button.focusedTextColor = "#269926ff"
else
fave_button.textColor = "0xddddddff"
fave_button.focusedTextColor = "#262626ff"
end if
end sub
sub setWatchedColor()
watched = m.top.itemContent.watched
watched_button = m.top.findNode("watched-button")
if watched
watched_button.textColor = "#ff0000ff"
watched_button.focusedTextColor = "#992626ff"
else
watched_button.textColor = "0xddddddff"
watched_button.focusedTextColor = "#262626ff"
end if
end sub
function round(f as float) as integer
' BrightScript only has a "floor" round
' This compares floor to floor + 1 to find which is closer
m = int(f)
n = m + 1
x = abs(f - m)
y = abs(f - n)
if y > x
return m
else
return n
end if
end function

View File

@ -1,36 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="CollectionDetail" extends="JFGroup">
<children>
<LayoutGroup id="main_group" layoutDirection="horiz" >
<Poster id="poster"
translation="[250,150]"
width="400" height="600" />
<LayoutGroup layoutDirection="vert" translation="[355, 150]" itemSpacings="[25]">
<LayoutGroup layoutDirection="horiz" itemSpacings="[150]">
<Label id="releaseYear" />
<Label id="runtime" />
<Label id="officialRating" />
<Label id="communityRating" />
<Label id="ends-at" />
</LayoutGroup>
<Label id="genres" />
<Label id="director" />
<Label id="video_codec" />
<Label id="audio_codec" />
<ButtonGroupHoriz id="buttons" itemSpacings="[10]">
<Button text="Play" id="play-button" />
<Button text="Watched" id="watched-button" />
<Button text="Favorite" id="favorite-button" />
</ButtonGroupHoriz>
<Label id="tagline" />
<Label id="overview" />
</LayoutGroup>
</LayoutGroup>
<RowList id="rowList" />
</children>
<interface>
<field id="itemContent" type="node" onChange="itemContentChanged" />
<field id="collectionId" type="string" onChange="collectionIdChanged" />
<field id="selectedItem" type="node" alwaysNotify="true" />
<field id="objects" type="associativearray" onChange="setupRows" />
</interface>
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
<script type="text/brightscript" uri="CollectionDetail.brs" />
</component>
</component>

View File

@ -0,0 +1,7 @@
sub setFields()
datum = m.top.json
m.top.id = datum.id
m.top.title = datum.name
end sub

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="AlbumData" extends="ContentNode">
<interface>
<field id="id" type="string" />
<field id="title" type="string" />
<field id="json" type="associativearray" onChange="setFields" />
</interface>
<script type="text/brightscript" uri="AlbumData.brs" />
</component>

View File

@ -9,6 +9,7 @@
<field id="favorite" type="boolean" />
<field id="watched" type="boolean" />
<field id="type" type="string" value="boxsets" />
<field id="overview" type="string" value="boxsets" />
<field id="json" type="associativearray" onChange="setFields" />
</interface>
<script type="text/brightscript" uri="CollectionData.brs" />

View File

@ -100,6 +100,33 @@ sub setData()
m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams)
end if
else if datum.type = "Video" then
imgParams = { AddPlayedIndicator: datum.UserData.Played }
if datum.UserData.PlayedPercentage <> invalid then
imgParams.Append({ "PercentPlayed": datum.UserData.PlayedPercentage })
end if
imgParams.Append({ "maxHeight": 261 })
imgParams.Append({ "maxWidth": 175 })
if datum.ImageTags.Primary <> invalid then
param = { "Tag" : datum.ImageTags.Primary }
imgParams.Append(param)
end if
m.top.posterURL = ImageURL(datum.id, "Primary", imgParams)
' For wide image, use backdrop
imgParams["maxWidth"] = 464
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid then
imgParams["Tag"] = datum.imageTags.Thumb
m.top.thumbnailUrl = ImageURL(datum.Id, "Thumb", imgParams)
else if datum.BackdropImageTags[0] <> invalid then
imgParams["Tag"] = datum.BackdropImageTags[0]
m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams)
end if
else if datum.type = "MusicAlbum" then
params = { "Tag" : datum.ImageTags.Primary, "maxHeight" : 261, "maxWidth" : 261 }
m.top.thumbnailURL = ImageURL(datum.id, "Primary", params)

View File

@ -0,0 +1,16 @@
sub setFields()
datum = m.top.json
m.top.id = datum.id
m.top.title = datum.name
end sub
sub setPoster()
if m.top.image <> invalid
m.top.posterURL = m.top.image.url
else
m.top.posterURL = ""
end if
end sub

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="VideoData" extends="ContentNode">
<interface>
<field id="id" type="string" />
<field id="title" type="string" />
<field id="json" type="associativearray" onChange="setFields" />
<field id="image" type="node" onChange="setPoster" />
<field id="posterUrl" type="string" />
</interface>
<script type="text/brightscript" uri="VideoData.brs" />
</component>

View File

@ -103,12 +103,26 @@ sub itemContentChanged()
return
end if
if itemData.type = "Video" then
m.itemText.text = itemData.name
if imageWidth = 180
itemPoster.uri = itemData.posterURL
else
itemPoster.uri = itemData.thumbnailURL
end if
return
end if
if itemData.type = "Series" then
m.itemText.text = itemData.name
if usePoster = true then
itemPoster.uri = itemData.widePosterURL
if imageWidth = 180 then
itemPoster.uri = itemData.posterURL
else
itemPoster.uri = itemData.widePosterURL
end if
else
itemPoster.uri = itemData.thumbnailURL
end if

View File

@ -2,7 +2,7 @@
<component name="HomeItem" extends="Group">
<children>
<Rectangle id="backdrop" width="464" height="261" translation="[8,5]" />
<Poster id="itemPoster" width="464" height="261" translation="[8,5]" />
<Poster id="itemPoster" width="464" height="261" translation="[8,5]" loadDisplayMode="scaleToZoom" />
<ScrollingLabel id="itemText" horizAlign="center" vertAlign="center" font="font:SmallBoldSystemFont" height="64" maxWidth="456" translation="[8,267]" repeatCount="0" />
<Label id="itemTextExtra" horizAlign="left" vertAlign="center" font="font:SmallBoldSystemFont" height="32" width="456" translation="[8,300]" visible="false" color="#777777FF" />
</children>

View File

@ -151,6 +151,21 @@ sub Main()
group = CreateMovieDetailsGroup(selectedItem)
group.overhangTitle = selectedItem.name
m.scene.appendChild(group)
else if selectedItem.type = "Video" then
' play episode
video_id = selectedItem.id
video = CreateVideoPlayerGroup(video_id)
if video <> invalid then
group.lastFocus = group.focusedChild
group.setFocus(false)
group.visible = false
group = video
m.scene.appendChild(group)
group.setFocus(true)
group.control = "play"
ReportPlayback(group, "start")
m.overhang.visible = false
end if
else
' TODO - switch on more node types
if selectedItem.type = "CollectionFolder" OR selectedItem.type = "UserView" then
@ -168,7 +183,7 @@ sub Main()
group.visible = false
m.overhang.title = node.title
group = CreateMovieListGroup(node)
group = CreateCollectionDetailList(node.Id)
group.overhangTitle = node.title
m.scene.appendChild(group)
else if isNodeEvent(msg, "movieSelected")

View File

@ -293,7 +293,7 @@ function CreateSeasonDetailsGroup(series, season)
end function
function CreateCollectionsList(libraryId)
' Load Movie Collection Items
' Load Collection Items
group = CreateObject("roSGNode", "Collections")
group.id = libraryId
@ -344,6 +344,27 @@ function CreateCollectionsList(libraryId)
return group
end function
function CreateCollectionDetailList(collectionId)
sort_order = get_user_setting("movie_sort_order", "Ascending")
sort_field = get_user_setting("movie_sort_field", "SortName")
item_list = ItemList(collectionId, {
"SortBy": sort_field,
"SortOrder": sort_order
})
group = CreateObject("roSGNode", "CollectionDetail")
group.collectionId = collectionId
group.objects = item_list
group.observeField("selectedItem", m.port)
return group
end function
function CreateChannelList(libraryId)
group = CreateObject("roSGNode", "Channels")

View File

@ -130,6 +130,22 @@ function ItemList(library_id = invalid as string, params = {})
tmp.image = PosterImage(item.id, imgParams)
tmp.json = item
results.push(tmp)
else if item.type = "Episode"
imgParams.Append({ "AddPlayedIndicator": item.UserData.Played })
tmp = CreateObject("roSGNode", "TVEpisodeData")
tmp.image = PosterImage(item.id, imgParams)
tmp.json = item
results.push(tmp)
else if item.type = "MusicAlbum"
tmp = CreateObject("roSGNode", "AlbumData")
tmp.image = PosterImage(item.id, imgParams)
tmp.json = item
results.push(tmp)
else if item.type = "Video"
tmp = CreateObject("roSGNode", "VideoData")
tmp.image = PosterImage(item.id, imgParams)
tmp.json = item
results.push(tmp)
else
print "Items.brs::ItemList received unhandled type: " item.type
' Otherwise we just stick with the JSON
@ -177,6 +193,11 @@ function ItemMetaData(id as string)
tmp.image = PosterImage(data.id)
tmp.json = data
return tmp
else if data.type = "Video"
tmp = CreateObject("roSGNode", "VideoData")
tmp.image = PosterImage(data.id)
tmp.json = data
return tmp
else if data.type = "TvChannel"
tmp = CreateObject("roSGNode", "ChannelData")
tmp.image = PosterImage(data.id)