Movie extras (#520)
This commit is contained in:
parent
0e2e2052e1
commit
0f18add523
|
@ -17,9 +17,9 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
m.top.focusButton = target
|
||||
return true
|
||||
else if key = "up" or key = "down"
|
||||
m.top.escape = true
|
||||
m.top.escape = key
|
||||
return true
|
||||
end if
|
||||
|
||||
return false
|
||||
end function
|
||||
end function
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="ButtonGroupHoriz" extends="ButtonGroup">
|
||||
<interface>
|
||||
<field id="escape" type="boolean" alwaysNotify="true" />
|
||||
<field id="escape" type="string" alwaysNotify="true" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="ButtonGroupHoriz.brs" />
|
||||
|
||||
|
|
|
@ -485,4 +485,4 @@ sub updateTitle()
|
|||
else
|
||||
m.top.overhangTitle = m.top.parentItem.title + " (Filtered)"
|
||||
end if
|
||||
end sub
|
||||
end sub
|
||||
|
|
35
components/OverviewDialog.xml
Normal file
35
components/OverviewDialog.xml
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="OverviewDialog" extends="StandardDialog">
|
||||
<interface>
|
||||
<field id="Title" type="string" onchange="setTitle" />
|
||||
<field id="Overview" type="string" onChange="setOverview" />
|
||||
</interface>
|
||||
<script type="text/brightscript">
|
||||
<![CDATA[
|
||||
sub setTitle()
|
||||
m.top.findNode("titleArea").primaryTitle = m.top.title
|
||||
end sub
|
||||
|
||||
sub setOverview()
|
||||
m.top.findNode("description").text = m.top.overview
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if press = false then return false
|
||||
|
||||
if key = "OK" and m.top.findNode("contentArea").isInFocusChain()
|
||||
m.top.close = true
|
||||
end if
|
||||
|
||||
return false
|
||||
end function
|
||||
]]>
|
||||
</script>
|
||||
<children>
|
||||
<StdDlgTitleArea id="titleArea" />
|
||||
<StdDlgContentArea id="contentArea">
|
||||
<StdDlgTextItem id="description"
|
||||
namedTextStyle="secondary" />
|
||||
</StdDlgContentArea>
|
||||
</children>
|
||||
</component>
|
164
components/PersonDetails.brs
Normal file
164
components/PersonDetails.brs
Normal file
|
@ -0,0 +1,164 @@
|
|||
sub init()
|
||||
m.dscr = m.top.findNode("description")
|
||||
m.vidsList = m.top.findNode("extrasGrid")
|
||||
m.btnGrp = m.top.findNode("buttons")
|
||||
m.btnGrp.observeField("escape", "onButtonGroupEscaped")
|
||||
m.favBtn = m.top.findNode("favorite-button")
|
||||
m.extrasGrp = m.top.findNode("extrasGrp")
|
||||
m.top.findNode("VertSlider").keyValue = "[[30, 998], [30, 789], [30, 580], [30,371 ], [30, 162]]"
|
||||
m.extrasGrp.opacity = 1.0
|
||||
m.extrasGrp.translation = "[30, 998]"
|
||||
m.dscr.observeField("isTextEllipsized", "onEllipsisChanged")
|
||||
createDialogPallete()
|
||||
m.top.optionsAvailable = false
|
||||
end sub
|
||||
|
||||
sub loadPerson()
|
||||
item = m.top.itemContent
|
||||
itemData = item.json
|
||||
m.top.Id = itemData.id
|
||||
m.top.findNode("Name").Text = itemData.Name
|
||||
if itemData.PremiereDate <> invalid and itemData.PremiereDate <> ""
|
||||
birthDate = CreateObject("roDateTime")
|
||||
birthDate.FromISO8601String(itemData.PremiereDate)
|
||||
deathDate = CreateObject("roDatetime")
|
||||
lifeString = tr("Born") + ": " + birthDate.AsDateString("short-month-no-weekday")
|
||||
|
||||
if itemData.EndDate <> invalid and itemData.EndDate <> ""
|
||||
deathDate.FromISO8601String(itemData.EndDate)
|
||||
lifeString = lifeString + " * " + tr("Died") + ": " + deathDate.AsDateString("short-month-no-weekday")
|
||||
|
||||
end if
|
||||
' Calculate age
|
||||
age = deathDate.getYear() - birthDate.getYear()
|
||||
if deathDate.getMonth() < birthDate.getMonth()
|
||||
age--
|
||||
else if deathDate.getMonth() = birthDate.getMonth()
|
||||
if deathDate.getDayOfMonth() < birthDate.getDayOfMonth()
|
||||
age--
|
||||
end if
|
||||
end if
|
||||
lifeString = lifeString + " * " + tr("Age") + ": " + stri(age)
|
||||
m.top.findNode("premierDate").Text = lifeString
|
||||
end if
|
||||
m.dscr.text = itemData.Overview
|
||||
if item.posterURL <> invalid and item.posterURL <> ""
|
||||
m.top.findnode("personImage").uri = item.posterURL
|
||||
else
|
||||
m.top.findnode("personImage").uri = "pkg:/images/baseline_person_white_48dp.png"
|
||||
end if
|
||||
m.vidsList.callFunc("loadPersonVideos", m.top.Id)
|
||||
|
||||
setFavoriteColor()
|
||||
m.favBtn.setFocus(true)
|
||||
end sub
|
||||
|
||||
sub onEllipsisChanged()
|
||||
if m.dscr.isTextEllipsized
|
||||
dscrShowFocus()
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub dscrShowFocus()
|
||||
if m.dscr.isTextEllipsized
|
||||
m.dscr.setFocus(true)
|
||||
m.dscr.opacity = 1.0
|
||||
m.top.findNode("dscrBorder").color = "#d0d0d0ff"
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub onButtonGroupEscaped()
|
||||
key = m.btnGrp.escape
|
||||
if key = "down"
|
||||
m.vidsList.setFocus(true)
|
||||
m.top.findNode("VertSlider").reverse = false
|
||||
m.top.findNode("pplAnime").control = "start"
|
||||
else if key = "up" and m.dscr.isTextEllipsized
|
||||
dscrShowFocus()
|
||||
end if
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "OK"
|
||||
if m.dscr.hasFocus() and m.dscr.isTextEllipsized
|
||||
createFullDscrDlg()
|
||||
return true
|
||||
end if
|
||||
return false
|
||||
end if
|
||||
|
||||
if key = "back"
|
||||
m.global.sceneManager.callfunc("popScene")
|
||||
return true
|
||||
end if
|
||||
|
||||
if key = "down"
|
||||
if m.dscr.hasFocus()
|
||||
m.favBtn.setFocus(true)
|
||||
m.dscr.opacity = 0.6
|
||||
m.top.findNode("dscrBorder").color = "#data202020ff"
|
||||
return true
|
||||
end if
|
||||
else if key = "up"
|
||||
if m.vidsList.isInFocusChain() and m.vidsList.itemFocused = 0
|
||||
m.top.findNode("VertSlider").reverse = true
|
||||
m.top.findNode("pplAnime").control = "start"
|
||||
m.favBtn.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
end if
|
||||
return false
|
||||
end function
|
||||
|
||||
sub setFavoriteColor()
|
||||
fave = m.top.itemContent.favorite
|
||||
fave_button = m.top.findNode("favorite-button")
|
||||
if fave <> invalid and fave
|
||||
fave_button.textColor = "#00ff00ff"
|
||||
fave_button.focusedTextColor = "#269926ff"
|
||||
else
|
||||
fave_button.textColor = "0xddddddff"
|
||||
fave_button.focusedTextColor = "#262626ff"
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub createFullDscrDlg()
|
||||
dlg = CreateObject("roSGNode", "OverviewDialog")
|
||||
dlg.Title = tr("Press 'OK' to Close")
|
||||
dlg.width = 1290
|
||||
dlg.palette = m.dlgPalette
|
||||
dlg.overview = [m.dscr.text]
|
||||
m.fullDscrDlg = dlg
|
||||
m.top.getScene().dialog = dlg
|
||||
border = createObject("roSGNode", "Poster")
|
||||
border.uri = "pkg:/images/hd_focul_9.png"
|
||||
border.blendColor = "#c9c9c9ff"
|
||||
border.width = dlg.width + 6
|
||||
border.height = dlg.height + 6
|
||||
border.translation = [dlg.translation[0] - 3, dlg.translation[1] - 3]
|
||||
border.visible = true
|
||||
end sub
|
||||
|
||||
sub createDialogPallete()
|
||||
m.dlgPalette = createObject("roSGNode", "RSGPalette")
|
||||
m.dlgPalette.colors = {
|
||||
DialogBackgroundColor: "0x262828FF",
|
||||
DialogItemColor: "0x00EF00FF",
|
||||
DialogTextColor: "0xb0b0b0FF",
|
||||
DialogFocusColor: "0xcececeFF",
|
||||
DialogFocusItemColor: "0x202020FF",
|
||||
DialogSecondaryTextColor: "0xf8f8f8ff",
|
||||
DialogSecondaryItemColor: "0xcc7ecc4D",
|
||||
DialogInputFieldColor: "0x80FF8080",
|
||||
DialogKeyboardColor: "0x80FF804D",
|
||||
DialogFootprintColor: "0x80FF804D"
|
||||
}
|
||||
end sub
|
||||
|
||||
function shortDate(isoDate) as string
|
||||
myDate = CreateObject("roDateTime")
|
||||
myDate.FromISO8601String(isoDate)
|
||||
return myDate.AsDateString("short-month-no-weekday")
|
||||
end function
|
45
components/PersonDetails.xml
Normal file
45
components/PersonDetails.xml
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="PersonDetails" extends="JFGroup">
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="loadPerson" />
|
||||
<field id="image" type="node" />
|
||||
<field id="selectedItem" type="node" alias="extrasGrid.selectedItem" alwaysNotify="true" />
|
||||
</interface>
|
||||
<children>
|
||||
<LayoutGroup id="main_group"
|
||||
layoutdirection="vert" translation="[60, 180]" itemSpacings="[36]">
|
||||
<LayoutGroup id="personInfoGroup"
|
||||
layoutDirection="horiz" itemSpacings="[36]">
|
||||
<Poster id="personImage"
|
||||
width="300" height="450" />
|
||||
<LayoutGroup id="vertSpacer" layoutDirection="vert" itemSpacings="[24]">
|
||||
<LayoutGroup id="dataGroup>" layoutDirection="vert"
|
||||
translation="[450,180]">
|
||||
<Label id="name" font="font:MediumBoldSystemFont" height="45" width="1200" />
|
||||
<label id="premierDate" font="font:SmallestBoldSystemFont" height="48" vertAlign="top" />
|
||||
<Rectangle id="dscrBorder"
|
||||
height="360" width="1422"
|
||||
color="0x202020ff">
|
||||
<Rectangle id='dscrRect' translation="[3, 3]"
|
||||
height="354" width="1416"
|
||||
color="0x202020ff">
|
||||
<Label id="description"
|
||||
height="342" width="1380" wrap="true" translation="[18, 15]"
|
||||
font="font:SmallestSystemFont" color="#e4e4e4ff" ellipsisText=" ... (-OK- for More)" />
|
||||
</Rectangle>
|
||||
</Rectangle>
|
||||
</LayoutGroup>
|
||||
<ButtonGroupHoriz id="buttons" >
|
||||
<Button id="favorite-button" text="Favorite"
|
||||
iconUri="" focusedIconUri="" />
|
||||
</ButtonGroupHoriz>
|
||||
</LayoutGroup>
|
||||
</LayoutGroup>
|
||||
</LayoutGroup>
|
||||
<extrasSlider id="personVideos" />
|
||||
</children>
|
||||
<script type="text/brightscript" uri="PersonDetails.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
||||
</component>
|
26
components/data/ExtrasData.xml
Normal file
26
components/data/ExtrasData.xml
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- The "ContentNode" for displaying the actual Extras Item -->
|
||||
<component name="ExtrasData" extends="ContentNode">
|
||||
<script type="text/brightscript">
|
||||
<![CDATA[
|
||||
sub setfields()
|
||||
datum = m.top.json
|
||||
m.top.id = datum.id
|
||||
m.top.subTitle = datum
|
||||
m.top.labelText = datum.Name
|
||||
end sub
|
||||
|
||||
sub setPoster()
|
||||
end sub
|
||||
]]>
|
||||
</script>
|
||||
<interface>
|
||||
<field id="image" type="node" />
|
||||
<field id="Type" type="string" />
|
||||
<field id="subTitle" type="string" />
|
||||
<field id="labelText" type="string" />
|
||||
<field id="posterUrl" type="string" />
|
||||
<field id="imageWidth" type="integer" value="234" />
|
||||
<field id="json" type="assocarray" />
|
||||
</interface>
|
||||
</component>
|
32
components/data/PersonData.brs
Normal file
32
components/data/PersonData.brs
Normal file
|
@ -0,0 +1,32 @@
|
|||
sub setFields()
|
||||
json = m.top.json
|
||||
m.top.id = json.id
|
||||
m.top.favorite = json.UserData.isFavorite
|
||||
m.top.Type = "Person"
|
||||
setPoster()
|
||||
end sub
|
||||
|
||||
sub setPoster()
|
||||
if m.top.image <> invalid
|
||||
m.top.posterURL = m.top.image.url
|
||||
else
|
||||
|
||||
if m.top.json.ImageTags.Primary <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
else if m.top.json.BackdropImageTags[0] <> invalid
|
||||
imgParams = { "maxHeight": 440, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
else if m.top.json.ParentThumbImageTag <> invalid and m.top.json.ParentThumbItemId <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ParentThumbImageTag }
|
||||
m.top.posterURL = ImageURL(m.top.json.ParentThumbItemId, "Thumb", imgParams)
|
||||
end if
|
||||
|
||||
' Add Backdrop Image
|
||||
if m.top.json.BackdropImageTags[0] <> invalid
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
end if
|
||||
end sub
|
12
components/data/PersonData.xml
Normal file
12
components/data/PersonData.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="PersonData" extends="JFContentItem">
|
||||
<interface>
|
||||
<field id="image" type="node" onChange="setPoster" />
|
||||
<field id="movieID" type="string" />
|
||||
<field id="overview" type="string" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="PersonData.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
||||
</component>
|
37
components/extras/ExtrasItem.xml
Normal file
37
components/extras/ExtrasItem.xml
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="ExtrasItem" extends="JFGroup">
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="showContent" />
|
||||
</interface>
|
||||
<script type="text/brightscript">
|
||||
<![CDATA[
|
||||
function init() as void
|
||||
m.posterImg = m.top.findNode("posterImg")
|
||||
m.name = m.top.findNode("pLabel")
|
||||
m.role = m.top.findNode("subTitle")
|
||||
end function
|
||||
|
||||
sub showContent()
|
||||
if m.top.itemContent <> Invalid
|
||||
cont = m.top.itemContent
|
||||
m.name.text = cont.labelText
|
||||
m.name.maxWidth = cont.imageWidth
|
||||
m.role.Width = cont.imageWidth
|
||||
m.posterImg.uri = cont.posterUrl
|
||||
m.posterImg.width = cont.imageWidth
|
||||
m.role.Text = cont.subTitle
|
||||
else
|
||||
m.role.text = "Who??"
|
||||
m.posterImg.uri = "pkg:/images/baseline_person_white_48dp.png"
|
||||
end if
|
||||
end sub
|
||||
]]>
|
||||
</script>
|
||||
<children>
|
||||
<LayoutGroup layoutDirection="vert" >
|
||||
<Poster id="posterImg" width="234" height="300" translation="[8,243]" failedBitmapUri="pkg:/images/baseline_person_white_48dp.png" />
|
||||
<ScrollingLabel id="pLabel" horizAlign="center" vertAlign="bottom" maxWidth="266" font="font:SmallestBoldSystemFont" height="48" />
|
||||
<Label id="SubTitle" horizAlign="center" vertAlign="center" font="font:SmallestBoldSystemFont" height="32" color="#A7A7A7FF" />
|
||||
</LayoutGroup>
|
||||
</children>
|
||||
</component>
|
187
components/extras/ExtrasRowList.brs
Normal file
187
components/extras/ExtrasRowList.brs
Normal file
|
@ -0,0 +1,187 @@
|
|||
sub init()
|
||||
m.top.visible = true
|
||||
updateSize()
|
||||
m.top.rowFocusAnimationStyle = "fixedFocus"
|
||||
m.top.observeField("rowItemSelected", "onRowItemSelected")
|
||||
|
||||
' Set up all Tasks
|
||||
m.LoadPeopleTask = CreateObject("roSGNode", "LoadItemsTask")
|
||||
m.LoadPeopleTask.itemsToLoad = "people"
|
||||
m.LoadPeopleTask.observeField("content", "onPeopleLoaded")
|
||||
m.LikeThisTask = CreateObject("roSGNode", "LoadItemsTask")
|
||||
m.LikeThisTask.itemsToLoad = "likethis"
|
||||
m.LikeThisTask.observeField("content", "onLikeThisLoaded")
|
||||
m.SpecialFeaturesTask = CreateObject("roSGNode", "LoadItemsTask")
|
||||
m.SpecialFeaturesTask.itemsToLoad = "specialfeatures"
|
||||
m.SpecialFeaturesTask.observeField("content", "onSpecialFeaturesLoaded")
|
||||
m.LoadMoviesTask = CreateObject("roSGNode", "LoadItemsTask")
|
||||
m.LoadMoviesTask.itemsToLoad = "personMovies"
|
||||
m.LoadShowsTask = CreateObject("roSGNode", "LoadItemsTask")
|
||||
m.LoadShowsTask.itemsToLoad = "personTVShows"
|
||||
m.LoadSeriesTask = CreateObject("roSGNode", "LoadItemsTask")
|
||||
m.LoadSeriesTask.itemsToLoad = "personSeries"
|
||||
end sub
|
||||
|
||||
sub updateSize()
|
||||
itemHeight = 396
|
||||
m.top.itemSize = [1710, itemHeight]
|
||||
m.top.rowItemSpacing = [36, 36]
|
||||
end sub
|
||||
|
||||
sub loadPeople(data as object)
|
||||
m.top.parentId = data.id
|
||||
m.LoadPeopleTask.peopleList = data.People
|
||||
m.LoadPeopleTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
sub loadPersonVideos(personId)
|
||||
m.personId = personId
|
||||
m.LoadMoviesTask.itemId = m.personId
|
||||
m.LoadMoviesTask.observeField("content", "onMoviesLoaded")
|
||||
m.LoadMoviesTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
sub onPeopleLoaded()
|
||||
people = m.LoadPeopleTask.content
|
||||
m.loadPeopleTask.unobserveField("content")
|
||||
data = CreateObject("roSGNode", "ContentNode") ' The row Node
|
||||
if people <> invalid and people.count() > 0
|
||||
row = data.createChild("ContentNode")
|
||||
row.Title = tr("Cast & Crew")
|
||||
for each person in people
|
||||
if person.json.type = "Actor"
|
||||
person.subTitle = "as " + person.json.Role
|
||||
else
|
||||
person.subTitle = person.json.Type
|
||||
end if
|
||||
person.Type = "Person"
|
||||
row.appendChild(person)
|
||||
end for
|
||||
end if
|
||||
m.top.content = data
|
||||
m.top.rowItemSize = [[234, 396]]
|
||||
m.LikeThisTask.itemId = m.top.parentId
|
||||
m.LikeThisTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
sub onLikeThisLoaded()
|
||||
data = m.LikeThisTask.content
|
||||
m.LikeThisTask.unobserveField("content")
|
||||
if data <> invalid and data.count() > 0
|
||||
row = m.top.content.createChild("ContentNode")
|
||||
row.Title = tr("More Like This")
|
||||
for each item in data
|
||||
item.Id = item.json.Id
|
||||
item.labelText = item.json.Name
|
||||
if item.json.ProductionYear <> invalid
|
||||
item.subTitle = stri(item.json.ProductionYear)
|
||||
else
|
||||
premierYear = CreateObject("roDateTime")
|
||||
premierYear.FromISO8601String(item.json.PremiereDate)
|
||||
item.subTitle = stri(premierYear.GetYear())
|
||||
end if
|
||||
item.Type = item.json.Type
|
||||
row.appendChild(item)
|
||||
end for
|
||||
addRowSize([234, 396])
|
||||
end if
|
||||
m.SpecialFeaturesTask.itemId = m.top.parentId
|
||||
m.SpecialFeaturesTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
function onSpecialFeaturesLoaded()
|
||||
data = m.SpecialFeaturesTask.content
|
||||
m.SpecialFeaturesTask.unobserveField("content")
|
||||
if data <> invalid and data.count() > 0
|
||||
row = m.top.content.createChild("ContentNode")
|
||||
row.Title = tr("Special Features")
|
||||
for each item in data
|
||||
m.top.visible = true
|
||||
item.Id = item.json.Id
|
||||
item.labelText = item.json.Name
|
||||
item.subTitle = ""
|
||||
item.Type = item.json.Type
|
||||
item.imageWidth = 450
|
||||
row.appendChild(item)
|
||||
end for
|
||||
addRowSize([462, 372])
|
||||
end if
|
||||
|
||||
return m.top.content
|
||||
end function
|
||||
|
||||
sub onMoviesLoaded()
|
||||
data = m.LoadMoviesTask.content
|
||||
m.LoadMoviesTask.unobserveField("content")
|
||||
rlContent = CreateObject("roSGNode", "ContentNode")
|
||||
if data <> invalid and data.count() > 0
|
||||
row = rlContent.createChild("ContentNode")
|
||||
row.title = tr("Movies")
|
||||
for each mov in data
|
||||
mov.Id = mov.json.Id
|
||||
mov.labelText = mov.json.Name
|
||||
mov.subTitle = mov.json.ProductionYear
|
||||
mov.Type = mov.json.Type
|
||||
row.appendChild(mov)
|
||||
end for
|
||||
m.top.rowItemSize = [[234, 396]]
|
||||
end if
|
||||
m.top.content = rlContent
|
||||
m.LoadShowsTask.itemId = m.personId
|
||||
m.LoadShowsTask.observeField("content", "onShowsLoaded")
|
||||
m.LoadShowsTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
sub onShowsLoaded()
|
||||
data = m.LoadShowsTask.content
|
||||
m.LoadShowsTask.unobserveField("content")
|
||||
if data <> invalid and data.count() > 0
|
||||
row = buildRow("TV Shows", data, 502)
|
||||
addRowSize([502, 396])
|
||||
m.top.content.appendChild(row)
|
||||
end if
|
||||
m.LoadSeriesTask.itemId = m.personId
|
||||
m.LoadSeriesTask.observeField("content", "onSeriesLoaded")
|
||||
m.LoadSeriesTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
sub onSeriesLoaded()
|
||||
data = m.LoadSeriesTask.content
|
||||
m.LoadSeriesTask.unobserveField("content")
|
||||
if data <> invalid and data.count() > 0
|
||||
row = buildRow("Series", data)
|
||||
addRowSize([234, 396])
|
||||
m.top.content.appendChild(row)
|
||||
end if
|
||||
m.top.visible = true
|
||||
end sub
|
||||
|
||||
function buildRow(rowTitle as string, items, imgWdth = 0)
|
||||
row = CreateObject("roSGNode", "ContentNode")
|
||||
row.Title = tr(rowTitle)
|
||||
for each mov in items
|
||||
mov.Id = mov.json.Id
|
||||
mov.labelText = mov.json.Name
|
||||
mov.subTitle = mov.json.ProductionYear
|
||||
mov.Type = mov.json.Type
|
||||
if imgWdth > 0
|
||||
mov.imageWidth = imgWdth
|
||||
end if
|
||||
row.appendChild(mov)
|
||||
end for
|
||||
return row
|
||||
end function
|
||||
|
||||
sub addRowSize(newRow)
|
||||
sizeArray = m.top.rowItemSize
|
||||
newSizeArray = []
|
||||
for each size in sizeArray
|
||||
newSizeArray.push(size)
|
||||
end for
|
||||
newSizeArray.push(newRow)
|
||||
m.top.rowItemSize = newSizeArray
|
||||
end sub
|
||||
|
||||
sub onRowItemSelected()
|
||||
m.top.selectedItem = m.top.content.getChild(m.top.rowItemSelected[0]).getChild(m.top.rowItemSelected[1])
|
||||
end sub
|
12
components/extras/ExtrasRowList.xml
Normal file
12
components/extras/ExtrasRowList.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- The top-Level RowList for the Extras rows -->
|
||||
<component name="ExtrasRowList" extends="RowList">
|
||||
<interface>
|
||||
<field id="type" type="string" />
|
||||
<field id="parentId" type="string" />
|
||||
<field id="selectedItem" type="node" alwaysNotify="true" />
|
||||
<function name="loadPeople" />
|
||||
<function name="loadPersonVideos" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="ExtrasRowList.brs" />
|
||||
</component>
|
28
components/extras/ExtrasSlider.xml
Normal file
28
components/extras/ExtrasSlider.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="ExtrasSlider" extends="JFGroup">
|
||||
<children>
|
||||
<Rectangle id="extrasGrp" color="#0A0010" opacity="0.3" width="1860" height="900" translation="[30, 1014]" >
|
||||
<ExtrasRowList id="extrasGrid"
|
||||
itemSpacing="[ 60, 132]"
|
||||
rowLabelOffset="[ [9, 24] ]"
|
||||
numRows="2"
|
||||
showRowLabel="true"
|
||||
showRowCounter="true"
|
||||
visible="true"
|
||||
translation="[12,18]"
|
||||
itemComponentName="ExtrasItem" />
|
||||
<Animation id="pplAnime" duration=".4" repeat="false" >
|
||||
<Vector2DFieldInterpolator
|
||||
id="VertSlider"
|
||||
key="[0.0, 0.25, 0.50, 75.0, 1.0]"
|
||||
keyValue="[[30, 1014], [30, 804], [30, 588], [30,375 ], [30, 162]]"
|
||||
fieldToInterp="extrasGrp.translation" />
|
||||
<FloatFieldInterpolator
|
||||
id="extrasFader"
|
||||
key="[0.0, 0.25, 0.50, 75.0, 1.0]"
|
||||
keyValue="[0.2, 0.4, 0.6, 0.8, 1.0]"
|
||||
fieldToInterp="extrasGrp.opacity" />
|
||||
</Animation>
|
||||
</Rectangle>
|
||||
</children>
|
||||
</component>
|
|
@ -100,8 +100,74 @@ sub loadItems()
|
|||
tmp.json = item
|
||||
results.push(tmp)
|
||||
end for
|
||||
|
||||
' Extract array of persons from Views and download full metadata for each
|
||||
else if m.top.itemsToLoad = "people"
|
||||
for each person in m.top.peopleList
|
||||
tmp = CreateObject("roSGNode", "ExtrasData")
|
||||
tmp.Id = person.Id
|
||||
tmp.labelText = person.Name
|
||||
params = {}
|
||||
params["Tags"] = person.PrimaryImageTag
|
||||
params["MaxWidth"] = 234
|
||||
params["MaxHeight"] = 330
|
||||
tmp.posterURL = ImageUrl(person.Id, "Primary", params)
|
||||
tmp.json = person
|
||||
results.push(tmp)
|
||||
end for
|
||||
else if m.top.itemsToLoad = "specialfeatures"
|
||||
params = {}
|
||||
url = Substitute("Users/{0}/Items/{1}/SpecialFeatures", get_setting("active_user"), m.top.itemId)
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
if data <> invalid and data.count() > 0
|
||||
for each specfeat in data
|
||||
tmp = CreateObject("roSGNode", "ExtrasData")
|
||||
results.push(tmp)
|
||||
params = {}
|
||||
params["Tags"] = specfeat.ImageTags.Primary
|
||||
params["MaxWidth"] = 450
|
||||
params["MaxHeight"] = 402
|
||||
tmp.posterURL = ImageUrl(specfeat.Id, "Primary", params)
|
||||
tmp.json = specfeat
|
||||
end for
|
||||
end if
|
||||
else if m.top.itemsToLoad = "likethis"
|
||||
params = { "userId": get_setting("active_user"), "limit": 16 }
|
||||
url = Substitute("Items/{0}/Similar", m.top.itemId)
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
for each item in data.items
|
||||
tmp = CreateObject("roSGNode", "ExtrasData")
|
||||
tmp.posterURL = ImageUrl(item.Id, "Primary", { "Tags": item.PrimaryImageTag })
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
end for
|
||||
else if m.top.itemsToLoad = "personMovies"
|
||||
getPersonVideos("Movie", results, {})
|
||||
else if m.top.itemsToLoad = "personTVShows"
|
||||
getPersonVideos("Episode", results, { MaxWidth: 502, MaxHeight: 300 })
|
||||
else if m.top.itemsToLoad = "personSeries"
|
||||
getPersonVideos("Series", results, {})
|
||||
end if
|
||||
|
||||
m.top.content = results
|
||||
|
||||
end sub
|
||||
|
||||
sub getPersonVideos(videoType, dest, dimens)
|
||||
params = { personIds: m.top.itemId, recursive: true, includeItemTypes: videoType, Limit: 50, SortBy: "Random" }
|
||||
url = Substitute("Users/{0}/Items", get_setting("active_user"))
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
if data <> invalid and data.count() > 0
|
||||
for each item in data.items
|
||||
tmp = CreateObject("roSGNode", "ExtrasData")
|
||||
imgParms = { "Tags": item.ImageTags.Primary }
|
||||
imgParms.append(dimens)
|
||||
tmp.posterURL = ImageUrl(item.Id, "Primary", imgParms)
|
||||
tmp.json = item
|
||||
dest.push(tmp)
|
||||
end for
|
||||
end if
|
||||
end sub
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<field id="itemsToLoad" type="string" value="libraries" />
|
||||
<field id="itemId" type="string" />
|
||||
<field id="metadata" type="assocarray" />
|
||||
<field id="peopleList" type="array" />
|
||||
<field id="content" type="array" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="LoadItemsTask.brs" />
|
||||
|
@ -13,4 +14,4 @@
|
|||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/deviceCapabilities.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
|
||||
</component>
|
||||
</component>
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
sub init()
|
||||
m.extrasGrp = m.top.findnode("extrasGrp")
|
||||
m.extrasGrid = m.top.findNode("extrasGrid")
|
||||
m.top.optionsAvailable = false
|
||||
m.main_group = m.top.findNode("main_group")
|
||||
m.options = m.top.findNode("options")
|
||||
|
||||
main = m.top.findNode("main_group")
|
||||
main.translation = [96, 175]
|
||||
|
||||
overview = m.top.findNode("overview")
|
||||
overview.width = 1920 - 96 - 300 - 96 - 30
|
||||
|
||||
m.top.findNode("buttons").setFocus(true)
|
||||
m.buttonGrp = m.top.findNode("buttons")
|
||||
m.buttonGrp.setFocus(true)
|
||||
end sub
|
||||
|
||||
sub itemContentChanged()
|
||||
|
@ -85,6 +86,7 @@ sub itemContentChanged()
|
|||
if itemData.taglines.count() > 0
|
||||
setFieldText("tagline", itemData.taglines[0])
|
||||
end if
|
||||
|
||||
setFavoriteColor()
|
||||
setWatchedColor()
|
||||
SetUpOptions(itemData.mediaStreams)
|
||||
|
@ -125,6 +127,7 @@ sub setFieldText(field, 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,
|
||||
|
@ -201,6 +204,24 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
m.options.setFocus(true)
|
||||
end if
|
||||
|
||||
if key = "down" and m.buttonGrp.isInFocusChain()
|
||||
m.extrasGrid.setFocus(true)
|
||||
m.top.findNode("VertSlider").reverse = false
|
||||
m.top.findNode("extrasFader").reverse = false
|
||||
m.top.findNode("pplAnime").control = "start"
|
||||
return true
|
||||
end if
|
||||
|
||||
if key = "up" and m.top.findNode("extrasGrid").isInFocusChain()
|
||||
if m.extrasGrid.itemFocused = 0
|
||||
m.top.findNode("VertSlider").reverse = true
|
||||
m.top.findNode("extrasFader").reverse = true
|
||||
m.top.findNode("pplAnime").control = "start"
|
||||
m.buttonGrp.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
end if
|
||||
|
||||
if not press then return false
|
||||
|
||||
if key = "options"
|
||||
|
@ -220,4 +241,4 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
end if
|
||||
end if
|
||||
return false
|
||||
end function
|
||||
end function
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
<Label id="communityRating" />
|
||||
</LayoutGroup>
|
||||
<LayoutGroup layoutDirection="horiz" itemSpacings="[-5]" id="criticRatingGroup">
|
||||
<Poster id="criticRatingIcon" height="32" width="32" />
|
||||
<Label id="criticRatingLabel" />
|
||||
</LayoutGroup>
|
||||
<Poster id="criticRatingIcon" height="32" width="32" />
|
||||
<Label id="criticRatingLabel" />
|
||||
</LayoutGroup>
|
||||
<Label id="ends-at" />
|
||||
</LayoutGroup>
|
||||
<Label id="genres" />
|
||||
|
@ -32,9 +32,11 @@
|
|||
</ButtonGroupHoriz>
|
||||
<Label id="tagline" />
|
||||
<Label id="overview" wrap="true" maxLines="8" />
|
||||
|
||||
</LayoutGroup>
|
||||
</LayoutGroup>
|
||||
|
||||
<!-- "Cast and Crew" row -->
|
||||
<extrasSlider id="movieExtras" />
|
||||
<MovieOptions id="options" visible="false" />
|
||||
</children>
|
||||
<interface>
|
||||
|
|
|
@ -2,6 +2,9 @@ sub init()
|
|||
m.top.optionsAvailable = false
|
||||
main = m.top.findNode("toplevel")
|
||||
main.translation = [96, 175]
|
||||
m.extrasSlider = m.top.findNode("tvSeasonExtras")
|
||||
'm.extrasSlider.translation = [30,1014]
|
||||
m.extrasSlider.visible = true
|
||||
end sub
|
||||
|
||||
sub itemContentChanged()
|
||||
|
@ -130,3 +133,26 @@ function round(f as float) as integer
|
|||
return n
|
||||
end if
|
||||
end function
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
topGrp = m.top.findNode("seasons")
|
||||
bottomGrp = m.top.findNode("extrasGrid")
|
||||
|
||||
|
||||
if key = "down" and topGrp.isinFocusChain()
|
||||
bottomGrp.setFocus(true)
|
||||
m.top.findNode("VertSlider").reverse = false
|
||||
m.top.findNode("extrasFader").reverse = false
|
||||
m.top.findNode("pplAnime").control = "start"
|
||||
return true
|
||||
else if key = "up" and bottomGrp.isinFocusChain()
|
||||
if bottomGrp.itemFocused = 0
|
||||
m.top.findNode("VertSlider").reverse = true
|
||||
m.top.findNode("extrasFader").reverse = true
|
||||
m.top.findNode("pplAnime").control = "start"
|
||||
topGrp.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
end if
|
||||
return false
|
||||
end function
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
</LayoutGroup>
|
||||
<TVSeasonRow id="seasons" />
|
||||
</LayoutGroup>
|
||||
<ExtrasSlider id="tvSeasonExtras" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="itemContentChanged" />
|
||||
|
|
|
@ -261,6 +261,42 @@
|
|||
<source>TAB_FILTER</source>
|
||||
<translation>Filter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Born</source>
|
||||
<translation>Born</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Died</source>
|
||||
<translation>Died</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Age</source>
|
||||
<translation>Age</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cast & Crew</source>
|
||||
<translation>Cast & Crew</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>More Like This</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special Features</source>
|
||||
<translation>Special Features</translation>
|
||||
<message>
|
||||
<source>Press 'OK' to Close</source>
|
||||
<translation>Press 'OK' to Close</translation>
|
||||
</message>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies</source>
|
||||
<translation>Movies</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Shows</source>
|
||||
<translation>TV Shows</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>today</translation>
|
||||
|
@ -381,6 +417,10 @@
|
|||
<source>Cancel Series Recording</source>
|
||||
<translation>Cancel Series Recording</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Close</source>
|
||||
<translation>Close</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Connecting to Server</translation>
|
||||
|
|
6761
package-lock.json
generated
6761
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -9,7 +9,6 @@
|
|||
"devDependencies": {
|
||||
"@rokucommunity/bslint": "^0.4.0",
|
||||
"brighterscript": "^0.39.4",
|
||||
"brighterscript-formatter": "^1.6.1",
|
||||
"rooibos-cli": "^1.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -32,5 +31,8 @@
|
|||
"bugs": {
|
||||
"url": "https://github.com/jellyfin/jellyfin-roku/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jellyfin/jellyfin-roku#readme"
|
||||
"homepage": "https://github.com/jellyfin/jellyfin-roku#readme",
|
||||
"dependencies": {
|
||||
"brighterscript-formatter": "^1.6.8"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,6 +112,8 @@ sub Main (args as dynamic) as void
|
|||
else if selectedItem.type = "Movie"
|
||||
' open movie detail page
|
||||
group = CreateMovieDetailsGroup(selectedItem)
|
||||
else if selectedItem.type = "Person"
|
||||
CreatePersonView(selectedItem)
|
||||
else if selectedItem.type = "TvChannel" or selectedItem.type = "Video" or selectedItem.type = "Program"
|
||||
' play channel feed
|
||||
video_id = selectedItem.id
|
||||
|
@ -283,6 +285,12 @@ sub Main (args as dynamic) as void
|
|||
autoPlayNextEpisode(node.id, node.showID)
|
||||
end if
|
||||
end if
|
||||
'else if isNodeEvent(msg, "selectedExtra")
|
||||
'rl = msg.getData()
|
||||
'sel = rl.rowItemSelected
|
||||
'? "msg.getfield():" + msg.getField()
|
||||
'stop
|
||||
'CreatePersonView(msg.getData())
|
||||
else if type(msg) = "roDeviceInfoEvent"
|
||||
event = msg.GetInfo()
|
||||
group = sceneManager.callFunc("getActiveScene")
|
||||
|
|
|
@ -306,6 +306,10 @@ function CreateMovieDetailsGroup(movie)
|
|||
b.observeField("buttonSelected", m.port)
|
||||
end for
|
||||
|
||||
extras = group.findNode("extrasGrid")
|
||||
extras.observeField("selectedItem", m.port)
|
||||
extras.callFunc("loadPeople", movie.json)
|
||||
|
||||
return group
|
||||
end function
|
||||
|
||||
|
@ -320,6 +324,10 @@ function CreateSeriesDetailsGroup(series)
|
|||
|
||||
group.observeField("seasonSelected", m.port)
|
||||
|
||||
extras = group.findNode("extrasGrid")
|
||||
extras.observeField("selectedItem", m.port)
|
||||
extras.callFunc("loadPeople", group.itemcontent.json)
|
||||
|
||||
return group
|
||||
end function
|
||||
|
||||
|
@ -375,6 +383,31 @@ function CreateVideoPlayerGroup(video_id, audio_stream_idx = 1)
|
|||
return video
|
||||
end function
|
||||
|
||||
function CreatePersonView(personData as object) as object
|
||||
person = CreateObject("roSGNode", "PersonDetails")
|
||||
m.global.SceneManager.callFunc("pushScene", person)
|
||||
|
||||
info = ItemMetaData(personData.id)
|
||||
person.itemContent = info
|
||||
|
||||
person.setFocus(true)
|
||||
person.observeField("selectedItem", m.port)
|
||||
person.findNode("favorite-button").observeField("buttonSelected", m.port)
|
||||
|
||||
return person
|
||||
end function
|
||||
|
||||
function CreatePhotoPage(photo)
|
||||
group = CreateObject("roSGNode", "PhotoDetails")
|
||||
group.optionsAvailable = true
|
||||
m.global.sceneManager.callFunc("pushScene", group)
|
||||
|
||||
group.itemContent = photo
|
||||
|
||||
return group
|
||||
|
||||
end function
|
||||
|
||||
sub UpdateSavedServerList()
|
||||
server = get_setting("server")
|
||||
username = get_setting("username")
|
||||
|
|
|
@ -112,6 +112,11 @@ function ItemMetaData(id as string)
|
|||
tmp.image = PosterImage(data.id)
|
||||
tmp.json = data
|
||||
return tmp
|
||||
else if data.type = "Person"
|
||||
tmp = CreateObject("roSGNode", "PersonData")
|
||||
tmp.image = PosterImage(data.id, { "MaxWidth": 300, "MaxHeight": 450 })
|
||||
tmp.json = data
|
||||
return tmp
|
||||
else
|
||||
print "Items.brs::ItemMetaData processed unhandled type: " data.type
|
||||
' Return json if we don't know what it is
|
||||
|
|
Loading…
Reference in New Issue
Block a user