Update Artist view

This commit is contained in:
1hitsong 2022-07-18 22:28:06 -04:00
parent 78a9e4e1c4
commit af4a9aabeb
14 changed files with 331 additions and 71 deletions

79
components/IconButton.brs Normal file
View File

@ -0,0 +1,79 @@
sub init()
m.buttonBackground = m.top.findNode("buttonBackground")
m.buttonIcon = m.top.findNode("buttonIcon")
m.buttonText = m.top.findNode("buttonText")
m.top.observeField("background", "onBackgroundChanged")
m.top.observeField("icon", "onIconChanged")
m.top.observeField("text", "onTextChanged")
m.top.observeField("height", "onHeightChanged")
m.top.observeField("width", "onWidthChanged")
m.top.observeField("padding", "onPaddingChanged")
m.top.observeField("focusedChild", "onFocusChanged")
end sub
sub onFocusChanged()
if m.top.hasFocus()
m.buttonBackground.blendColor = m.top.focusBackground
else
m.buttonBackground.blendColor = m.top.background
end if
end sub
sub onBackgroundChanged()
m.buttonBackground.blendColor = m.top.background
m.top.unobserveField("background")
end sub
sub onIconChanged()
m.buttonIcon.uri = m.top.icon
end sub
sub onTextChanged()
m.buttonText.text = m.top.text
end sub
sub setIconSize()
height = m.buttonBackground.height
width = m.buttonBackground.width
if height > 0 and width > 0
' TODO: Use smallest number between them
m.buttonIcon.height = m.top.height
if m.top.padding > 0
m.buttonIcon.height = m.buttonIcon.height - m.top.padding
end if
m.buttonIcon.width = m.buttonIcon.height
m.buttonIcon.translation = [((width - m.buttonIcon.width) / 2), ((height - m.buttonIcon.height) / 2)]
m.buttonText.translation = [0, height + 10]
m.buttonText.width = width
end if
end sub
sub onHeightChanged()
m.buttonBackground.height = m.top.height
setIconSize()
end sub
sub onWidthChanged()
m.buttonBackground.width = m.top.width
setIconSize()
end sub
sub onPaddingChanged()
setIconSize()
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
if key = "OK" and m.top.hasFocus()
' Simply toggle the selected field to trigger the next event
m.top.selected = not m.top.selected
return true
end if
return false
end function

19
components/IconButton.xml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="IconButton" extends="Group">
<children>
<Poster id="buttonBackground" uri="pkg:/images/white.9.png" />
<Poster id="buttonIcon" />
<Label id="buttonText" color="#aaaaaa" font="font:SmallestSystemFont" horizAlign="center" />
</children>
<interface>
<field id="background" type="color" value="" />
<field id="focusBackground" type="color" value="" />
<field id="text" type="string" value="" />
<field id="padding" type="integer" value="-1" />
<field id="height" type="integer" value="" />
<field id="width" type="integer" value="" />
<field id="icon" type="string" value="" />
<field id="selected" type="boolean" value="false" />
</interface>
<script type="text/brightscript" uri="IconButton.brs" />
</component>

View File

@ -0,0 +1,44 @@
sub init()
getData()
m.top.infocus = false
end sub
function getData()
' If we have no album data, return a blank node
if m.top.MusicArtistAlbumData = invalid
data = CreateObject("roSGNode", "ContentNode")
return data
end if
albumData = m.top.MusicArtistAlbumData
data = CreateObject("roSGNode", "ContentNode")
for each album in albumData.items
gridAlbum = CreateObject("roSGNode", "ContentNode")
gridAlbum.shortdescriptionline1 = album.title
gridAlbum.HDGRIDPOSTERURL = album.posterURL
gridAlbum.hdposterurl = album.posterURL
gridAlbum.SDGRIDPOSTERURL = album.SDGRIDPOSTERURL
gridAlbum.sdposterurl = album.posterURL
data.appendChild(gridAlbum)
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
if key = "up"
if m.top.itemFocused <= 4
m.top.infocus = false
return true
end if
end if
return false
end function

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<<<<<<< unstable:components/music/AlbumRow.xml
<<<<<<< unstable:components/music/MusicAlbumSongList.xml
<component name="MusicAlbumSongList" extends="MarkupList">
=======
@ -7,6 +8,12 @@
<interface>
<field id="MusicArtistAlbumData" type="assocarray" onChange="getData" />
<field id="doneLoading" type="boolean" value="false"/>
=======
<component name="AlbumGrid" extends="PosterGrid">
<interface>
<field id="MusicArtistAlbumData" type="assocarray" onChange="getData" />
<field id="infocus" type="boolean" />
>>>>>>> Update Artist view:components/music/AlbumGrid.xml
</interface>
<script type="text/brightscript" uri="AlbumRow.brs" />
<script type="text/brightscript" uri="AlbumGrid.brs" />
</component>

