Merge pull request #811 from 1hitsong/music-appears-on

Add appears on music artist section
This commit is contained in:
1hitsong 2022-10-07 08:05:52 -04:00 committed by GitHub
commit b5293c5773
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 357 additions and 104 deletions

View File

@ -69,17 +69,11 @@ 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
if key = "right" and m.top.hasFocus()
if key = "right" and m.top.focus
m.top.escape = "right"
end if
if key = "left" and m.top.hasFocus()
if key = "left" and m.top.focus
m.top.escape = "left"
end if

View File

@ -80,6 +80,20 @@ sub itemContentChanged()
m.backdrop.height = 290
m.backdrop.width = 290
m.posterText.height = 200
m.posterText.width = 280
else if itemData.json.type = "MusicAlbum"
m.itemPoster.uri = itemData.PosterUrl
m.itemText.text = itemData.Title
m.itemPoster.height = 290
m.itemPoster.width = 290
m.itemText.translation = [0, m.itemPoster.height + 7]
m.backdrop.height = 290
m.backdrop.width = 290
m.posterText.height = 200
m.posterText.width = 280
else

View File

@ -163,25 +163,16 @@ sub loadInitialItems()
m.loadItemsTask.itemId = m.top.parentItem.Id
else if getCollectionType() = "music"
' Default Settings
m.loadItemsTask.recursive = true
m.itemGrid.itemSize = "[290, 290]"
if m.voiceBox.text <> ""
m.loadItemsTask.recursive = true
else
m.loadItemsTask.recursive = false
m.itemGrid.itemSize = "[290, 290]"
end if
m.loadItemsTask.itemType = "MusicArtist,MusicAlbum"
m.loadItemsTask.itemType = "MusicArtist"
m.loadItemsTask.itemId = m.top.parentItem.Id
m.view = get_user_setting("display.music.view")
if m.view = "music-artist"
m.loadItemsTask.recursive = true
m.loadItemsTask.itemType = "MusicArtist"
else if m.view = "music-album"
if m.view = "music-album"
m.loadItemsTask.itemType = "MusicAlbum"
m.loadItemsTask.recursive = true
end if
else if m.top.parentItem.collectionType = "livetv"
m.loadItemsTask.itemType = "TvChannel"
@ -312,7 +303,6 @@ end sub
' Set Music view, sort, and filter options
sub setMusicOptions(options)
options.views = [
{ "Title": tr("Default"), "Name": "music-default" },
{ "Title": tr("Artists"), "Name": "music-artist" },
{ "Title": tr("Albums"), "Name": "music-album" },
]
@ -599,13 +589,7 @@ sub optionsClosed()
if m.top.parentItem.collectionType = "music"
if m.options.view <> m.view
if m.options.view = "music-artist"
m.view = "music-artist"
else if m.options.view = "music-album"
m.view = "music-album"
else
m.view = "music-default"
end if
m.view = m.options.view
set_user_setting("display.music.view", m.view)
reload = true
end if
@ -796,6 +780,12 @@ sub updateTitle()
m.top.overhangTitle = m.top.parentItem.title + tr(" (Filtered by ") + m.loadItemsTask.nameStartsWith + ")"
end if
if m.view = "music-artist"
m.top.overhangTitle = "%s (%s)".Format(m.top.parentItem.title, tr("Artists"))
else if m.view = "music-album"
m.top.overhangTitle = "%s (%s)".Format(m.top.parentItem.title, tr("Albums"))
end if
if m.options.view = "Networks" or m.view = "Networks"
m.top.overhangTitle = "%s (%s)".Format(m.top.parentItem.title, tr("Networks"))
end if

View File

