Allow selection of different audio tracks for tv shows

This commit is contained in:
Jimi 2022-04-23 21:49:42 -06:00
parent e9509a68c5
commit 5ffa09f602
12 changed files with 292 additions and 15 deletions

View File

@ -10,6 +10,7 @@
<field id="overview" type="string" /> <field id="overview" type="string" />
<field id="type" type="string" value="Episode" /> <field id="type" type="string" value="Episode" />
<field id="json" type="assocarray" onChange="setFields" /> <field id="json" type="assocarray" onChange="setFields" />
<field id="selectedAudioStreamIndex" type="integer" />
</interface> </interface>
<script type="text/brightscript" uri="TVEpisodeData.brs" /> <script type="text/brightscript" uri="TVEpisodeData.brs" />
</component> </component>

View File

@ -52,6 +52,8 @@ function setData()
row.appendChild(item) row.appendChild(item)
end for end for
m.top.doneLoading = true
return data return data
end function end function

View File

@ -3,6 +3,7 @@
<interface> <interface>
<field id="objects" type="assocarray" onChange="setupRows" /> <field id="objects" type="assocarray" onChange="setupRows" />
<field id="escapeButton" type="string" alwaysNotify="true" /> <field id="escapeButton" type="string" alwaysNotify="true" />
<field id="doneLoading" type="boolean" />
</interface> </interface>
<script type="text/brightscript" uri="TVEpisodeRow.brs" /> <script type="text/brightscript" uri="TVEpisodeRow.brs" />
</component> </component>

View File

@ -0,0 +1,64 @@
sub init()
m.rows = m.top.findNode("tvEpisodeRow")
m.tvListOptions = m.top.findNode("tvListOptions")
m.rows.observeField("doneLoading", "rowsDoneLoading")
end sub
sub setupRows()
objects = m.top.objects
m.rows.objects = objects
end sub
sub rowsDoneLoading()
m.top.doneLoading = true
end sub
sub SetUpAudioOptions(streams)
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 })
end if
end for
if tracks.count() > 1
options = {}
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.tvListOptions.audioStreamIndex <> m.top.selectedAudioStreamIndex
m.top.objects.items[m.currentSelected].selectedAudioStreamIndex = m.tvListOptions.audioStreamIndex
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.rowItemFocused <> invalid
m.currentSelected = m.rows.focusedChild.rowItemFocused[0]
mediaStreams = m.rows.objects.items[m.currentSelected].json.MediaStreams
SetUpAudioOptions(mediaStreams)
return true
else if key = "back" and m.tvListOptions.visible = true
m.tvListOptions.setFocus(false)
m.tvListOptions.visible = false
m.rows.setFocus(true)
audioOptionsClosed()
return true
else if key = "up" and m.rows.hasFocus() = false
m.rows.setFocus(true)
else if key = "down" and m.rows.hasFocus() = false
m.rows.setFocus(true)
end if
return false
end function

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="TVEpisodeRowWithOptions" extends="Group">
<children>
<TVEpisodeRow id="tvEpisodeRow" visible="true" />
<TVListOptions id="tvListOptions" visible="false" />
</children>
<interface>
<field id="objects" type="assocarray" onChange="setupRows"/>
<field id="itemSelected" type="integer" />
<field id="doneLoading" type="boolean" />
</interface>
<script type="text/brightscript" uri="TVEpisodeRowWithOptions.brs" />
</component>

View File