View File

@ -1,50 +0,0 @@
sub init()
m.top.itemComponentName = "ListPoster"
m.top.rowFocusAnimationStyle = "fixedFocusWrap"
m.top.showRowLabel = [false]
m.top.showRowCounter = [true]
getData()
updateSize()
m.top.setfocus(true)
end sub
sub updateSize()
itemWidth = 250
itemHeight = 250
m.top.visible = true
' size of the whole row
m.top.itemSize = [1650, 290]
' spacing between rows
m.top.itemSpacing = [0, 0]
' size of the item in the row
m.top.rowItemSize = [itemWidth, itemHeight]
' spacing between items in a row
m.top.rowItemSpacing = [10, 0]
end sub
function getData()
' If we have no album data, return a blank node
if m.top.MusicArtistAlbumData = invalid
data = CreateObject("roSGNode", "ContentNode")
return data
end if
albumData = m.top.MusicArtistAlbumData
data = CreateObject("roSGNode", "ContentNode")
row = data.CreateChild("ContentNode")
for each album in albumData.items
row.appendChild(album)
end for
m.top.content = data
return data
end function

View File

@ -1,6 +1,19 @@
sub init()
m.top.optionsAvailable = false
setupMainNode()
setupButtons()
m.albumHeader = m.top.findNode("albumHeader")
m.albumHeader.text = tr("Albums")
m.albums = m.top.findNode("albums")
m.albums.observeField("infocus", "onAlbumFocusChange")
m.pageLoadAnimation = m.top.findNode("pageLoad")
m.pageLoadAnimation.control = "start"
m.showAlbumsAnimation = m.top.findNode("showAlbums")
m.hideAlbumsAnimation = m.top.findNode("hideAlbums")
' Load background image
m.LoadBackdropImageTask = CreateObject("roSGNode", "LoadItemsTask")
@ -13,9 +26,34 @@ sub init()
createDialogPallete()
end sub
' Setup playback buttons, default to Play button selected
sub setupButtons()
m.buttonGrp = m.top.findNode("buttons")
m.buttonCount = m.buttonGrp.getChildCount()
m.playButton = m.top.findNode("play")
m.previouslySelectedButtonIndex = -1
m.top.observeField("selectedButtonIndex", "onButtonSelectedChange")
m.top.selectedButtonIndex = 0
end sub
' Event handler when user selected a different playback button
sub onButtonSelectedChange()
' Change previously selected button back to default image
if m.previouslySelectedButtonIndex > -1
selectedButton = m.buttonGrp.getChild(m.previouslySelectedButtonIndex)
selectedButton.setFocus(false)
end if
' Change selected button image to selected image
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.setFocus(true)
end sub
sub setupMainNode()
main = m.top.findNode("toplevel")
main.translation = [96, 175]
m.main = m.top.findNode("toplevel")
m.main.translation = [96, 175]
end sub
' Event fired when page data is loaded
@ -40,9 +78,11 @@ sub setScreenTitle(json)
end sub
sub setPosterImage(posterURL)
if isValid(posterURL)
m.artistImage.uri = posterURL
if not isValid(posterURL) or posterURL = ""
posterURL = "pkg:/images/missingArtist.png"
end if
m.artistImage.uri = posterURL
end sub
sub onBackdropImageLoaded()
@ -75,6 +115,21 @@ sub onEllipsisChanged()
end if
end sub
sub onAlbumFocusChange()
if m.albums.infocus
m.albums.setFocus(true)
m.showAlbumsAnimation.control = "start"
return
end if
' Change selected button image to selected image
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.setFocus(true)
m.albums.setFocus(false)
m.hideAlbumsAnimation.control = "start"
end sub
sub dscrShowFocus()
if m.dscr.isTextEllipsized
m.dscr.setFocus(true)
@ -116,6 +171,28 @@ sub createDialogPallete()
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if m.buttonGrp.isInFocusChain()
if key = "down"
m.albums.infocus = true
return true
else if key = "left"
if m.top.pageContent.count() = 1 then return false
if m.top.selectedButtonIndex > 0
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
m.top.selectedButtonIndex = m.top.selectedButtonIndex - 1
end if
return true
else if key = "right"
if m.top.pageContent.count() = 1 then return false
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
if m.top.selectedButtonIndex < m.buttonCount - 1 then m.top.selectedButtonIndex = m.top.selectedButtonIndex + 1
return true
end if
end if
if not press then return false
if key = "options"