@ -77,6 +77,16 @@ sub loadItems()
else if m.top.view = "Genres"
url = "Genres"
params.append({ UserId: get_setting("active_user") })
else if m.top.ItemType = "MusicArtist"
url = "Artists"
params.append({
UserId: get_setting("active_user")
})
params.IncludeItemTypes = ""
else if m.top.ItemType = "MusicAlbum"
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
params.append({ ImageTypeLimit: 1 })
params.append({ EnableImageTypes: "Primary,Backdrop,Banner,Thumb" })
else
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
end if
@ -110,7 +120,15 @@ sub loadItems()
tmp = CreateObject("roSGNode", "FolderData")
else if item.Type = "Studio"
tmp = CreateObject("roSGNode", "FolderData")
else if item.Type = "MusicArtist" or item.Type = "MusicAlbum"
else if item.Type = "MusicAlbum"
tmp = CreateObject("roSGNode", "MusicAlbumData")
tmp.type = "MusicAlbum"
if api_API().items.headimageurlbyname(item.id, "primary")
tmp.posterURL = ImageURL(item.id, "Primary")
else
tmp.posterURL = ImageURL(item.id, "backdrop")
end if
else if item.Type = "MusicArtist"
tmp = CreateObject("roSGNode", "MusicArtistData")
else if item.Type = "Audio"
tmp = CreateObject("roSGNode", "MusicSongData")

View File

@ -22,6 +22,7 @@
</interface>
<script type="text/brightscript" uri="LoadItemsTask2.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Items.brs" />
<script type="text/brightscript" uri="pkg:/source/roku_modules/api/api.brs" />
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />

View File

@ -1,13 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="MusicAlbumData" extends="ContentNode">
<component name="MusicAlbumData" extends="JFContentItem">
<interface>
<field id="id" type="string" />
<field id="title" type="string" />
<field id="image" type="node" onChange="setPoster" />
<field id="posterURL" type="string" />
<field id="overview" type="string" />
<field id="type" type="string" value="Episode" />
<field id="json" type="assocarray" onChange="setFields" />
</interface>
<script type="text/brightscript" uri="MusicAlbumData.brs" />
</component>

View File

@ -1,15 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="MusicSongData" extends="ContentNode">
<component name="MusicSongData" extends="JFContentItem">
<interface>
<field id="id" type="string" />
<field id="title" type="string" />
<field id="trackNumber" type="integer" />
<field id="image" type="node" onChange="setPoster" />
<field id="posterURL" type="string" />
<field id="overview" type="string" />
<field id="type" type="string" value="Song" />
<field id="json" type="assocarray" onChange="setFields" />
<field id="favorite" type="boolean" />
</interface>
<script type="text/brightscript" uri="MusicSongData.brs" />
</component>

View File

@ -15,10 +15,15 @@ function getData()
for each album in albumData.items
gridAlbum = CreateObject("roSGNode", "ContentNode")
if not isValid(album.posterURL) or album.posterURL = ""
album.posterURL = "pkg:/images/icons/album.png"
end if
gridAlbum.shortdescriptionline1 = album.title
gridAlbum.HDGRIDPOSTERURL = album.posterURL
gridAlbum.hdposterurl = album.posterURL
gridAlbum.SDGRIDPOSTERURL = album.SDGRIDPOSTERURL
gridAlbum.SDGRIDPOSTERURL = album.posterURL
gridAlbum.sdposterurl = album.posterURL
data.appendChild(gridAlbum)
@ -47,6 +52,15 @@ function onKeyEvent(key as string, press as boolean) as boolean
m.top.escape = key
return true
end if
else if key = "down"
totalCount = m.top.MusicArtistAlbumData.items.count()
totalRows = div_ceiling(totalCount, 5)
currentRow = div_ceiling(m.top.itemFocused + 1, 5)
if currentRow = totalRows
m.top.escape = key
return true
end if
end if
return false

View File

@ -5,4 +5,5 @@
<field id="escape" type="string" alwaysNotify="true" />
</interface>
<script type="text/brightscript" uri="AlbumGrid.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
</component>

View File

