Merge pull request #213 from neilsb/collections
Fix Collections for Movies, Episodes & Series
This commit is contained in:
commit
146e00b23b
|
@ -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(", "))
|
||||
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
|
||||
director = invalid
|
||||
for each person in itemData.people
|
||||
if person.type = "Director"
|
||||
director = person.name
|
||||
exit for
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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>
|
7
components/data/AlbumData.brs
Normal file
7
components/data/AlbumData.brs
Normal file
|
@ -0,0 +1,7 @@
|
|||
sub setFields()
|
||||
datum = m.top.json
|
||||
|
||||
m.top.id = datum.id
|
||||
m.top.title = datum.name
|
||||
end sub
|
||||
|
9
components/data/AlbumData.xml
Normal file
9
components/data/AlbumData.xml
Normal 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>
|
|
@ -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" />
|
||||
|
|
|
@ -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)
|
||||
|
|
16
components/data/VideoData.brs
Normal file
16
components/data/VideoData.brs
Normal 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
|
11
components/data/VideoData.xml
Normal file
11
components/data/VideoData.xml
Normal 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>
|
|
@ -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
|
||||
if imageWidth = 180 then
|
||||
itemPoster.uri = itemData.posterURL
|
||||
else
|
||||
itemPoster.uri = itemData.widePosterURL
|
||||
end if
|
||||
else
|
||||
itemPoster.uri = itemData.thumbnailURL
|
||||
end if
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -287,7 +287,7 @@ function CreateSeasonDetailsGroup(series, season)
|
|||
end function
|
||||
|
||||
function CreateCollectionsList(libraryId)
|
||||
' Load Movie Collection Items
|
||||
' Load Collection Items
|
||||
group = CreateObject("roSGNode", "Collections")
|
||||
group.id = libraryId
|
||||
|
||||
|
@ -338,6 +338,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")
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user