View File

@ -2,20 +2,50 @@
<component name="ArtistView" extends="JFGroup">
<children>
<Poster id="backdrop" opacity=".4" loadDisplayMode="scaleToZoom" width="1920" height="1200" blendColor="#3f3f3f" />
<LayoutGroup id="toplevel" layoutDirection="vert" itemSpacings="[35]" >
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[15]" >
<Poster id="artistImage" width="450" height="450" />
<Label id="overview" wrap="true" maxLines="9" width="1250" ellipsisText=" ... (Press * to read more)" />
<LayoutGroup id="toplevel" layoutDirection="vert" itemSpacings="[75]" >
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[125]" >
<LayoutGroup layoutDirection="vert" itemSpacings="[75]" >
<Label id="overview" wrap="true" lineSpacing="25" maxLines="6" width="1100" ellipsisText=" ... (Press * to read more)" />
<ButtonGroupHoriz id="buttons" itemSpacings="[20]">
<IconButton id="play" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/play.png" text="Play" height="85" width="150" />
<IconButton id="instantMix" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/instantMix.png" text="Instant Mix" height="85" width="150" />
</ButtonGroupHoriz>
</LayoutGroup>
<Rectangle id='albumRect' translation="[0, 0]" width="1720" height="325" color="0x00000066">
<AlbumRow id="albums" translation="[35, 10]" rowLabelColor="#999999" rowLabelFont="font:SmallestSystemFont" />
</Rectangle>
<Poster id="artistImage" width="500" height="500" />
</LayoutGroup>
</LayoutGroup>
<Rectangle id='albumRect' translation="[0, 1050]" width="1920" height="1080" color="#000000" opacity=".75" />
<Label id="albumHeader" translation="[100, 1100]" font="font:LargeSystemFont" />
<AlbumGrid id="albums" translation="[100, 1200]" vertFocusAnimationStyle="fixedFocus" basePosterSize="[300, 300]" numColumns="5" numRows="99" caption1NumLines="1" itemSpacing="[50, 50]" />
<Animation id="pageLoad" duration=".5" repeat="false">
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[0, 1050], [0, 750]]" fieldToInterp="albumRect.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[100, 1100], [100, 800]]" fieldToInterp="albumHeader.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[100, 1200], [100, 900]]" fieldToInterp="albums.translation" />
</Animation>
<Animation id="showAlbums" duration="0.5" repeat="false">
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[0, 750], [0, 0]]" fieldToInterp="albumRect.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[100, 800], [100, 175]]" fieldToInterp="albumHeader.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[100, 900], [100, 275]]" fieldToInterp="albums.translation" />
<FloatFieldInterpolator key="[0.0, 1.0]" keyValue="[0.75, 0.95]" fieldToInterp="albumRect.opacity" />
</Animation>
<Animation id="hideAlbums" duration="0.5" repeat="false">
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[0, 0], [0, 750]]" fieldToInterp="albumRect.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[100, 175], [100, 800]]" fieldToInterp="albumHeader.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[100, 275], [100, 900]]" fieldToInterp="albums.translation" />
<FloatFieldInterpolator key="[0.0, 1.0]" keyValue="[0.95, 0.75]" fieldToInterp="albumRect.opacity" />
</Animation>
</children>
<interface>
<field id="pageContent" type="node" onChange="pageContentChanged" />
<field id="musicArtistAlbumData" type="assocarray" alias="albums.MusicArtistAlbumData" />
<field id="musicAlbumSelected" alias="albums.rowItemSelected" />
<field id="musicAlbumSelected" alias="albums.itemSelected" />
<field id="playArtistSelected" alias="play.selected" />
<field id="instantMixSelected" alias="instantMix.selected" />
<field id="selectedButtonIndex" type="integer" value="-1" />
</interface>
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
<script type="text/brightscript" uri="ArtistView.brs" />

View File