@ -1,21 +1,36 @@
sub init() sub init()
m.top.optionsAvailable = false m.top.optionsAvailable = false
m.rows = m.top.findNode("picker")
m.rows.observeField("doneLoading", "updateSeason")
end sub end sub
sub setSeason() sub setSeasonLoading()
m.top.overhangTitle = tr("Loading...")
end sub
sub updateSeason()
m.top.overhangTitle = m.top.seasonData.SeriesName + " - " + m.top.seasonData.name m.top.overhangTitle = m.top.seasonData.SeriesName + " - " + m.top.seasonData.name
end sub end sub
function onKeyEvent(key as string, press as boolean) as boolean function onKeyEvent(key as string, press as boolean) as boolean
handled = false handled = false
if press
if key = "play" focusedChild = m.top.focusedChild.focusedChild
itemToPlay = m.top.focusedChild.content.getChild(m.top.focusedChild.rowItemFocused[0]).getChild(0) if focusedChild.content = invalid then return handled
if itemToPlay <> invalid and itemToPlay.id <> ""
m.top.quickPlayNode = itemToPlay ' OK needs to be handled on release...
end if proceed = false
handled = true if key = "OK"
proceed = true
end if
if press and key = "play" or proceed = true
itemToPlay = focusedChild.content.getChild(focusedChild.rowItemFocused[0]).getChild(0)
if itemToPlay <> invalid and itemToPlay.id <> ""
m.top.quickPlayNode = itemToPlay
end if end if
handled = true
end if end if
return handled return handled
end function end function

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<component name="TVEpisodes" extends="JFGroup"> <component name="TVEpisodes" extends="JFGroup">
<children> <children>
<TVEpisodeRow id="picker" visible="true" /> <TVEpisodeRowWithOptions id="picker" visible="true" />
</children> </children>
<interface> <interface>
<field id="episodeSelected" alias="picker.itemSelected" /> <field id="episodeSelected" alias="picker.itemSelected" />
<field id="quickPlayNode" type="node" alwaysNotify="true" /> <field id="quickPlayNode" type="node" alwaysNotify="true" />
<field id="seasonData" type="assocarray" onChange="setSeason" /> <field id="seasonData" type="assocarray" onChange="setSeasonLoading" />
<field id="objects" alias="picker.objects" /> <field id="objects" alias="picker.objects" />
</interface> </interface>
<script type="text/brightscript" uri="TVEpisodes.brs" /> <script type="text/brightscript" uri="TVEpisodes.brs" />

View File

@ -1,6 +1,7 @@
sub init() sub init()
m.title = m.top.findNode("title") m.title = m.top.findNode("title")
m.title.text = tr("Loading...") m.title.text = tr("Loading...")
m.options = m.top.findNode("tvListOptions")
end sub end sub
sub itemContentChanged() sub itemContentChanged()
@ -34,7 +35,11 @@ sub itemContentChanged()
if itemData.MediaStreams[i].Type = "Video" and videoIdx = invalid if itemData.MediaStreams[i].Type = "Video" and videoIdx = invalid
videoIdx = i videoIdx = i
else if itemData.MediaStreams[i].Type = "Audio" and audioIdx = invalid else if itemData.MediaStreams[i].Type = "Audio" and audioIdx = invalid
audioIdx = i if item.selectedAudioStreamIndex > 1
audioIdx = item.selectedAudioStreamIndex
else
audioIdx = i
end if
end if end if
if videoIdx <> invalid and audioIdx <> invalid then exit for if videoIdx <> invalid and audioIdx <> invalid then exit for
end for end for
@ -46,6 +51,23 @@ sub itemContentChanged()
m.top.findNode("video_codec").visible = false m.top.findNode("video_codec").visible = false
m.top.findNode("audio_codec").visible = false m.top.findNode("audio_codec").visible = false
end if end if
DisplayAudioAvailable(itemData.mediaStreams)
end sub
sub DisplayAudioAvailable(streams)
count = 0
for i = 0 to streams.Count() - 1
if streams[i].Type = "Audio"
count++
end if
end for
if count > 1
m.top.findnode("audio_codec_count").text = "+" + stri(count - 1).trim()
end if
end sub end sub
function getRuntime() as integer function getRuntime() as integer

View File