@ -8,8 +8,16 @@ sub init()
m.albumHeader = m.top.findNode("albumHeader")
m.albumHeader.text = tr("Albums")
m.appearsOnHeader = m.top.findNode("appearsOnHeader")
m.appearsOnHeader.text = tr("AppearsOn")
m.appearsOn = m.top.findNode("appearsOn")
m.appearsOn.observeField("escape", "onAppearsOnEscape")
m.appearsOn.observeField("MusicArtistAlbumData", "onAppearsOnData")
m.albums = m.top.findNode("albums")
m.albums.observeField("escape", "onAlbumsEscape")
m.albums.observeField("MusicArtistAlbumData", "onAlbumsData")
m.pageLoadAnimation = m.top.findNode("pageLoad")
m.pageLoadAnimation.control = "start"
@ -33,15 +41,47 @@ sub init()
createDialogPallete()
end sub
sub onAlbumsData()
' We have no album data
if m.albums.MusicArtistAlbumData.TotalRecordCount = 0
m.sectionScroller.removeChild(m.top.findNode("albumsSlide"))
m.sectionNavigation.removeChild(m.top.findNode("albumsLink"))
m.top.findNode("appearsOnSlide").callFunc("scrollUpToOnDeck")
end if
end sub
sub onAppearsOnData()
' We have no appears on data
if m.appearsOn.MusicArtistAlbumData.TotalRecordCount = 0
m.sectionScroller.removeChild(m.top.findNode("appearsOnSlide"))
m.sectionNavigation.removeChild(m.top.findNode("appearsOnLink"))
end if
end sub
sub onSectionScrollerChange()
m.overhang.isVisible = (m.sectionScroller.displayedIndex = 0)
end sub
sub OnScreenShown()
m.sectionScroller.focus = true
if m.sectionScroller.displayedIndex = 0
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
m.top.selectedButtonIndex = 0
m.buttonGrp.setFocus(true)
else
m.overhang.opacity = "0"
m.overhang.isVisible = false
m.overhang.opacity = "1"
end if
end sub
sub OnScreenHidden()
if not m.overhang.isVisible
m.overhang.disableMoveAnimation = true
m.overhang.isVisible = true
m.overhang.disableMoveAnimation = false
m.overhang.opacity = "1"
end if
end sub
@ -50,6 +90,18 @@ sub onAlbumsEscape()
m.sectionNavigation.selected = m.sectionScroller.displayedIndex - 1
else if m.albums.escape = "left"
m.sectionNavigation.setFocus(true)
else if m.albums.escape = "down"
if m.sectionScroller.displayedIndex + 1 < m.sectionNavigation.getChildCount()
m.sectionNavigation.selected = m.sectionScroller.displayedIndex + 1
end if
end if
end sub
sub onAppearsOnEscape()
if m.appearsOn.escape = "up"
m.sectionNavigation.selected = m.sectionScroller.displayedIndex - 1
else if m.appearsOn.escape = "left"
m.sectionNavigation.setFocus(true)
end if
end sub
@ -95,7 +147,6 @@ sub pageContentChanged()
' Populate scene data
setScreenTitle(item.json)
setPosterImage(item.posterURL)
setOnScreenTextValues(item.json)
end sub
sub setScreenTitle(json)
@ -129,10 +180,12 @@ sub setBackdropImage(data)
end if
end sub
' Populate on screen text variables
sub setOnScreenTextValues(json)
if isValid(json)
setFieldTextValue("overview", json.overview)
' Event fired when page data is loaded
sub artistOverviewChanged()
overviewContent = m.top.artistOverview
if isValid(overviewContent)
setFieldTextValue("overview", overviewContent)
end if
end sub
@ -194,19 +247,17 @@ sub createDialogPallete()
}
end sub
sub OnScreenShown()
m.sectionScroller.focus = true
if m.sectionScroller.displayedIndex = 0
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
m.top.selectedButtonIndex = 0
m.buttonGrp.setFocus(true)
end if
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if m.buttonGrp.isInFocusChain()
if key = "OK"
if press
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.selected = not selectedButton.selected
return true
end if
end if
if key = "left"
if m.top.selectedButtonIndex > 0
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
@ -241,11 +292,13 @@ function onKeyEvent(key as string, press as boolean) as boolean
end if
if key = "down"
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.focus = false
if m.sectionNavigation.getChildCount() > 1
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.focus = false
m.top.selectedButtonIndex = 0
m.sectionNavigation.selected = m.sectionScroller.displayedIndex + 1
m.top.selectedButtonIndex = 0
m.sectionNavigation.selected = m.sectionScroller.displayedIndex + 1
end if
end if
end if

