mirror of
https://github.com/jellyfin/jellyfin-roku.git
synced 2024-11-23 14:19:40 +00:00
Create music artist view
This commit is contained in:
parent
047f884b35
commit
8f516926ef
@ -57,6 +57,9 @@ sub itemContentChanged()
|
||||
else if itemData.type = "Photo"
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else if itemData.type = "MusicArtist"
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else
|
||||
print "Unhandled Grid Item Type: " + itemData.type
|
||||
end if
|
||||
|
@ -100,6 +100,8 @@ sub loadInitialItems()
|
||||
m.loadItemsTask.itemType = "Movie"
|
||||
else if m.top.parentItem.collectionType = "tvshows"
|
||||
m.loadItemsTask.itemType = "Series"
|
||||
else if m.top.parentItem.collectionType = "music"
|
||||
m.loadItemsTask.itemType = "MusicArtist"
|
||||
else if m.top.parentItem.collectionType = "livetv"
|
||||
m.loadItemsTask.itemType = "LiveTV"
|
||||
|
||||
@ -208,6 +210,19 @@ sub SetUpOptions()
|
||||
options.views = []
|
||||
options.sort = []
|
||||
options.filter = []
|
||||
'Music
|
||||
else if m.top.parentItem.collectionType = "music"
|
||||
options.views = [{ "Title": tr("Music"), "Name": "music" }]
|
||||
options.sort = [
|
||||
{ "Title": tr("TITLE"), "Name": "SortName" },
|
||||
{ "Title": tr("DATE_ADDED"), "Name": "DateCreated" },
|
||||
{ "Title": tr("DATE_PLAYED"), "Name": "DatePlayed" },
|
||||
{ "Title": tr("RELEASE_DATE"), "Name": "PremiereDate" },
|
||||
]
|
||||
options.filter = [
|
||||
{ "Title": tr("All"), "Name": "All" },
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" }
|
||||
]
|
||||
else
|
||||
options.views = [
|
||||
{ "Title": tr("Default"), "Name": "default" }
|
||||
|
@ -76,6 +76,8 @@ sub loadItems()
|
||||
tmp = CreateObject("roSGNode", "PhotoData")
|
||||
else if item.type = "PhotoAlbum"
|
||||
tmp = CreateObject("roSGNode", "FolderData")
|
||||
else if item.Type = "MusicArtist"
|
||||
tmp = CreateObject("roSGNode", "MusicArtistData")
|
||||
else
|
||||
print "[LoadItems] Unknown Type: " item.Type
|
||||
end if
|
||||
|
32
components/data/MusicArtistData.brs
Normal file
32
components/data/MusicArtistData.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 = "MusicArtist"
|
||||
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
|
7
components/data/MusicArtistData.xml
Normal file
7
components/data/MusicArtistData.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="MusicArtistData" extends="JFContentItem">
|
||||
<script type="text/brightscript" uri="MusicArtistData.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>
|
48
components/music/MusicArtistAlbumRow.brs
Normal file
48
components/music/MusicArtistAlbumRow.brs
Normal file
@ -0,0 +1,48 @@
|
||||
sub init()
|
||||
m.top.itemComponentName = "ListPoster"
|
||||
m.top.content = getData()
|
||||
|
||||
m.top.rowFocusAnimationStyle = "fixedFocusWrap"
|
||||
|
||||
m.top.showRowLabel = [false]
|
||||
m.top.showRowCounter = [true]
|
||||
m.top.rowLabelOffset = [0, 0]
|
||||
|
||||
updateSize()
|
||||
|
||||
m.top.setfocus(true)
|
||||
end sub
|
||||
|
||||
sub updateSize()
|
||||
itemWidth = 200
|
||||
itemHeight = 320 ' width * 1.5 + text
|
||||
|
||||
m.top.visible = true
|
||||
|
||||
' size of the whole row
|
||||
m.top.itemSize = [1800, (itemHeight + 40)]
|
||||
' 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 = [0, 0]
|
||||
end sub
|
||||
|
||||
function getData()
|
||||
if m.top.MusicArtistAlbumData = invalid
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
return data
|
||||
end if
|
||||
|
||||
seasonData = m.top.MusicArtistAlbumData
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
row = data.CreateChild("ContentNode")
|
||||
row.title = "Seasons"
|
||||
for each item in seasonData.items
|
||||
row.appendChild(item)
|
||||
end for
|
||||
m.top.content = data
|
||||
return data
|
||||
end function
|
7
components/music/MusicArtistAlbumRow.xml
Normal file
7
components/music/MusicArtistAlbumRow.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="MusicArtistAlbumRow" extends="RowList">
|
||||
<interface>
|
||||
<field id="MusicArtistAlbumData" type="assocarray" onChange="getData" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="MusicArtistAlbumRow.brs" />
|
||||
</component>
|
107
components/music/MusicArtistDetails.brs
Normal file
107
components/music/MusicArtistDetails.brs
Normal file
@ -0,0 +1,107 @@
|
||||
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()
|
||||
' Updates video metadata
|
||||
' TODO - make things use item rather than itemData
|
||||
item = m.top.itemContent
|
||||
itemData = item.json
|
||||
|
||||
m.top.findNode("musicartistPoster").uri = m.top.itemContent.posterURL
|
||||
|
||||
' Handle all "As Is" fields
|
||||
m.top.overhangTitle = itemData.name
|
||||
|
||||
setFieldText("overview", itemData.overview)
|
||||
|
||||
if type(itemData.RunTimeTicks) = "LongInteger"
|
||||
setFieldText("runtime", stri(getRuntime()) + " mins")
|
||||
end if
|
||||
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"
|
||||
value = str(value).trim()
|
||||
else if type(value) = "roFloat" or type(value) = "Float"
|
||||
value = str(value).trim()
|
||||
else if type(value) <> "roString" and type(value) <> "String"
|
||||
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)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
overview = m.top.findNode("overview")
|
||||
topGrp = m.top.findNode("seasons")
|
||||
bottomGrp = m.top.findNode("extrasGrid")
|
||||
|
||||
if key = "down" and overview.hasFocus()
|
||||
topGrp.setFocus(true)
|
||||
return true
|
||||
else if key = "down" and topGrp.hasFocus()
|
||||
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.hasFocus()
|
||||
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
|
||||
else if key = "up" and topGrp.hasFocus()
|
||||
overview.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
||||
return false
|
||||
end function
|
22
components/music/MusicArtistDetails.xml
Normal file
22
components/music/MusicArtistDetails.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="MusicArtistDetails" extends="JFGroup">
|
||||
<children>
|
||||
<LayoutGroup id="toplevel" layoutDirection="vert" itemSpacings="[-10]" >
|
||||
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[15]" >
|
||||
<Poster id="musicartistPoster" width="450" height="450" />
|
||||
<LayoutGroup layoutDirection="vert" itemSpacings="[15]">
|
||||
<Label id="overview" wrap="true" width="1250" maxLines="8" />
|
||||
</LayoutGroup>
|
||||
</LayoutGroup>
|
||||
<MusicArtistAlbumRow id="seasons" />
|
||||
</LayoutGroup>
|
||||
<ExtrasSlider id="tvSeasonExtras" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="itemContentChanged" />
|
||||
<field id="musicArtistAlbumData" type="assocarray" alias="seasons.MusicArtistAlbumData" />
|
||||
<field id="seasonSelected" alias="seasons.rowItemSelected" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
<script type="text/brightscript" uri="MusicArtistDetails.brs" />
|
||||
</component>
|
@ -154,6 +154,8 @@ sub Main (args as dynamic) as void
|
||||
end if
|
||||
else if selectedItem.type = "Photo"
|
||||
' Nothing to do here, handled in ItemGrid
|
||||
else if selectedItem.type = "MusicArtist"
|
||||
group = CreateMusicArtistDetailsGroup(selectedItem.json)
|
||||
else
|
||||
' TODO - switch on more node types
|
||||
message_dialog("This type is not yet supported: " + selectedItem.type + ".")
|
||||
|
@ -321,6 +321,21 @@ function CreateSeriesDetailsGroup(series)
|
||||
return group
|
||||
end function
|
||||
|
||||
function CreateMusicArtistDetailsGroup(musicartist)
|
||||
group = CreateObject("roSGNode", "MusicArtistDetails")
|
||||
m.global.sceneManager.callFunc("pushScene", group)
|
||||
|
||||
group.itemContent = ItemMetaData(musicartist.id)
|
||||
group.musicArtistAlbumData = MusicAlbums(musicartist.id)
|
||||
|
||||
group.observeField("seasonSelected", m.port)
|
||||
|
||||
extras = group.findNode("extrasGrid")
|
||||
extras.observeField("selectedItem", m.port)
|
||||
|
||||
return group
|
||||
end function
|
||||
|
||||
function CreateSeasonDetailsGroup(series, season)
|
||||
group = CreateObject("roSGNode", "TVEpisodes")
|
||||
group.optionsAvailable = false
|
||||
|
@ -118,6 +118,11 @@ function ItemMetaData(id as string)
|
||||
tmp.image = PosterImage(data.id, { "MaxWidth": 300, "MaxHeight": 450 })
|
||||
tmp.json = data
|
||||
return tmp
|
||||
else if data.type = "MusicArtist"
|
||||
tmp = CreateObject("roSGNode", "MusicArtistData")
|
||||
tmp.image = PosterImage(data.id)
|
||||
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
|
||||
@ -125,6 +130,27 @@ function ItemMetaData(id as string)
|
||||
end if
|
||||
end function
|
||||
|
||||
' Music Albums Belonging to an Artist
|
||||
function MusicAlbums(id as string)
|
||||
url = Substitute("Users/{0}/Items", get_setting("active_user"), id)
|
||||
resp = APIRequest(url, {
|
||||
"UserId": get_setting("active_user"),
|
||||
"parentId": id,
|
||||
"includeitemtypes": "MusicAlbum"
|
||||
})
|
||||
|
||||
data = getJson(resp)
|
||||
results = []
|
||||
for each item in data.Items
|
||||
tmp = CreateObject("roSGNode", "TVEpisodeData")
|
||||
tmp.image = PosterImage(item.id)
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
end for
|
||||
data.Items = results
|
||||
return data
|
||||
end function
|
||||
|
||||
' Seasons for a TV Show
|
||||
function TVSeasons(id as string)
|
||||
url = Substitute("Shows/{0}/Seasons", id)
|
||||
|
Loading…
Reference in New Issue
Block a user