Merge pull request #1279 from jimdogx/enhancement/jf-1276-tv-versions
Allow selection of TV Show versions
This commit is contained in:
commit
6c80e8c923
|
@ -11,6 +11,7 @@
|
|||
<field id="type" type="string" value="Episode" />
|
||||
<field id="startingPoint" type="longinteger" value="0" />
|
||||
<field id="json" type="assocarray" onChange="setFields" />
|
||||
<field id="selectedVideoStreamId" type="string" />
|
||||
<field id="selectedAudioStreamIndex" type="integer" />
|
||||
<field id="favorite" type="boolean" />
|
||||
</interface>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import "pkg:/source/utils/misc.brs"
|
||||
|
||||
sub init()
|
||||
m.rows = m.top.findNode("tvEpisodeRow")
|
||||
m.tvListOptions = m.top.findNode("tvListOptions")
|
||||
|
@ -14,26 +16,55 @@ sub rowsDoneLoading()
|
|||
m.top.doneLoading = true
|
||||
end sub
|
||||
|
||||
sub SetUpAudioOptions(streams)
|
||||
' List of video versions to choose from
|
||||
sub SetUpVideoOptions(streams as object)
|
||||
videos = []
|
||||
|
||||
for i = 0 to streams.Count() - 1
|
||||
if LCase(streams[i].VideoType) = "videofile"
|
||||
' Create options for user to switch between video tracks
|
||||
videos.push({
|
||||
"Title": streams[i].Name,
|
||||
"Description": tr("Video"),
|
||||
"Selected": m.top.objects.items[m.currentSelected].selectedVideoStreamId = streams[i].id,
|
||||
"StreamID": streams[i].id,
|
||||
"video_codec": streams[i].mediaStreams[0].displayTitle
|
||||
})
|
||||
end if
|
||||
end for
|
||||
|
||||
if videos.count() >= 1
|
||||
options = {}
|
||||
options.videos = videos
|
||||
m.tvListOptions.options = options
|
||||
end if
|
||||
end sub
|
||||
|
||||
' List of audio tracks to choose from
|
||||
sub SetUpAudioOptions(streams as object)
|
||||
tracks = []
|
||||
|
||||
for i = 0 to streams.Count() - 1
|
||||
if streams[i].Type = "Audio"
|
||||
tracks.push({ "Title": streams[i].displayTitle, "Description": streams[i].Title, "Selected": m.top.objects.items[m.currentSelected].selectedAudioStreamIndex = i, "StreamIndex": i })
|
||||
tracks.push({
|
||||
"Title": streams[i].displayTitle,
|
||||
"Description": streams[i].Title,
|
||||
"Selected": m.top.objects.items[m.currentSelected].selectedAudioStreamIndex = i,
|
||||
"StreamIndex": i
|
||||
})
|
||||
end if
|
||||
end for
|
||||
|
||||
if tracks.count() > 1
|
||||
if tracks.count() >= 1
|
||||
options = {}
|
||||
if isValid(m.tvListOptions.options) and isValid(m.tvListOptions.options.videos)
|
||||
options.videos = m.tvListOptions.options.videos
|
||||
end if
|
||||
options.audios = tracks
|
||||
m.tvListOptions.options = options
|
||||
m.tvListOptions.visible = true
|
||||
m.tvListOptions.setFocus(true)
|
||||
end if
|
||||
end sub
|
||||
|
||||
'
|
||||
'Check if options updated and any reloading required
|
||||
sub audioOptionsClosed()
|
||||
if m.currentSelected <> invalid
|
||||
' If the user opened the audio options, we report back even if they left the selection alone.
|
||||
|
@ -43,18 +74,43 @@ sub audioOptionsClosed()
|
|||
end if
|
||||
end sub
|
||||
|
||||
sub videoOptionsClosed()
|
||||
if m.tvListOptions.videoStreamId <> m.top.objects.items[m.currentSelected].selectedVideoStreamId
|
||||
m.rows.objects.items[m.currentSelected].selectedVideoStreamId = m.tvListOptions.videoStreamId
|
||||
end if
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "options" and m.rows.focusedChild <> invalid and m.rows.focusedChild.rowItemFocused <> invalid
|
||||
if key = "options" and isValid(m.rows.focusedChild) and isValid(m.rows.focusedChild.rowItemFocused)
|
||||
m.currentSelected = m.rows.focusedChild.rowItemFocused[0]
|
||||
mediaStreams = m.rows.objects.items[m.currentSelected].json.MediaStreams
|
||||
SetUpAudioOptions(mediaStreams)
|
||||
mediaSources = m.rows.objects.items[m.currentSelected].json.MediaSources
|
||||
if m.rows.objects.items[m.currentSelected].selectedVideoStreamId <> ""
|
||||
for each source in mediaSources
|
||||
if source.id = m.rows.objects.items[m.currentSelected].selectedVideoStreamId
|
||||
mediaStreams = source.MediaStreams
|
||||
exit for
|
||||
end if
|
||||
end for
|
||||
end if
|
||||
if isValid(mediaSources)
|
||||
SetUpVideoOptions(mediaSources)
|
||||
end if
|
||||
if isValid(mediaStreams)
|
||||
SetUpAudioOptions(mediaStreams)
|
||||
end if
|
||||
if isValid(m.tvListOptions.options)
|
||||
m.tvListOptions.visible = true
|
||||
m.tvListOptions.setFocus(true)
|
||||
end if
|
||||
return true
|
||||
else if m.tvListOptions.visible = true and key = "back" or key = "options"
|
||||
m.tvListOptions.setFocus(false)
|
||||
m.tvListOptions.visible = false
|
||||
m.rows.setFocus(true)
|
||||
videoOptionsClosed()
|
||||
audioOptionsClosed()
|
||||
return true
|
||||
else if key = "up" and m.rows.hasFocus() = false
|
||||
|
|
|
@ -42,7 +42,7 @@ end sub
|
|||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
handled = false
|
||||
|
||||
if key = "left" and not m.Shuffle.hasFocus()
|
||||
if key = "left" and m.tvEpisodeRow.hasFocus()
|
||||
m.Shuffle.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
@ -57,7 +57,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
return true
|
||||
end if
|
||||
|
||||
if key = "right" and not m.tvEpisodeRow.hasFocus()
|
||||
if key = "right" and (m.Random.hasFocus() or m.Shuffle.hasFocus())
|
||||
m.tvEpisodeRow.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
|
|
@ -4,7 +4,6 @@ import "pkg:/source/utils/config.brs"
|
|||
sub init()
|
||||
m.title = m.top.findNode("title")
|
||||
m.title.text = tr("Loading...")
|
||||
m.options = m.top.findNode("tvListOptions")
|
||||
m.overview = m.top.findNode("overview")
|
||||
m.poster = m.top.findNode("poster")
|
||||
|
||||
|
@ -15,11 +14,19 @@ sub init()
|
|||
m.playedIndicator = m.top.findNode("playedIndicator")
|
||||
m.checkmark = m.top.findNode("checkmark")
|
||||
m.checkmark.font.size = 35
|
||||
|
||||
m.videoCodec = m.top.findNode("video_codec")
|
||||
end sub
|
||||
|
||||
sub itemContentChanged()
|
||||
item = m.top.itemContent
|
||||
itemData = item.json
|
||||
|
||||
' Set default video source if user hasn't selected one yet
|
||||
if item.selectedVideoStreamId = "" and isValid(itemData.MediaSources)
|
||||
item.selectedVideoStreamId = itemData.MediaSources[0].id
|
||||
end if
|
||||
|
||||
if isValid(itemData.indexNumber)
|
||||
indexNumber = itemData.indexNumber.toStr() + ". "
|
||||
else
|
||||
|
@ -85,36 +92,62 @@ sub itemContentChanged()
|
|||
m.progressBar.visible = true
|
||||
end if
|
||||
|
||||
videoIdx = invalid
|
||||
audioIdx = invalid
|
||||
' Display current video_codec and check if there is more than one video to choose from...
|
||||
m.videoCodec.visible = false
|
||||
if isValid(itemData.MediaSources)
|
||||
for i = 0 to itemData.MediaSources.Count() - 1
|
||||
if item.selectedVideoStreamId = itemData.MediaSources[i].id
|
||||
m.videoCodec.text = tr("Video") + ": " + itemData.MediaSources[i].MediaStreams[0].DisplayTitle
|
||||
SetupAudioDisplay(itemData.MediaSources[i].MediaStreams, item.selectedAudioStreamIndex)
|
||||
exit for
|
||||
end if
|
||||
end for
|
||||
m.videoCodec.visible = true
|
||||
DisplayVideoAvailable(itemData.MediaSources)
|
||||
end if
|
||||
end sub
|
||||
|
||||
if isValid(itemData.MediaStreams)
|
||||
for i = 0 to itemData.MediaStreams.Count() - 1
|
||||
if itemData.MediaStreams[i].Type = "Video" and videoIdx = invalid
|
||||
videoIdx = i
|
||||
m.top.findNode("video_codec").text = tr("Video") + ": " + itemData.mediaStreams[videoIdx].DisplayTitle
|
||||
else if itemData.MediaStreams[i].Type = "Audio" and audioIdx = invalid
|
||||
if item.selectedAudioStreamIndex > 1
|
||||
audioIdx = item.selectedAudioStreamIndex
|
||||
' Display current audio_codec and check if there is more than one audio track to choose from...
|
||||
sub SetupAudioDisplay(mediaStreams as object, selectedAudioStreamIndex as integer)
|
||||
audioIdx = invalid
|
||||
if isValid(mediaStreams)
|
||||
for i = 0 to mediaStreams.Count() - 1
|
||||
if LCase(mediaStreams[i].Type) = "audio" and audioIdx = invalid
|
||||
if selectedAudioStreamIndex > 0 and selectedAudioStreamIndex < mediaStreams.Count()
|
||||
audioIdx = selectedAudioStreamIndex
|
||||
else
|
||||
audioIdx = i
|
||||
end if
|
||||
m.top.findNode("audio_codec").text = tr("Audio") + ": " + itemData.mediaStreams[audioIdx].DisplayTitle
|
||||
m.top.findNode("audio_codec").text = tr("Audio") + ": " + mediaStreams[audioIdx].DisplayTitle
|
||||
end if
|
||||
if isValid(videoIdx) and isValid(audioIdx) then exit for
|
||||
if isValid(audioIdx) then exit for
|
||||
end for
|
||||
end if
|
||||
|
||||
m.top.findNode("video_codec").visible = isValid(videoIdx)
|
||||
if isValid(audioIdx)
|
||||
m.top.findNode("audio_codec").visible = true
|
||||
DisplayAudioAvailable(itemData.mediaStreams)
|
||||
DisplayAudioAvailable(mediaStreams)
|
||||
else
|
||||
m.top.findNode("audio_codec").visible = false
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub DisplayAudioAvailable(streams)
|
||||
' Adds "+N" (e.g. +1) if there is more than one video version to choose from
|
||||
sub DisplayVideoAvailable(streams as object)
|
||||
count = 0
|
||||
for i = 0 to streams.Count() - 1
|
||||
if LCase(streams[i].VideoType) = "videofile"
|
||||
count++
|
||||
end if
|
||||
end for
|
||||
|
||||
if count > 1
|
||||
m.top.findnode("video_codec_count").text = "+" + stri(count - 1).trim()
|
||||
end if
|
||||
end sub
|
||||
|
||||
' Adds "+N" (e.g. +1) if there is more than one audio track to choose from
|
||||
sub DisplayAudioAvailable(streams as object)
|
||||
count = 0
|
||||
for i = 0 to streams.Count() - 1
|
||||
if streams[i].Type = "Audio"
|
||||
|
|
|
@ -26,8 +26,9 @@
|
|||
</LayoutGroup>
|
||||
<Label id="overview" font="font:SmallestSystemFont" wrap="true" height="130" width="950" maxLines="3" ellipsizeOnBoundary="true" />
|
||||
<LayoutGroup layoutDirection="horiz" itemSpacings="[15]">
|
||||
<Label id="video_codec" font="font:SmallestSystemFont" />
|
||||
<ScrollingLabel id="audio_codec" font="font:SmallestSystemFont" />
|
||||
<ScrollingLabel id="video_codec" font="font:SmallestSystemFont" maxWidth="400" />
|
||||
<label id="video_codec_count" font="font:smallestSystemFont" vertAlign="top" color="#ceffff" />
|
||||
<ScrollingLabel id="audio_codec" font="font:SmallestSystemFont" maxWidth="400" />
|
||||
<label id="audio_codec_count" font="font:smallestSystemFont" vertAlign="top" color="#ceffff" />
|
||||
</LayoutGroup>
|
||||
</LayoutGroup>
|
||||
|
@ -36,6 +37,7 @@
|
|||
</children>
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="itemContentChanged" />
|
||||
<field id="selectedVideoStreamId" type="string" />
|
||||
<field id="selectedAudioStreamIndex" type="integer" />
|
||||
<field id="itemHasFocus" type="boolean" onChange="focusChanged" />
|
||||
</interface>
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
' You may be wondering what's with all the array stuff (m.menus, m.buttons, m.selectedItem etc)?
|
||||
' This was copied from Movie Options which has both Video and Audio options.
|
||||
' At the moment we only have Audio Options for TV Episodes, but the code here
|
||||
' is ready to be expanded easily in the future to add in additional options.
|
||||
import "pkg:/source/utils/misc.brs"
|
||||
|
||||
sub init()
|
||||
|
||||
m.buttons = m.top.findNode("buttons")
|
||||
m.buttons.buttons = [tr("Audio")]
|
||||
m.buttons.buttons = [tr("Video"), tr("Audio")]
|
||||
m.buttons.selectedIndex = 0
|
||||
m.buttons.setFocus(true)
|
||||
|
||||
m.selectedItem = 0
|
||||
m.selectedAudioIndex = 0
|
||||
m.selectedVideoIndex = 0
|
||||
|
||||
m.menus = [m.top.findNode("audioMenu")]
|
||||
m.menus = [m.top.findNode("videoMenu"), m.top.findNode("audioMenu")]
|
||||
|
||||
m.videoNames = []
|
||||
m.audioNames = []
|
||||
|
||||
' Set button colors to global
|
||||
m.top.findNode("videoMenu").focusBitmapBlendColor = m.global.constants.colors.button
|
||||
m.top.findNode("audioMenu").focusBitmapBlendColor = m.global.constants.colors.button
|
||||
|
||||
' Animation
|
||||
|
@ -30,8 +31,35 @@ sub init()
|
|||
end sub
|
||||
|
||||
sub optionsSet()
|
||||
' Videos Tab
|
||||
if isValid(m.top.options.videos)
|
||||
viewContent = CreateObject("roSGNode", "ContentNode")
|
||||
index = 0
|
||||
selectedViewIndex = 0
|
||||
|
||||
for each view in m.top.options.videos
|
||||
entry = viewContent.CreateChild("VideoTrackListData")
|
||||
entry.title = view.Title
|
||||
entry.description = view.Description
|
||||
entry.streamId = view.streamId
|
||||
entry.video_codec = view.video_codec
|
||||
m.videoNames.push(view.Name)
|
||||
if isValid(view.Selected) and view.Selected
|
||||
selectedViewIndex = index
|
||||
entry.selected = true
|
||||
m.top.videoStreamId = view.streamId
|
||||
end if
|
||||
index = index + 1
|
||||
end for
|
||||
|
||||
m.menus[0].content = viewContent
|
||||
m.menus[0].jumpToItem = selectedViewIndex
|
||||
m.menus[0].checkedItem = selectedViewIndex
|
||||
m.selectedVideoIndex = selectedViewIndex
|
||||
end if
|
||||
|
||||
' audio Tab
|
||||
if m.top.Options.audios <> invalid
|
||||
if isValid(m.top.options.audios)
|
||||
audioContent = CreateObject("roSGNode", "ContentNode")
|
||||
index = 0
|
||||
selectedAudioIndex = 0
|
||||
|
@ -42,7 +70,7 @@ sub optionsSet()
|
|||
entry.description = audio.Description
|
||||
entry.streamIndex = audio.StreamIndex
|
||||
m.audioNames.push(audio.Name)
|
||||
if audio.Selected <> invalid and audio.Selected = true
|
||||
if isValid(audio.Selected) and audio.Selected
|
||||
selectedAudioIndex = index
|
||||
entry.selected = true
|
||||
m.top.audioStreamIndex = audio.streamIndex
|
||||
|
@ -50,9 +78,9 @@ sub optionsSet()
|
|||
index = index + 1
|
||||
end for
|
||||
|
||||
m.menus[0].content = audioContent
|
||||
m.menus[0].jumpToItem = selectedAudioIndex
|
||||
m.menus[0].checkedItem = selectedAudioIndex
|
||||
m.menus[1].content = audioContent
|
||||
m.menus[1].jumpToItem = selectedAudioIndex
|
||||
m.menus[1].checkedItem = selectedAudioIndex
|
||||
m.selectedAudioIndex = selectedAudioIndex
|
||||
end if
|
||||
|
||||
|
@ -88,17 +116,25 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
selMenu = m.menus[m.selectedItem]
|
||||
selIndex = selMenu.itemSelected
|
||||
|
||||
' Audio options
|
||||
'if m.selectedItem = 0
|
||||
if m.selectedAudioIndex = selIndex
|
||||
else
|
||||
selMenu.content.GetChild(m.selectedAudioIndex).selected = false
|
||||
newSelection = selMenu.content.GetChild(selIndex)
|
||||
newSelection.selected = true
|
||||
m.selectedAudioIndex = selIndex
|
||||
m.top.audioStreamIndex = newSelection.streamIndex
|
||||
'Handle Videos menu
|
||||
if m.selectedItem = 0
|
||||
if m.selectedVideoIndex <> selIndex
|
||||
selMenu.content.GetChild(m.selectedVideoIndex).selected = false
|
||||
newSelection = selMenu.content.GetChild(selIndex)
|
||||
newSelection.selected = true
|
||||
m.selectedVideoIndex = selIndex
|
||||
m.top.videoStreamId = newSelection.streamId
|
||||
end if
|
||||
' Then it is Audio options
|
||||
else if m.selectedItem = 1
|
||||
if m.selectedAudioIndex <> selIndex
|
||||
selMenu.content.GetChild(m.selectedAudioIndex).selected = false
|
||||
newSelection = selMenu.content.GetChild(selIndex)
|
||||
newSelection.selected = true
|
||||
m.selectedAudioIndex = selIndex
|
||||
m.top.audioStreamIndex = newSelection.streamIndex
|
||||
end if
|
||||
end if
|
||||
'end if
|
||||
|
||||
end if
|
||||
return true
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<LayoutGroup horizAlignment="center" translation="[860,50]" itemSpacings="[50]">
|
||||
<JFButtons id="buttons" />
|
||||
<Group>
|
||||
<RadiobuttonList id="audioMenu" itemspacing="[0,10]" opacity="1" vertFocusAnimationStyle="floatingFocus" drawFocusFeedback="false" />
|
||||
<RadiobuttonList id="videoMenu" itemspacing="[0,10]" opacity="1" vertFocusAnimationStyle="floatingFocus" drawFocusFeedback="false" itemSize="[1500, 70]" numRows="8" />
|
||||
<RadiobuttonList id="audioMenu" itemspacing="[0,10]" opacity="0" vertFocusAnimationStyle="floatingFocus" drawFocusFeedback="false" itemSize="[1500, 70]" numRows="8" />
|
||||
</Group>
|
||||
</LayoutGroup>
|
||||
</Group>
|
||||
|
@ -21,6 +22,7 @@
|
|||
<interface>
|
||||
<field id="buttons" type="nodearray" />
|
||||
<field id="options" type="assocarray" onChange="optionsSet" />
|
||||
<field id="videoStreamId" type="string" />
|
||||
<field id="audioStreamIndex" type="integer" />
|
||||
</interface>
|
||||
</component>
|
|
@ -121,6 +121,10 @@ sub Main (args as dynamic) as void
|
|||
itemNode = reportingNode.quickPlayNode
|
||||
if isValid(itemNode) and isValid(itemNode.id) and itemNode.id <> ""
|
||||
if itemNode.type = "Episode" or itemNode.type = "Movie" or itemNode.type = "Video"
|
||||
if isValid(itemNode.selectedVideoStreamId)
|
||||
itemNode.id = itemNode.selectedVideoStreamId
|
||||
end if
|
||||
|
||||
audio_stream_idx = 0
|
||||
if isValid(itemNode.selectedAudioStreamIndex) and itemNode.selectedAudioStreamIndex > 0
|
||||
audio_stream_idx = itemNode.selectedAudioStreamIndex
|
||||
|
|
|
@ -419,7 +419,7 @@ end function
|
|||
|
||||
function TVEpisodes(show_id as string, season_id as string) as dynamic
|
||||
url = Substitute("Shows/{0}/Episodes", show_id)
|
||||
resp = APIRequest(url, { "seasonId": season_id, "UserId": m.global.session.user.id, "fields": "MediaStreams" })
|
||||
resp = APIRequest(url, { "seasonId": season_id, "UserId": m.global.session.user.id, "fields": "MediaStreams,MediaSources" })
|
||||
|
||||
data = getJson(resp)
|
||||
' validate data
|
||||
|
|
Loading…
Reference in New Issue
Block a user