View File

@ -19,20 +19,27 @@
</LayoutGroup>
</Section>
<Section id="slide-2" translation="[0, 1050]" defaultFocusID="albums">
<Section id="albumsSlide" translation="[0, 950]" defaultFocusID="albums">
<Rectangle id='albumRect' translation="[0, 0]" width="1920" height="1080" color="#000000" opacity=".75" />
<Label id="albumHeader" translation="[120, 50]" font="font:LargeSystemFont" />
<AlbumGrid id="albums" translation="[120, 150]" vertFocusAnimationStyle="fixedFocus" basePosterSize="[300, 300]" numColumns="5" numRows="99" caption1NumLines="1" itemSpacing="[50, 50]" />
</Section>
<Section id="appearsOnSlide" translation="[0, 1100]" defaultFocusID="appearsOn">
<Rectangle id='appearsOnRect' translation="[0, 0]" width="1920" height="1080" color="#000000" opacity=".75" />
<Label id="appearsOnHeader" translation="[120, 50]" font="font:LargeSystemFont" />
<AlbumGrid id="appearsOn" translation="[120, 150]" vertFocusAnimationStyle="fixedFocus" basePosterSize="[300, 300]" numColumns="5" numRows="99" caption1NumLines="1" itemSpacing="[50, 50]" />
</Section>
</SectionScroller>
<bgv_ButtonGroupVert id="sectionNavigation" translation="[-100, 175]" itemSpacings="[10]">
<sob_SlideOutButton background="#070707" focusBackground="#00a4dc" highlightBackground="#555555" padding="20" icon="pkg:/images/icons/details.png" text="Details" height="50" width="60" />
<sob_SlideOutButton background="#070707" focusBackground="#00a4dc" highlightBackground="#555555" padding="20" icon="pkg:/images/icons/cd.png" text="Albums" height="50" width="60" />
<sob_SlideOutButton id="albumsLink" background="#070707" focusBackground="#00a4dc" highlightBackground="#555555" padding="20" icon="pkg:/images/icons/cd.png" text="Albums" height="50" width="60" />
<sob_SlideOutButton id="appearsOnLink" background="#070707" focusBackground="#00a4dc" highlightBackground="#555555" padding="20" icon="pkg:/images/icons/cassette.png" text="Appears On" height="50" width="60" />
</bgv_ButtonGroupVert>
<Animation id="pageLoad" duration=".5" repeat="false">
<Vector2DFieldInterpolator key="[0.0, .5]" keyValue="[[0, 1050], [0, 750]]" fieldToInterp="slide-2.translation" />
<Animation id="pageLoad" duration="1" repeat="false">
<Vector2DFieldInterpolator key="[0.5, 1.0]" keyValue="[[-100, 175], [40, 175]]" fieldToInterp="sectionNavigation.translation" />
</Animation>
@ -40,7 +47,10 @@
<interface>
<field id="pageContent" type="node" onChange="pageContentChanged" />
<field id="musicArtistAlbumData" type="assocarray" alias="albums.MusicArtistAlbumData" />
<field id="musicArtistAppearsOnData" type="assocarray" alias="appearsOn.MusicArtistAlbumData" />
<field id="artistOverview" type="string" onChange="artistOverviewChanged" />
<field id="musicAlbumSelected" alias="albums.itemSelected" />
<field id="appearsOnSelected" alias="appearsOn.itemSelected" />
<field id="playArtistSelected" alias="play.selected" />
<field id="instantMixSelected" alias="instantMix.selected" />
<field id="selectedButtonIndex" type="integer" value="-1" />

View File