@ -19,7 +19,8 @@
<Label id="overview" wrap="true" height="170" width="1200" maxLines="4" ellipsizeOnBoundary="true"/> <Label id="overview" wrap="true" height="170" width="1200" maxLines="4" ellipsizeOnBoundary="true"/>
<LayoutGroup layoutDirection="horiz" itemSpacings="[15]"> <LayoutGroup layoutDirection="horiz" itemSpacings="[15]">
<Label id="video_codec" /> <Label id="video_codec" />
<Label id="audio_codec" /> <ScrollingLabel id="audio_codec" />
<label id="audio_codec_count" font="font:smallestSystemFont" vertAlign="top" color="#ceffff" />
</LayoutGroup> </LayoutGroup>
</LayoutGroup> </LayoutGroup>
</LayoutGroup> </LayoutGroup>
@ -27,6 +28,7 @@
</children> </children>
<interface> <interface>
<field id="itemContent" type="node" onChange="itemContentChanged"/> <field id="itemContent" type="node" onChange="itemContentChanged"/>
<field id="selectedAudioStreamIndex" type="integer" />
</interface> </interface>
<script type="text/brightscript" uri="TVListDetails.brs" /> <script type="text/brightscript" uri="TVListDetails.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" /> <script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />

View File

@ -0,0 +1,118 @@
' 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.
sub init()
m.buttons = m.top.findNode("buttons")
m.buttons.buttons = [tr("Audio")]
m.buttons.selectedIndex = 0
m.buttons.setFocus(true)
m.selectedItem = 0
m.selectedAudioIndex = 0
m.menus = [m.top.findNode("audioMenu")]
m.audioNames = []
' Set button colors to global
m.top.findNode("audioMenu").focusBitmapBlendColor = m.global.constants.colors.button
' Animation
m.fadeAnim = m.top.findNode("fadeAnim")
m.fadeOutAnimOpacity = m.top.findNode("outOpacity")
m.fadeInAnimOpacity = m.top.findNode("inOpacity")
m.buttons.observeField("focusedIndex", "buttonFocusChanged")
m.buttons.focusedIndex = m.selectedItem
end sub
sub optionsSet()
' audio Tab
if m.top.Options.audios <> invalid
audioContent = CreateObject("roSGNode", "ContentNode")
index = 0
selectedAudioIndex = 0
for each audio in m.top.options.audios
entry = audioContent.CreateChild("AudioTrackListData")
entry.title = audio.Title
entry.description = audio.Description
entry.streamIndex = audio.StreamIndex
m.audioNames.push(audio.Name)
if audio.Selected <> invalid and audio.Selected = true
selectedAudioIndex = index
entry.selected = true
m.top.audioStreamIndex = audio.streamIndex
end if
index = index + 1
end for
m.menus[0].content = audioContent
m.menus[0].jumpToItem = selectedAudioIndex
m.menus[0].checkedItem = selectedAudioIndex
m.selectedAudioIndex = selectedAudioIndex
end if
end sub
' Switch menu shown when button focus changes
sub buttonFocusChanged()
if m.buttons.focusedIndex = m.selectedItem then return
m.fadeOutAnimOpacity.fieldToInterp = m.menus[m.selectedItem].id + ".opacity"
m.fadeInAnimOpacity.fieldToInterp = m.menus[m.buttons.focusedIndex].id + ".opacity"
m.fadeAnim.control = "start"
m.selectedItem = m.buttons.focusedIndex
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if key = "down" or (key = "OK" and m.top.findNode("buttons").hasFocus())
m.top.findNode("buttons").setFocus(false)
m.menus[m.selectedItem].setFocus(true)
m.menus[m.selectedItem].drawFocusFeedback = true
'If user presses down from button menu, focus first item. If OK, focus checked item
if key = "down"
m.menus[m.selectedItem].jumpToItem = 0
else
m.menus[m.selectedItem].jumpToItem = m.menus[m.selectedItem].itemSelected
end if
return true
else if key = "OK"
if m.menus[m.selectedItem].isInFocusChain()
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
end if
'end if
end if
return true
else if key = "back" or key = "up"
if m.menus[m.selectedItem].isInFocusChain()
m.buttons.setFocus(true)
m.menus[m.selectedItem].drawFocusFeedback = false
return true
end if
else if key = "options"
m.menus[m.selectedItem].drawFocusFeedback = false
return false
end if
return false
end function

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="TVListOptions" extends="Group">
<children>
<Rectangle width="1920" height="1080" color="#000000" opacity="0.75" />
<Group translation="[100,100]">
<Poster width="1720" height="880" uri="pkg:/images/dialog.9.png" />
<LayoutGroup horizAlignment="center" translation="[860,50]" itemSpacings="[50]">
<JFButtons id="buttons" />
<Group>
<RadiobuttonList id="audioMenu" itemspacing="[0,10]" opacity="1" vertFocusAnimationStyle="floatingFocus" drawFocusFeedback="false" />
</Group>
</LayoutGroup>
</Group>
<Animation id="fadeAnim" duration="0.5" repeat="false">
<FloatFieldInterpolator id="outOpacity" key="[0.0, 0.5, 1.0]" keyValue="[ 1, 0, 0 ]" fieldToInterp="focus.opacity" />
<FloatFieldInterpolator id="inOpacity" key="[0.0, 0.5, 1.0]" keyValue="[ 0, 0, 1 ]" fieldToInterp="focus.opacity" />
</Animation>
</children>
<interface>
<field id="buttons" type="nodearray" />
<field id="options" type="assocarray" onChange="optionsSet" />
<field id="audioStreamIndex" type="integer" />
</interface>
<script type="text/brightscript" uri="TVListOptions.brs" />
</component>

