enable Quick Play for boxsets, series, seasons, albums, and music artists
This commit is contained in:
parent
d92dc42d8c
commit
1acec715b1
|
@ -780,11 +780,11 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
m.loadItemsTask.control = "stop"
|
||||
return true
|
||||
end if
|
||||
else if key = "play" or key = "OK"
|
||||
else if key = "play"
|
||||
markupGrid = m.top.findNode("itemGrid")
|
||||
itemToPlay = markupGrid.content.getChild(markupGrid.itemFocused)
|
||||
|
||||
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode")
|
||||
if itemToPlay <> invalid
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
return true
|
||||
else if itemToPlay <> invalid and itemToPlay.type = "Photo"
|
||||
|
|
|
@ -869,11 +869,10 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
m.loadItemsTask.control = "stop"
|
||||
return true
|
||||
end if
|
||||
else if key = "play" or key = "OK"
|
||||
|
||||
else if key = "play"
|
||||
itemToPlay = getItemFocused()
|
||||
|
||||
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode")
|
||||
if itemToPlay <> invalid
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
return true
|
||||
end if
|
||||
|
|
|
@ -750,7 +750,6 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
alpha.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
||||
else if key = "right" and m.Alpha.isinFocusChain()
|
||||
m.top.alphaActive = false
|
||||
m.Alpha.setFocus(false)
|
||||
|
@ -760,14 +759,12 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
m.genreList.setFocus(m.genreList.opacity = 1)
|
||||
|
||||
return true
|
||||
|
||||
else if key = "replay" and m.itemGrid.isinFocusChain()
|
||||
if m.resetGrid = true
|
||||
m.itemGrid.animateToItem = 0
|
||||
else
|
||||
m.itemGrid.jumpToItem = 0
|
||||
end if
|
||||
|
||||
else if key = "replay" and m.genreList.isinFocusChain()
|
||||
if m.resetGrid = true
|
||||
m.genreList.animateToItem = 0
|
||||
|
@ -775,6 +772,12 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
m.genreList.jumpToItem = 0
|
||||
end if
|
||||
return true
|
||||
else if key = "play"
|
||||
itemToPlay = getItemFocused()
|
||||
if itemToPlay <> invalid
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
return true
|
||||
end if
|
||||
end if
|
||||
|
||||
if key = "replay"
|
||||
|
|
|
@ -3,6 +3,7 @@ sub init()
|
|||
updateSize()
|
||||
m.top.rowFocusAnimationStyle = "fixedFocus"
|
||||
m.top.observeField("rowItemSelected", "onRowItemSelected")
|
||||
m.top.observeField("rowItemFocused", "onRowItemFocused")
|
||||
|
||||
' Set up all Tasks
|
||||
m.LoadPeopleTask = CreateObject("roSGNode", "LoadItemsTask")
|
||||
|
@ -207,3 +208,7 @@ end sub
|
|||
sub onRowItemSelected()
|
||||
m.top.selectedItem = m.top.content.getChild(m.top.rowItemSelected[0]).getChild(m.top.rowItemSelected[1])
|
||||
end sub
|
||||
|
||||
sub onRowItemFocused()
|
||||
m.top.focusedItem = m.top.content.getChild(m.top.rowItemFocused[0]).getChild(m.top.rowItemFocused[1])
|
||||
end sub
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<interface>
|
||||
<field id="type" type="string" />
|
||||
<field id="parentId" type="string" />
|
||||
<field id="focusedItem" type="node" alwaysNotify="true" />
|
||||
<field id="selectedItem" type="node" alwaysNotify="true" />
|
||||
<function name="loadParts" />
|
||||
<function name="loadPersonVideos" />
|
||||
|
|
|
@ -440,21 +440,20 @@ sub itemSelected()
|
|||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
handled = false
|
||||
if press
|
||||
if key = "play"
|
||||
print "play was pressed from homerow"
|
||||
itemToPlay = m.top.content.getChild(m.top.rowItemFocused[0]).getChild(m.top.rowItemFocused[1])
|
||||
if isValid(itemToPlay) and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode")
|
||||
if isValid(itemToPlay)
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
end if
|
||||
handled = true
|
||||
end if
|
||||
|
||||
if key = "replay"
|
||||
return true
|
||||
else if key = "replay"
|
||||
m.top.jumpToRowItem = [m.top.rowItemFocused[0], 0]
|
||||
return true
|
||||
end if
|
||||
end if
|
||||
return handled
|
||||
return false
|
||||
end function
|
||||
|
||||
function filterNodeArray(nodeArray as object, nodeKey as string, excludeArray as object) as object
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<component name="HomeRows" extends="RowList">
|
||||
<interface>
|
||||
<field id="selectedItem" type="node" alwaysNotify="true" />
|
||||
<field id="quickPlayNode" type="node" alwaysNotify="true" />
|
||||
<field id="quickPlayNode" type="node" />
|
||||
<function name="updateHomeRows" />
|
||||
<function name="loadLibraries" />
|
||||
</interface>
|
||||
|
|
|
@ -137,7 +137,8 @@ sub loadItems()
|
|||
if isValid(data) and isValid(data.Items)
|
||||
for each item in data.Items
|
||||
' Skip Books for now as we don't support it (issue #558)
|
||||
if item.Type <> "Book"
|
||||
' also skip songs since there is limited space
|
||||
if not (item.Type = "Book" or item.Type = "Audio")
|
||||
tmp = CreateObject("roSGNode", "HomeData")
|
||||
|
||||
params = {}
|
||||
|
|
|
@ -127,6 +127,11 @@ sub playQueue()
|
|||
return
|
||||
end if
|
||||
|
||||
if nextItemMediaType = "movie"
|
||||
CreateVideoPlayerView()
|
||||
return
|
||||
end if
|
||||
|
||||
if nextItemMediaType = "episode"
|
||||
CreateVideoPlayerView()
|
||||
return
|
||||
|
|
|
@ -383,6 +383,12 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
audioOptionsClosed()
|
||||
return true
|
||||
end if
|
||||
else if key = "play" and m.extrasGrid.hasFocus()
|
||||
print "Play was pressed from the movie details extras slider"
|
||||
if m.extrasGrid.focusedItem <> invalid
|
||||
m.top.quickPlayNode = m.extrasGrid.focusedItem
|
||||
return true
|
||||
end if
|
||||
end if
|
||||
return false
|
||||
end function
|
||||
|
|
|
@ -50,5 +50,6 @@
|
|||
<field id="trailerAvailable" type="bool" onChange="trailerAvailableChanged" value="false" />
|
||||
<field id="selectedAudioStreamIndex" type="integer" />
|
||||
<field id="selectedVideoStreamId" type="string" />
|
||||
<field id="quickPlayNode" type="node" alwaysNotify="true" />
|
||||
</interface>
|
||||
</component>
|
|
@ -313,5 +313,13 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
end if
|
||||
end if
|
||||
|
||||
if key = "play"
|
||||
itemToPlay = m.albums.MusicArtistAlbumData.items[m.albums.itemFocused]
|
||||
if isValid(itemToPlay)
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
end if
|
||||
return true
|
||||
end if
|
||||
|
||||
return false
|
||||
end function
|
||||
|
|
|
@ -54,5 +54,6 @@
|
|||
<field id="playArtistSelected" alias="play.selected" />
|
||||
<field id="instantMixSelected" alias="instantMix.selected" />
|
||||
<field id="selectedButtonIndex" type="integer" value="-1" />
|
||||
<field id="quickPlayNode" type="node" alwaysNotify="true" />
|
||||
</interface>
|
||||
</component>
|
|
@ -11,6 +11,7 @@ sub init()
|
|||
m.getShuffleEpisodesTask = createObject("roSGNode", "getShuffleEpisodesTask")
|
||||
m.Shuffle = m.top.findNode("Shuffle")
|
||||
m.extrasSlider.visible = true
|
||||
m.seasons = m.top.findNode("seasons")
|
||||
end sub
|
||||
|
||||
sub itemContentChanged()
|
||||
|
@ -223,6 +224,20 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
else if key = "up" and m.Shuffle.hasFocus()
|
||||
overview.setFocus(true)
|
||||
return true
|
||||
else if key = "play" and m.seasons.hasFocus()
|
||||
print "play was pressed from the seasons row"
|
||||
if isValid(m.seasons.TVSeasonData) and isValid(m.seasons.TVSeasonData.Items)
|
||||
itemFocused = m.seasons.rowItemFocused
|
||||
m.top.quickPlayNode = m.seasons.TVSeasonData.Items[itemFocused[1]]
|
||||
return true
|
||||
end if
|
||||
else if key = "play" and m.extrasSlider.isInFocusChain()
|
||||
print "play was pressed from the extras grid"
|
||||
extrasGrid = m.top.findNode("extrasGrid")
|
||||
if extrasGrid.focusedItem <> invalid
|
||||
m.top.quickPlayNode = extrasGrid.focusedItem
|
||||
return true
|
||||
end if
|
||||
end if
|
||||
|
||||
return false
|
||||
|
|
|
@ -32,5 +32,6 @@
|
|||
<field id="itemContent" type="node" onChange="itemContentChanged" />
|
||||
<field id="seasonData" type="assocarray" alias="seasons.TVSeasonData" />
|
||||
<field id="seasonSelected" alias="seasons.rowItemSelected" alwaysNotify="true" />
|
||||
<field id="quickPlayNode" type="node" alwaysNotify="true" />
|
||||
</interface>
|
||||
</component>
|
201
source/Main.brs
201
source/Main.brs
|
@ -118,9 +118,30 @@ sub Main (args as dynamic) as void
|
|||
else if isNodeEvent(msg, "quickPlayNode")
|
||||
group = sceneManager.callFunc("getActiveScene")
|
||||
reportingNode = msg.getRoSGNode()
|
||||
itemNode = reportingNode.quickPlayNode
|
||||
itemNode = invalid
|
||||
if isValid(reportingNode)
|
||||
itemNode = reportingNode.quickPlayNode
|
||||
reportingNodeType = reportingNode.subtype()
|
||||
' prevent double fire on continue watching home row
|
||||
if isValid(reportingNodeType) and reportingNodeType = "Home"
|
||||
reportingNode.quickPlayNode = invalid
|
||||
end if
|
||||
end if
|
||||
if isValid(itemNode) and isValid(itemNode.id) and itemNode.id <> ""
|
||||
if itemNode.type = "Episode" or itemNode.type = "Movie" or itemNode.type = "Video"
|
||||
print "quickPlayNode=", itemNode
|
||||
itemType = invalid
|
||||
if isValid(itemNode.type)
|
||||
itemType = Lcase(itemNode.type)
|
||||
end if
|
||||
' grab type from json if needed
|
||||
if not isValid(itemType) or itemType = ""
|
||||
if isValid(itemNode.json) and isValid(itemNode.json.type)
|
||||
itemType = Lcase(itemNode.json.type)
|
||||
end if
|
||||
end if
|
||||
print "quickPlayNode type=", itemType
|
||||
if itemType = "episode" or itemType = "movie" or itemType = "video"
|
||||
' attempt to play video file. resume if possible
|
||||
if isValid(itemNode.selectedVideoStreamId)
|
||||
itemNode.id = itemNode.selectedVideoStreamId
|
||||
end if
|
||||
|
@ -141,14 +162,184 @@ sub Main (args as dynamic) as void
|
|||
m.global.queueManager.callFunc("push", itemNode)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
|
||||
' Prevent quick play node from double firing
|
||||
reportingNode.quickPlayNode = invalid
|
||||
|
||||
if LCase(group.subtype()) = "tvepisodes"
|
||||
if isValid(group.lastFocus)
|
||||
group.lastFocus.setFocus(true)
|
||||
end if
|
||||
end if
|
||||
else if itemType = "audio"
|
||||
' attempt to play audio file
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", itemNode)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else if itemType = "musicalbum"
|
||||
' attempt to play the entire album starting with track 1
|
||||
m.global.queueManager.callFunc("clear")
|
||||
' convert album to list of songs
|
||||
albumSongs = MusicSongList(itemNode.id)
|
||||
' add each song to the queue
|
||||
for each song in albumSongs.items
|
||||
song.type = "Audio"
|
||||
m.global.queueManager.callFunc("push", song)
|
||||
end for
|
||||
' play queue
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else if itemType = "musicartist"
|
||||
' attempt to shuffle play all songs by artist
|
||||
m.global.queueManager.callFunc("clear")
|
||||
|
||||
data = GetSongsByArtist(itemNode.id, { "sortBy": "Random" })
|
||||
|
||||
if isValid(data)
|
||||
for each item in data.items
|
||||
m.global.queueManager.callFunc("push", item)
|
||||
end for
|
||||
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
end if
|
||||
else if itemType = "boxset"
|
||||
' attempt to play all movies in the boxset
|
||||
' play them in order of release
|
||||
m.global.queueManager.callFunc("clear")
|
||||
|
||||
data = api.items.GetByQuery({
|
||||
"userid": m.global.session.user.id,
|
||||
"parentid": itemNode.id,
|
||||
"EnableTotalRecordCount": false
|
||||
})
|
||||
if isValid(data) and isValid(data.Items) and data.Items.count() > 0
|
||||
' there are videos inside
|
||||
print "found videos inside boxset"
|
||||
for each item in data.Items
|
||||
m.global.queueManager.callFunc("push", item)
|
||||
end for
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
end if
|
||||
else if itemType = "series"
|
||||
' attempt to play first unwatched episode
|
||||
m.global.queueManager.callFunc("clear")
|
||||
|
||||
data = api.shows.GetNextUp({
|
||||
"seriesId": itemNode.id,
|
||||
"recursive": true,
|
||||
"SortBy": "DatePlayed",
|
||||
"SortOrder": "Descending",
|
||||
"ImageTypeLimit": 1,
|
||||
"UserId": m.global.session.user.id,
|
||||
"EnableRewatching": false,
|
||||
"DisableFirstEpisode": false,
|
||||
"EnableTotalRecordCount": false
|
||||
})
|
||||
if isValid(data) and isValid(data.Items) and data.Items.count() > 0
|
||||
' there are unwatched episodes
|
||||
for each item in data.Items
|
||||
m.global.queueManager.callFunc("push", item)
|
||||
end for
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else
|
||||
' next up check was empty
|
||||
' check for a resumable episode
|
||||
data = api.users.GetResumeItemsByQuery(m.global.session.user.id, {
|
||||
"parentId": itemNode.id,
|
||||
"userid": m.global.session.user.id,
|
||||
"SortBy": "DatePlayed",
|
||||
"recursive": true,
|
||||
"SortOrder": "Descending",
|
||||
"Filters": "IsResumable",
|
||||
"EnableTotalRecordCount": false
|
||||
})
|
||||
print "resumeitems data=", data
|
||||
if isValid(data) and isValid(data.Items) and data.Items.count() > 0
|
||||
' play the resumable episode
|
||||
for each item in data.Items
|
||||
if isValid(item.UserData) and isValid(item.UserData.PlaybackPositionTicks)
|
||||
item.startingPoint = item.userdata.PlaybackPositionTicks
|
||||
end if
|
||||
m.global.queueManager.callFunc("push", item)
|
||||
end for
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else
|
||||
' shuffle all episodes
|
||||
data = api.shows.GetEpisodes(itemNode.id, {
|
||||
"userid": m.global.session.user.id,
|
||||
"SortBy": "Random",
|
||||
"EnableTotalRecordCount": false
|
||||
})
|
||||
|
||||
if isValid(data) and isValid(data.Items) and data.Items.count() > 0
|
||||
' add all episodes found to a playlist
|
||||
for each item in data.Items
|
||||
m.global.queueManager.callFunc("push", item)
|
||||
end for
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
else if itemType = "collectionfolder"
|
||||
' play depends on the kind of files inside the collectionfolder
|
||||
else if itemType = "season"
|
||||
' play first unwatched episode
|
||||
m.global.queueManager.callFunc("clear")
|
||||
|
||||
unwatchedData = api.shows.GetEpisodes(itemNode.json.SeriesId, {
|
||||
"seasonId": itemNode.id,
|
||||
"userid": m.global.session.user.id,
|
||||
"EnableTotalRecordCount": false
|
||||
})
|
||||
|
||||
if isValid(unwatchedData) and isValid(unwatchedData.Items)
|
||||
' find the first unwatched episode
|
||||
firstUnwatchedEpisodeIndex = invalid
|
||||
for each item in unwatchedData.Items
|
||||
if isValid(item.UserData)
|
||||
if isValid(item.UserData.Played) and item.UserData.Played = false
|
||||
firstUnwatchedEpisodeIndex = item.IndexNumber - 1
|
||||
if isValid(item.UserData.PlaybackPositionTicks)
|
||||
item.startingPoint = item.UserData.PlaybackPositionTicks
|
||||
end if
|
||||
exit for
|
||||
end if
|
||||
end if
|
||||
end for
|
||||
if isValid(firstUnwatchedEpisodeIndex)
|
||||
for i = firstUnwatchedEpisodeIndex to unwatchedData.Items.count() - 1
|
||||
m.global.queueManager.callFunc("push", unwatchedData.Items[i])
|
||||
end for
|
||||
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else
|
||||
' try to find a "continue watching" episode
|
||||
continueData = api.users.GetResumeItemsByQuery(m.global.session.user.id, {
|
||||
"parentId": itemNode.id,
|
||||
"userid": m.global.session.user.id,
|
||||
"SortBy": "DatePlayed",
|
||||
"recursive": true,
|
||||
"SortOrder": "Descending",
|
||||
"Filters": "IsResumable",
|
||||
"EnableTotalRecordCount": false
|
||||
})
|
||||
print "resumeitems continueData=", continueData
|
||||
if isValid(continueData) and isValid(continueData.Items) and continueData.Items.count() > 0
|
||||
' play the resumable episode
|
||||
for each item in continueData.Items
|
||||
if isValid(item.UserData) and isValid(item.UserData.PlaybackPositionTicks)
|
||||
item.startingPoint = item.userdata.PlaybackPositionTicks
|
||||
end if
|
||||
m.global.queueManager.callFunc("push", item)
|
||||
end for
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else
|
||||
' play the whole season in order
|
||||
if isValid(unwatchedData) and isValid(unwatchedData.Items) and unwatchedData.Items.count() > 0
|
||||
' add all episodes found to a playlist
|
||||
for each item in unwatchedData.Items
|
||||
m.global.queueManager.callFunc("push", item)
|
||||
end for
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
else if isNodeEvent(msg, "selectedItem")
|
||||
|
|
|
@ -565,6 +565,7 @@ function CreateMovieDetailsGroup(movie as object) as dynamic
|
|||
end if
|
||||
' start building MovieDetails view
|
||||
group = CreateObject("roSGNode", "MovieDetails")
|
||||
group.observeField("quickPlayNode", m.port)
|
||||
group.overhangTitle = movie.title
|
||||
group.optionsAvailable = false
|
||||
group.trailerAvailable = false
|
||||
|
@ -618,6 +619,7 @@ function CreateSeriesDetailsGroup(seriesID as string) as dynamic
|
|||
group.seasonData = seasonData
|
||||
' watch for button presses
|
||||
group.observeField("seasonSelected", m.port)
|
||||
group.observeField("quickPlayNode", m.port)
|
||||
' setup and load series extras
|
||||
extras = group.findNode("extrasGrid")
|
||||
extras.observeField("selectedItem", m.port)
|
||||
|
@ -670,6 +672,7 @@ function CreateArtistView(artist as object) as dynamic
|
|||
group.observeField("appearsOnSelected", m.port)
|
||||
end if
|
||||
|
||||
group.observeField("quickPlayNode", m.port)
|
||||
m.global.sceneManager.callFunc("pushScene", group)
|
||||
|
||||
return group
|
||||
|
@ -806,6 +809,7 @@ function CreateMusicLibraryView(libraryItem as object) as dynamic
|
|||
group.parentItem = libraryItem
|
||||
group.optionsAvailable = true
|
||||
group.observeField("selectedItem", m.port)
|
||||
group.observeField("quickPlayNode", m.port)
|
||||
return group
|
||||
end function
|
||||
|
||||
|
|
|
@ -223,15 +223,20 @@ function AppearsOnList(id as string)
|
|||
end function
|
||||
|
||||
' Get list of songs belonging to an artist
|
||||
function GetSongsByArtist(id as string)
|
||||
function GetSongsByArtist(id as string, params = {} as object)
|
||||
url = Substitute("Users/{0}/Items", m.global.session.user.id)
|
||||
resp = APIRequest(url, {
|
||||
paramArray = {
|
||||
"AlbumArtistIds": id,
|
||||
"includeitemtypes": "Audio",
|
||||
"sortBy": "SortName",
|
||||
"Recursive": true
|
||||
})
|
||||
}
|
||||
' overwrite defaults with the params provided
|
||||
for each param in params
|
||||
paramArray.AddReplace(param, params[param])
|
||||
end for
|
||||
|
||||
resp = APIRequest(url, paramArray)
|
||||
data = getJson(resp)
|
||||
results = []
|
||||
|
||||
|
@ -408,7 +413,7 @@ function TVSeasons(id as string) as dynamic
|
|||
results = []
|
||||
for each item in data.Items
|
||||
imgParams = { "AddPlayedIndicator": item.UserData.Played }
|
||||
tmp = CreateObject("roSGNode", "TVEpisodeData")
|
||||
tmp = CreateObject("roSGNode", "TVSeasonData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
|
|
Loading…
Reference in New Issue
Block a user