@ -15,6 +15,16 @@ sub init()
m.scrollOffBottomPosition = m.top.findNode("scrollOffBottomPosition")
m.scrollOffBottomOpacity = m.top.findNode("scrollOffBottomOpacity")
m.scrollUpToOnDeckAnimation = m.top.findNode("scrollUpToOnDeckAnimation")
m.scrollUpToOnDeckPosition = m.top.findNode("scrollUpToOnDeckPosition")
m.scrollDownToOnDeckAnimation = m.top.findNode("scrollDownToOnDeckAnimation")
m.scrollDownToOnDeckPosition = m.top.findNode("scrollDownToOnDeckPosition")
m.scrollOffOnDeckAnimation = m.top.findNode("scrollOffOnDeckAnimation")
m.scrollOffOnDeckPosition = m.top.findNode("scrollOffOnDeckPosition")
m.top.observeField("translation", "onTranslationChange")
m.top.observeField("id", "onIDChange")
m.top.observeField("focusedChild", "onFocusChange")
end sub
@ -31,6 +41,18 @@ sub onIDChange()
m.scrollOffBottomPosition.fieldToInterp = m.top.id + ".translation"
m.scrollOffBottomOpacity.fieldToInterp = m.top.id + ".opacity"
m.scrollUpToOnDeckPosition.fieldToInterp = m.top.id + ".translation"
m.scrollDownToOnDeckPosition.fieldToInterp = m.top.id + ".translation"
m.scrollOffOnDeckPosition.fieldToInterp = m.top.id + ".translation"
end sub
sub onTranslationChange()
m.startingPosition = m.top.translation
m.scrollOffBottomPosition.keyValue = "[[0, 0], [" + str(m.startingPosition[0]) + ", " + str(m.startingPosition[1]) + "]]"
m.top.unobserveField("translation")
end sub
sub showFromTop()
@ -49,6 +71,18 @@ sub scrollOffTop()
m.scrollOffTopAnimation.control = "start"
end sub
sub scrollUpToOnDeck()
m.scrollUpToOnDeckAnimation.control = "start"
end sub
sub scrollDownToOnDeck()
m.scrollDownToOnDeckAnimation.control = "start"
end sub
sub scrollOffOnDeck()
m.scrollOffOnDeckAnimation.control = "start"
end sub
sub onFocusChange()
defaultFocusElement = m.top.findNode(m.top.defaultFocusID)

View File

@ -3,19 +3,28 @@
<children>
<Animation id="showFromBottomAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="showFromBottomPosition" key="[0.0, 1.0]" keyValue="[[0, 750], [0, 0]]" fieldToInterp="" />
<FloatFieldInterpolator id="showFromBottomOpacity" key="[0.0, 1.0]" keyValue="[0.75, 0.95]" fieldToInterp="" />
<FloatFieldInterpolator id="showFromBottomOpacity" key="[0.0, 1.0]" keyValue="[0.55, 0.95]" fieldToInterp="" />
</Animation>
<Animation id="showFromTopAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="showFromTopPosition" key="[0.0, 1.0]" keyValue="[[0, -1080], [0, 0]]" fieldToInterp="" />
<FloatFieldInterpolator id="showFromTopOpacity" key="[0.0, 1.0]" keyValue="[0.75, 0.95]" fieldToInterp="" />
<FloatFieldInterpolator id="showFromTopOpacity" key="[0.0, 1.0]" keyValue="[0.55, 0.95]" fieldToInterp="" />
</Animation>
<Animation id="scrollOffBottomAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="scrollOffBottomPosition" key="[0.0, 1.0]" keyValue="[[0, 0], [0, 750]]" fieldToInterp="" />
<FloatFieldInterpolator id="scrollOffBottomOpacity" key="[0.0, 1.0]" keyValue="[0.95, 0.75]" fieldToInterp="" />
<Vector2DFieldInterpolator id="scrollOffBottomPosition" key="[0.0, 1.0]" keyValue="[]" fieldToInterp="" />
<FloatFieldInterpolator id="scrollOffBottomOpacity" key="[0.0, 1.0]" keyValue="[0.95, 0.55]" fieldToInterp="" />
</Animation>
<Animation id="scrollOffTopAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="scrollOffTopPosition" key="[0.0, 1.0]" keyValue="[[0, 0], [0, -1080]]" fieldToInterp="" />
<FloatFieldInterpolator id="scrollOffTopOpacity" key="[0.0, 1.0]" keyValue="[0.95, 0.75]" fieldToInterp="" />
<FloatFieldInterpolator id="scrollOffTopOpacity" key="[0.0, 1.0]" keyValue="[0.95, 0.55]" fieldToInterp="" />
</Animation>
<Animation id="scrollUpToOnDeckAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="scrollUpToOnDeckPosition" key="[0.0, 1.0]" keyValue="[[0, 1100], [0, 950]]" fieldToInterp="" />
</Animation>
<Animation id="scrollDownToOnDeckAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="scrollDownToOnDeckPosition" key="[0.0, 1.0]" keyValue="[[0, 0], [0, 950]]" fieldToInterp="" />
</Animation>
<Animation id="scrollOffOnDeckAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="scrollOffOnDeckPosition" key="[0.0, 1.0]" keyValue="[[0, 950], [0, 1080]]" fieldToInterp="" />
</Animation>
</children>
<interface>
@ -24,6 +33,9 @@
<function name="showFromBottom" />
<function name="scrollOffTop" />
<function name="scrollOffBottom" />
<function name="scrollUpToOnDeck" />
<function name="scrollDownToOnDeck" />
<function name="scrollOffOnDeck" />
</interface>
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
<script type="text/brightscript" uri="section.brs" />