View File

@ -88,7 +88,11 @@ sub Main (args as dynamic) as void
itemNode = reportingNode.quickPlayNode itemNode = reportingNode.quickPlayNode
if itemNode = invalid or itemNode.id = "" then return if itemNode = invalid or itemNode.id = "" then return
if itemNode.type = "Episode" or itemNode.type = "Movie" or itemNode.type = "Video" if itemNode.type = "Episode" or itemNode.type = "Movie" or itemNode.type = "Video"
video = CreateVideoPlayerGroup(itemNode.id) if itemNode.type = "Episode" and itemNode.selectedAudioStreamIndex > 1
video = CreateVideoPlayerGroup(itemNode.id, invalid, itemNode.selectedAudioStreamIndex)
else
video = CreateVideoPlayerGroup(itemNode.id)
end if
if video <> invalid if video <> invalid
sceneManager.callFunc("pushScene", video) sceneManager.callFunc("pushScene", video)
end if end if
@ -103,7 +107,11 @@ sub Main (args as dynamic) as void
' play episode ' play episode
' todo: create an episode page to link here ' todo: create an episode page to link here
video_id = selectedItem.id video_id = selectedItem.id
video = CreateVideoPlayerGroup(video_id) if selectedItem.selectedAudioStreamIndex > 1
video = CreateVideoPlayerGroup(video_id, invalid, selectedItem.selectedAudioStreamIndex)
else
video = CreateVideoPlayerGroup(video_id)
end if
if video <> invalid if video <> invalid
sceneManager.callFunc("pushScene", video) sceneManager.callFunc("pushScene", video)
end if end if
@ -162,7 +170,11 @@ sub Main (args as dynamic) as void
' If you select a TV Episode from ANYWHERE, follow this flow ' If you select a TV Episode from ANYWHERE, follow this flow
node = getMsgPicker(msg, "picker") node = getMsgPicker(msg, "picker")
video_id = node.id video_id = node.id
video = CreateVideoPlayerGroup(video_id) if node.selectedAudioStreamIndex > 1
video = CreateVideoPlayerGroup(video_id, invalid, node.selectedAudioStreamIndex)
else
video = CreateVideoPlayerGroup(video_id)
end if
if video <> invalid if video <> invalid
sceneManager.callFunc("pushScene", video) sceneManager.callFunc("pushScene", video)
end if end if