@ -382,9 +382,11 @@ sub onMetaDataLoaded()
if data <> invalid and data.count() > 0
' Use metadata to load backdrop image
if isvalid(data?.json?.ArtistItems?[0].id)
m.LoadBackdropImageTask.itemId = data.json.ArtistItems[0].id
m.LoadBackdropImageTask.observeField("content", "onBackdropImageLoaded")
m.LoadBackdropImageTask.control = "RUN"
end if
setPosterImage(data.posterURL)
setScreenTitle(data.json)

BIN
images/icons/instantMix.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
images/icons/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/missingArtist.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -186,9 +186,8 @@ sub Main (args as dynamic) as void
else if isNodeEvent(msg, "musicAlbumSelected")
' If you select a Music Album from ANYWHERE, follow this flow
ptr = msg.getData()
' ptr is for [row, col] of selected item... but we only have 1 row
albums = msg.getRoSGNode()
node = albums.musicArtistAlbumData.items[ptr[1]]
node = albums.musicArtistAlbumData.items[ptr]
group = CreateAlbumView(node)
else if isNodeEvent(msg, "playSong")
' User has selected audio they want us to play
@ -201,13 +200,24 @@ sub Main (args as dynamic) as void
m.spinner = screenContent.findNode("spinner")
m.spinner.visible = true
group = CreateAudioPlayerGroup(screenContent.albumData.items)
else if isNodeEvent(msg, "playArtistSelected")
' User has selected playlist of of audio they want us to play
screenContent = msg.getRoSGNode()
group = CreateArtistMixGroup(screenContent.pageContent.id)
else if isNodeEvent(msg, "instantMixSelected")
' User has selected instant mix
' User has selected playlist of of audio they want us to play
screenContent = msg.getRoSGNode()
m.spinner = screenContent.findNode("spinner")
if isValid(m.spinner)
m.spinner.visible = true
end if
if isValid(screenContent.albumData)
group = CreateInstantMixGroup(screenContent.albumData.items)
else if isValid(screenContent.pageContent)
print screenContent.musicArtistAlbumData.items[0].json
group = CreateInstantMixGroup([{ id: screenContent.musicArtistAlbumData.items[0].json.id }])
end if
else if isNodeEvent(msg, "episodeSelected")
' If you select a TV Episode from ANYWHERE, follow this flow
node = getMsgPicker(msg, "picker")

View File

@ -387,6 +387,8 @@ function CreateArtistView(musicartist)
group.pageContent = ItemMetaData(musicartist.id)
group.musicArtistAlbumData = musicData
group.observeField("musicAlbumSelected", m.port)
group.observeField("playArtistSelected", m.port)
group.observeField("instantMixSelected", m.port)
end if
m.global.sceneManager.callFunc("pushScene", group)
@ -512,6 +514,30 @@ function CreateInstantMixGroup(audiodata)
return group
end function
' Play Artist
function CreateArtistMixGroup(artistID)
songList = CreateArtistMix(artistID)
group = CreateObject("roSGNode", "NowPlaying")
group.observeField("state", m.port)
songIDArray = CreateObject("roArray", 0, true)
' All we need is an array of Song IDs the user selected to play.
for each song in songList.items
songIDArray.push(song.id)
end for
songIDArray.shift()
group.pageContent = songIDArray
group.musicArtistAlbumData = songList.items
m.global.sceneManager.callFunc("pushScene", group)
return group
end function
function CreatePersonView(personData as object) as object
person = CreateObject("roSGNode", "PersonDetails")
m.global.SceneManager.callFunc("pushScene", person)

View File

@ -167,7 +167,7 @@ function MusicAlbumList(id as string)
results = []
for each item in data.Items
tmp = CreateObject("roSGNode", "MusicAlbumData")
tmp.image = PosterImage(item.id)
tmp.image = PosterImage(item.id, { "maxHeight": "500", "maxWidth": "500" })
tmp.json = item
results.push(tmp)
end for
@ -220,6 +220,22 @@ function CreateInstantMix(id as string)
return getJson(resp)
end function
' Get Instant Mix based on item
function CreateArtistMix(id as string)
url = Substitute("Users/{0}/Items", get_setting("active_user"), id)
resp = APIRequest(url, {
"UserId": get_setting("active_user"),
"parentId": id,
"Filters": "IsNotFolder",
"Recursive": true,
"SortBy": "SortName",
"MediaTypes": "Audio",
"Limit": 300
})
return getJson(resp)
end function
' Get Intro Videos for an item
function GetIntroVideos(id as string)
url = Substitute("Users/{0}/Items/{1}/Intros", get_setting("active_user"), id)