View File

@ -24,13 +24,34 @@ sub displayedIndexChanged()
displayedSection = m.top.getChild(m.top.displayedIndex)
displayedSection.setFocus(true)
onDeckSection = invalid
previouslyOnDeckSection = invalid
if m.top.displayedIndex + 1 <= (m.top.getChildCount() - 1)
onDeckSection = m.top.getChild(m.top.displayedIndex + 1)
end if
if m.top.displayedIndex + 2 <= (m.top.getChildCount() - 1)
previouslyOnDeckSection = m.top.getChild(m.top.displayedIndex + 2)
end if
' Move sections either up or down depending on what index we're moving to
if m.top.displayedIndex > m.previouslyDisplayedSection
m.top.getChild(m.previouslyDisplayedSection).callFunc("scrollOffTop")
for i = m.previouslyDisplayedSection to m.top.displayedIndex - 1
m.top.getChild(i).callFunc("scrollOffTop")
end for
displayedSection.callFunc("showFromBottom")
if isValid(onDeckSection)
onDeckSection.callFunc("scrollUpToOnDeck")
end if
else if m.top.displayedIndex < m.previouslyDisplayedSection
m.top.getChild(m.previouslyDisplayedSection).callFunc("scrollOffBottom")
m.top.getChild(m.top.displayedIndex + 1).callFunc("scrollDownToOnDeck")
displayedSection.callFunc("showFromTop")
if isValid(previouslyOnDeckSection)
previouslyOnDeckSection.callFunc("scrollOffOnDeck")
end if
end if
m.previouslyDisplayedSection = m.top.displayedIndex

BIN
images/icons/album.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
images/icons/cassette.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -757,7 +757,7 @@
<message>
<source>direct</source>
<translation>direct</translation>
</message>
</message>
<message>
<source>Total Bitrate</source>
<translation>Total Bitrate</translation>
@ -765,7 +765,7 @@
<message>
<source>Audio Channels</source>
<translation>Audio Channels</translation>
</message>
</message>
<message>
<source>Stream Information</source>
<translation>Stream Information</translation>
@ -781,36 +781,41 @@
<message>
<source>Level</source>
<translation>Level</translation>
<extracomment>Video profile level</extracomment>
</message>
<extracomment>Video profile level</extracomment>
</message>
<message>
<source>Bit Rate</source>
<translation>Bit Rate</translation>
<extracomment>Video streaming bit rate</extracomment>
</message>
<extracomment>Video streaming bit rate</extracomment>
</message>
<message>
<source>Container</source>
<translation>Container</translation>
<extracomment>Video streaming container</extracomment>
</message>
<extracomment>Video streaming container</extracomment>
</message>
<message>
<source>Size</source>
<translation>Size</translation>
<extracomment>Video size</extracomment>
</message>
<extracomment>Video size</extracomment>
</message>
<message>
<source>Video range type</source>
<translation>Video range type</translation>
</message>
</message>
<message>
<source>Pixel format</source>
<translation>Pixel format</translation>
<extracomment>Video pixel format</extracomment>
</message>
<extracomment>Video pixel format</extracomment>
</message>
<message>
<source>WxH</source>
<translation>WxH</translation>
<extracomment>Video width x height</extracomment>
</message>
<extracomment>Video width x height</extracomment>
</message>
<message>
<source>Unable to find any albums or songs belonging to this artist</source>
<translation>Unable to find any albums or songs belonging to this artist</translation>
<extracomment>Popup message when we find no audio data for an artist</extracomment>
</message>
</context>
</TS>

View File

@ -159,6 +159,9 @@ sub Main (args as dynamic) as void
' Nothing to do here, handled in ItemGrid
else if selectedItem.type = "MusicArtist"
group = CreateArtistView(selectedItem.json)
if not isValid(group)
message_dialog(tr("Unable to find any albums or songs belonging to this artist"))
end if
else if selectedItem.type = "MusicAlbum"
group = CreateAlbumView(selectedItem.json)
else if selectedItem.type = "Audio"
@ -188,6 +191,12 @@ sub Main (args as dynamic) as void
albums = msg.getRoSGNode()
node = albums.musicArtistAlbumData.items[ptr]
group = CreateAlbumView(node)
else if isNodeEvent(msg, "appearsOnSelected")
' If you select a Music Album from ANYWHERE, follow this flow
ptr = msg.getData()
albums = msg.getRoSGNode()
node = albums.musicArtistAppearsOnData.items[ptr]
group = CreateAlbumView(node)
else if isNodeEvent(msg, "playSong")
' User has selected audio they want us to play
selectedIndex = msg.getData()

View File

@ -377,13 +377,26 @@ end function
' Shows details on selected artist. Bio, image, and list of available albums
function CreateArtistView(musicartist)
musicData = MusicAlbumList(musicartist.id)
appearsOnData = AppearsOnList(musicartist.id)
' User only has songs under artists
if musicData = invalid or musicData.Items.Count() = 0
if (musicData = invalid or musicData.Items.Count() = 0) and (appearsOnData = invalid or appearsOnData.Items.Count() = 0)
' Just songs under artists...
group = CreateObject("roSGNode", "AlbumView")
group.pageContent = ItemMetaData(musicartist.id)
group.albumData = MusicSongList(musicartist.id)
' Lookup songs based on artist id
songList = GetSongsByArtist(musicartist.id)
if not isValid(songList)
' Lookup songs based on folder parent / child relationship
songList = MusicSongList(musicartist.id)
end if
if not isValid(songList)
return invalid
end if
group.albumData = songList
group.observeField("playSong", m.port)
group.observeField("playAllSelected", m.port)
group.observeField("instantMixSelected", m.port)
@ -392,9 +405,13 @@ function CreateArtistView(musicartist)
group = CreateObject("roSGNode", "ArtistView")
group.pageContent = ItemMetaData(musicartist.id)
group.musicArtistAlbumData = musicData
group.musicArtistAppearsOnData = appearsOnData
group.artistOverview = ArtistOverview(musicartist.name)
group.observeField("musicAlbumSelected", m.port)
group.observeField("playArtistSelected", m.port)
group.observeField("instantMixSelected", m.port)
group.observeField("appearsOnSelected", m.port)
end if
m.global.sceneManager.callFunc("pushScene", group)
@ -531,8 +548,6 @@ function CreateArtistMixGroup(artistID)
songIDArray.push(song.id)
end for
songIDArray.shift()
group.pageContent = songIDArray
group.musicArtistAlbumData = songList.items

View File

@ -78,7 +78,7 @@ function ItemMetaData(id as string)
if data = invalid then return invalid
imgParams = {}
if data.type <> "Audio"
if data.UserData.PlayedPercentage <> invalid
if data?.UserData?.PlayedPercentage <> invalid
param = { "PercentPlayed": data.UserData.PlayedPercentage }
imgParams.Append(param)
end if
@ -163,21 +163,82 @@ function ItemMetaData(id as string)
end if
end function
' Music Artist Data
function ArtistOverview(name as string)
req = createObject("roUrlTransfer")
url = Substitute("Artists/{0}", req.escape(name))
resp = APIRequest(url)
data = getJson(resp)
if data = invalid then return invalid
return data.overview
end function
' Get list of albums belonging to an artist
function MusicAlbumList(id as string)
url = Substitute("Users/{0}/Items", get_setting("active_user"), id)
url = Substitute("Users/{0}/Items", get_setting("active_user"))
resp = APIRequest(url, {
"UserId": get_setting("active_user"),
"parentId": id,
"AlbumArtistIds": id,
"includeitemtypes": "MusicAlbum",
"sortBy": "SortName"
"sortBy": "SortName",
"Recursive": true
})
data = getJson(resp)
results = []
for each item in data.Items
tmp = CreateObject("roSGNode", "MusicAlbumData")
tmp.image = PosterImage(item.id, { "maxHeight": "500", "maxWidth": "500" })
tmp.image = PosterImage(item.id)
tmp.json = item
results.push(tmp)
end for
data.Items = results
return data
end function
' Get list of albums an artist appears on
function AppearsOnList(id as string)
url = Substitute("Users/{0}/Items", get_setting("active_user"))
resp = APIRequest(url, {
"ContributingArtistIds": id,
"ExcludeItemIds": id,
"includeitemtypes": "MusicAlbum",
"sortBy": "PremiereDate,ProductionYear,SortName",
"SortOrder": "Descending",
"Recursive": true
})
data = getJson(resp)
results = []
for each item in data.Items
tmp = CreateObject("roSGNode", "MusicAlbumData")
tmp.image = PosterImage(item.id)
tmp.json = item
results.push(tmp)
end for
data.Items = results
return data
end function
' Get list of songs belonging to an artist
function GetSongsByArtist(id as string)
url = Substitute("Users/{0}/Items", get_setting("active_user"))
resp = APIRequest(url, {
"AlbumArtistIds": id,
"includeitemtypes": "Audio",
"sortBy": "SortName",
"Recursive": true
})
data = getJson(resp)
results = []
if data = invalid then return invalid
if data.Items = invalid then return invalid
if data.Items.Count() = 0 then return invalid
for each item in data.Items
tmp = CreateObject("roSGNode", "MusicAlbumData")
tmp.image = PosterImage(item.id)
tmp.json = item
results.push(tmp)
end for
@ -195,8 +256,13 @@ function MusicSongList(id as string)
"sortBy": "SortName"
})
data = getJson(resp)
results = []
data = getJson(resp)
if data = invalid then return invalid
if data.Items = invalid then return invalid
if data.Items.Count() = 0 then return invalid
for each item in data.Items
tmp = CreateObject("roSGNode", "MusicSongData")
tmp.image = PosterImage(item.id)
@ -232,15 +298,18 @@ end function
' Get Instant Mix based on item
function CreateArtistMix(id as string)
url = Substitute("Users/{0}/Items", get_setting("active_user"), id)
url = Substitute("Users/{0}/Items", get_setting("active_user"))
resp = APIRequest(url, {
"UserId": get_setting("active_user"),
"parentId": id,
"Filters": "IsNotFolder",
"Recursive": true,
"SortBy": "SortName",
"ArtistIds": id,
"Recursive": "true",
"MediaTypes": "Audio",
"Limit": 300
"Filters": "IsNotFolder",
"SortBy": "SortName",
"Limit": 300,
"Fields": "Chapters",
"ExcludeLocationTypes": "Virtual",
"EnableTotalRecordCount": false,
"CollapseBoxSetItems": false
})
return getJson(resp)