Merge pull request #533 from jimdogx/feature/531-mulitple-movie-video-streams
This commit is contained in:
commit
f789867f5a
BIN
components/.DS_Store
vendored
Normal file
BIN
components/.DS_Store
vendored
Normal file
Binary file not shown.
|
@ -8,6 +8,13 @@ sub setFields()
|
||||||
m.top.watched = json.UserData.played
|
m.top.watched = json.UserData.played
|
||||||
m.top.Type = "Movie"
|
m.top.Type = "Movie"
|
||||||
|
|
||||||
|
if json.MediaSourceCount <> invalid and json.MediaSourceCount > 1
|
||||||
|
m.top.mediaSources = []
|
||||||
|
for each source in json.MediaSources
|
||||||
|
m.top.mediaSources.push(source)
|
||||||
|
end for
|
||||||
|
end if
|
||||||
|
|
||||||
if json.ProductionYear <> invalid
|
if json.ProductionYear <> invalid
|
||||||
m.top.SubTitle = json.ProductionYear
|
m.top.SubTitle = json.ProductionYear
|
||||||
end if
|
end if
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
<field id="movieID" type="string" />
|
<field id="movieID" type="string" />
|
||||||
<field id="seasons" type="assocarray" />
|
<field id="seasons" type="assocarray" />
|
||||||
<field id="container" type="string" />
|
<field id="container" type="string" />
|
||||||
|
<field id="mediaSourceCount" type="integer" />
|
||||||
|
<field id="mediaSources" type="array" />
|
||||||
</interface>
|
</interface>
|
||||||
<script type="text/brightscript" uri="MovieData.brs" />
|
<script type="text/brightscript" uri="MovieData.brs" />
|
||||||
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
|
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
|
||||||
|
|
|
@ -41,7 +41,7 @@ sub optionsSet()
|
||||||
if view.Selected <> invalid and view.Selected = true
|
if view.Selected <> invalid and view.Selected = true
|
||||||
selectedViewIndex = index
|
selectedViewIndex = index
|
||||||
entry.selected = true
|
entry.selected = true
|
||||||
m.top.audioSteamIndex = view.streamIndex
|
m.top.audioStreamIndex = view.streamIndex
|
||||||
end if
|
end if
|
||||||
index = index + 1
|
index = index + 1
|
||||||
end for
|
end for
|
||||||
|
@ -90,7 +90,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||||
newSelection = selMenu.content.GetChild(selIndex)
|
newSelection = selMenu.content.GetChild(selIndex)
|
||||||
newSelection.selected = true
|
newSelection.selected = true
|
||||||
m.selectedAudioIndex = selIndex
|
m.selectedAudioIndex = selIndex
|
||||||
m.top.audioSteamIndex = newSelection.streamIndex
|
m.top.audioStreamIndex = newSelection.streamIndex
|
||||||
end if
|
end if
|
||||||
end if
|
end if
|
||||||
return true
|
return true
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<component name="MovieOptions" extends="Group">
|
<component name="MovieAudioOptions" extends="Group">
|
||||||
<children>
|
<children>
|
||||||
<Rectangle width="1920" height="1080" color="#000000" opacity="0.75" />
|
<Rectangle width="1920" height="1080" color="#000000" opacity="0.75" />
|
||||||
<Group translation="[100,100]">
|
<Group translation="[100,100]">
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<interface>
|
<interface>
|
||||||
<field id="buttons" type="nodearray" />
|
<field id="buttons" type="nodearray" />
|
||||||
<field id="options" type="assocarray" onChange="optionsSet" />
|
<field id="options" type="assocarray" onChange="optionsSet" />
|
||||||
<field id="audioSteamIndex" type="integer" />
|
<field id="audioStreamIndex" type="integer" />
|
||||||
</interface>
|
</interface>
|
||||||
<script type="text/brightscript" uri="MovieOptions.brs" />
|
<script type="text/brightscript" uri="MovieAudioOptions.brs" />
|
||||||
</component>
|
</component>
|
|
@ -2,14 +2,21 @@ sub init()
|
||||||
m.extrasGrp = m.top.findnode("extrasGrp")
|
m.extrasGrp = m.top.findnode("extrasGrp")
|
||||||
m.extrasGrid = m.top.findNode("extrasGrid")
|
m.extrasGrid = m.top.findNode("extrasGrid")
|
||||||
m.top.optionsAvailable = false
|
m.top.optionsAvailable = false
|
||||||
|
|
||||||
|
m.audioOptions = m.top.findNode("audioOptions")
|
||||||
|
m.videoOptions = m.top.findNode("videoOptions")
|
||||||
|
|
||||||
m.main_group = m.top.findNode("main_group")
|
m.main_group = m.top.findNode("main_group")
|
||||||
m.options = m.top.findNode("options")
|
|
||||||
main = m.top.findNode("main_group")
|
main = m.top.findNode("main_group")
|
||||||
main.translation = [96, 175]
|
main.translation = [96, 175]
|
||||||
overview = m.top.findNode("overview")
|
overview = m.top.findNode("overview")
|
||||||
overview.width = 1920 - 96 - 300 - 96 - 30
|
overview.width = 1920 - 96 - 300 - 96 - 30
|
||||||
|
|
||||||
m.buttonGrp = m.top.findNode("buttons")
|
m.buttonGrp = m.top.findNode("buttons")
|
||||||
m.buttonGrp.setFocus(true)
|
m.buttonGrp.setFocus(true)
|
||||||
|
|
||||||
|
m.top.observeField("itemContent", "itemContentChanged")
|
||||||
end sub
|
end sub
|
||||||
|
|
||||||
sub itemContentChanged()
|
sub itemContentChanged()
|
||||||
|
@ -19,13 +26,11 @@ sub itemContentChanged()
|
||||||
m.top.id = itemData.id
|
m.top.id = itemData.id
|
||||||
m.top.findNode("moviePoster").uri = m.top.itemContent.posterURL
|
m.top.findNode("moviePoster").uri = m.top.itemContent.posterURL
|
||||||
|
|
||||||
|
' Set default video source
|
||||||
|
m.top.selectedVideoStreamId = itemData.MediaSources[0].id
|
||||||
|
|
||||||
' Find first Audio Stream and set that as default
|
' Find first Audio Stream and set that as default
|
||||||
for i = 0 to itemData.mediaStreams.Count() - 1
|
SetDefaultAudioTrack(itemData)
|
||||||
if itemData.mediaStreams[i].Type = "Audio"
|
|
||||||
m.top.selectedAudioStreamIndex = i
|
|
||||||
exit for
|
|
||||||
end if
|
|
||||||
end for
|
|
||||||
|
|
||||||
' Handle all "As Is" fields
|
' Handle all "As Is" fields
|
||||||
m.top.overhangTitle = itemData.name
|
m.top.overhangTitle = itemData.name
|
||||||
|
@ -79,9 +84,6 @@ sub itemContentChanged()
|
||||||
if itemData.mediaStreams[0] <> invalid
|
if itemData.mediaStreams[0] <> invalid
|
||||||
setFieldText("video_codec", tr("Video") + ": " + itemData.mediaStreams[0].displayTitle)
|
setFieldText("video_codec", tr("Video") + ": " + itemData.mediaStreams[0].displayTitle)
|
||||||
end if
|
end if
|
||||||
if itemData.mediaStreams[m.top.selectedAudioStreamIndex] <> invalid
|
|
||||||
setFieldText("audio_codec", tr("Audio") + ": " + itemData.mediaStreams[m.top.selectedAudioStreamIndex].displayTitle)
|
|
||||||
end if
|
|
||||||
' TODO - cmon now. these are buttons, not words
|
' TODO - cmon now. these are buttons, not words
|
||||||
if itemData.taglines.count() > 0
|
if itemData.taglines.count() > 0
|
||||||
setFieldText("tagline", itemData.taglines[0])
|
setFieldText("tagline", itemData.taglines[0])
|
||||||
|
@ -89,11 +91,29 @@ sub itemContentChanged()
|
||||||
|
|
||||||
setFavoriteColor()
|
setFavoriteColor()
|
||||||
setWatchedColor()
|
setWatchedColor()
|
||||||
SetUpOptions(itemData.mediaStreams)
|
SetUpVideoOptions(itemData.mediaSources)
|
||||||
|
SetUpAudioOptions(itemData.mediaStreams)
|
||||||
end sub
|
end sub
|
||||||
|
|
||||||
|
|
||||||
sub SetUpOptions(streams)
|
sub SetUpVideoOptions(streams)
|
||||||
|
|
||||||
|
videos = []
|
||||||
|
|
||||||
|
for i = 0 to streams.Count() - 1
|
||||||
|
if streams[i].VideoType = "VideoFile"
|
||||||
|
videos.push({ "Title": streams[i].Name, "Description": tr("Video"), "Selected": m.top.selectedVideoStreamId = streams[i].id, "StreamID": streams[i].id, "video_codec": streams[i].mediaStreams[0].displayTitle })
|
||||||
|
end if
|
||||||
|
end for
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
options.views = videos
|
||||||
|
m.videoOptions.options = options
|
||||||
|
|
||||||
|
end sub
|
||||||
|
|
||||||
|
|
||||||
|
sub SetUpAudioOptions(streams)
|
||||||
|
|
||||||
tracks = []
|
tracks = []
|
||||||
|
|
||||||
|
@ -105,11 +125,21 @@ sub SetUpOptions(streams)
|
||||||
|
|
||||||
options = {}
|
options = {}
|
||||||
options.views = tracks
|
options.views = tracks
|
||||||
m.options.options = options
|
m.audioOptions.options = options
|
||||||
|
|
||||||
end sub
|
end sub
|
||||||
|
|
||||||
|
|
||||||
|
sub SetDefaultAudioTrack(itemData)
|
||||||
|
for i = 0 to itemData.mediaStreams.Count() - 1
|
||||||
|
if itemData.mediaStreams[i].Type = "Audio"
|
||||||
|
m.top.selectedAudioStreamIndex = i
|
||||||
|
setFieldText("audio_codec", tr("Audio") + ": " + itemData.mediaStreams[i].displayTitle)
|
||||||
|
exit for
|
||||||
|
end if
|
||||||
|
end for
|
||||||
|
end sub
|
||||||
|
|
||||||
sub setFieldText(field, value)
|
sub setFieldText(field, value)
|
||||||
node = m.top.findNode(field)
|
node = m.top.findNode(field)
|
||||||
if node = invalid or value = invalid then return
|
if node = invalid or value = invalid then return
|
||||||
|
@ -186,22 +216,50 @@ end function
|
||||||
|
|
||||||
'
|
'
|
||||||
'Check if options updated and any reloading required
|
'Check if options updated and any reloading required
|
||||||
sub optionsClosed()
|
sub audioOptionsClosed()
|
||||||
if m.options.audioSteamIndex <> m.top.selectedAudioStreamIndex
|
if m.audioOptions.audioStreamIndex <> m.top.selectedAudioStreamIndex
|
||||||
m.top.selectedAudioStreamIndex = m.options.audioSteamIndex
|
m.top.selectedAudioStreamIndex = m.audioOptions.audioStreamIndex
|
||||||
setFieldText("audio_codec", tr("Audio") + ": " + m.top.itemContent.json.mediaStreams[m.top.selectedAudioStreamIndex].displayTitle)
|
setFieldText("audio_codec", tr("Audio") + ": " + m.top.itemContent.json.mediaStreams[m.top.selectedAudioStreamIndex].displayTitle)
|
||||||
end if
|
end if
|
||||||
m.top.findNode("buttons").setFocus(true)
|
m.top.findNode("buttons").setFocus(true)
|
||||||
end sub
|
end sub
|
||||||
|
|
||||||
|
'
|
||||||
|
' Check if options were updated and if any reloding is needed...
|
||||||
|
sub videoOptionsClosed()
|
||||||
|
if m.videoOptions.videoStreamId <> m.top.selectedVideoStreamId
|
||||||
|
m.top.selectedVideoStreamId = m.videoOptions.videoStreamId
|
||||||
|
setFieldText("video_codec", tr("Video") + ": " + m.videoOptions.video_codec)
|
||||||
|
' Because the video stream has changed (i.e. the actual video)... we need to reload the audio stream choices for that video
|
||||||
|
m.top.unobservefield("itemContent")
|
||||||
|
itemData = m.top.itemContent.json
|
||||||
|
for each mediaSource in itemData.mediaSources
|
||||||
|
if mediaSource.id = m.top.selectedVideoStreamId
|
||||||
|
itemData.mediaStreams = []
|
||||||
|
for i = 0 to mediaSource.mediaStreams.Count() - 1
|
||||||
|
itemData.mediaStreams.push(mediaSource.mediaStreams[i])
|
||||||
|
end for
|
||||||
|
SetDefaultAudioTrack(itemData)
|
||||||
|
SetUpAudioOptions(itemData.mediaStreams)
|
||||||
|
exit for
|
||||||
|
end if
|
||||||
|
end for
|
||||||
|
m.top.itemContent.json = itemData
|
||||||
|
m.top.observeField("itemContent", "itemContentChanged")
|
||||||
|
end if
|
||||||
|
m.top.findNode("buttons").setFocus(true)
|
||||||
|
end sub
|
||||||
|
|
||||||
function onKeyEvent(key as string, press as boolean) as boolean
|
function onKeyEvent(key as string, press as boolean) as boolean
|
||||||
|
|
||||||
' Due to the way the button pressed event works, need to catch the release for the button as the press is being sent
|
' Due to the way the button pressed event works, need to catch the release for the button as the press is being sent
|
||||||
' directly to the main loop. Will get this sorted in the layout update for Movie Details
|
' directly to the main loop. Will get this sorted in the layout update for Movie Details
|
||||||
if key = "OK" and m.top.findNode("audio-button").isInFocusChain()
|
if key = "OK" and m.top.findNode("video-button").isInFocusChain()
|
||||||
m.options.visible = true
|
m.videoOptions.visible = true
|
||||||
m.options.setFocus(true)
|
m.videoOptions.setFocus(true)
|
||||||
|
else if key = "OK" and m.top.findNode("audio-button").isInFocusChain()
|
||||||
|
m.audioOptions.visible = true
|
||||||
|
m.audioOptions.setFocus(true)
|
||||||
end if
|
end if
|
||||||
|
|
||||||
if key = "down" and m.buttonGrp.isInFocusChain()
|
if key = "down" and m.buttonGrp.isInFocusChain()
|
||||||
|
@ -224,19 +282,14 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||||
|
|
||||||
if not press then return false
|
if not press then return false
|
||||||
|
|
||||||
if key = "options"
|
if key = "back"
|
||||||
if m.options.visible = true
|
if m.audioOptions.visible = true
|
||||||
m.options.visible = false
|
m.audioOptions.visible = false
|
||||||
optionsClosed()
|
audioOptionsClosed()
|
||||||
else
|
return true
|
||||||
m.options.visible = true
|
else if m.videoOptions.visible = true
|
||||||
m.options.setFocus(true)
|
m.videoOptions.visible = false
|
||||||
end if
|
videoOptionsClosed()
|
||||||
return true
|
|
||||||
else if key = "back"
|
|
||||||
if m.options.visible = true
|
|
||||||
m.options.visible = false
|
|
||||||
optionsClosed()
|
|
||||||
return true
|
return true
|
||||||
end if
|
end if
|
||||||
end if
|
end if
|
||||||
|
|
|
@ -25,23 +25,25 @@
|
||||||
<Label id="video_codec" />
|
<Label id="video_codec" />
|
||||||
<Label id="audio_codec" />
|
<Label id="audio_codec" />
|
||||||
<ButtonGroupHoriz id="buttons" itemSpacings="[10]">
|
<ButtonGroupHoriz id="buttons" itemSpacings="[10]">
|
||||||
<Button text="Play" id="play-button" iconUri="" focusedIconUri="" maxWidth="320" minWidth="320" />
|
<Button text="Play" id="play-button" iconUri="" focusedIconUri="" maxWidth="300" minWidth="300" />
|
||||||
<Button text="Audio" id="audio-button" iconUri="" focusedIconUri="" maxWidth="320" minWidth="320" />
|
<Button text="Version" id="video-button" iconUri="" focusedIconUri="" maxWidth="300" minWidth="300" />
|
||||||
<Button text="Watched" id="watched-button" iconUri="" focusedIconUri="" maxWidth="320" minWidth="320" />
|
<Button text="Audio" id="audio-button" iconUri="" focusedIconUri="" maxWidth="300" minWidth="300" />
|
||||||
<Button text="Favorite" id="favorite-button" iconUri="" focusedIconUri="" maxWidth="320" minWidth="320" />
|
<Button text="Watched" id="watched-button" iconUri="" focusedIconUri="" maxWidth="300" minWidth="300" />
|
||||||
|
<Button text="Favorite" id="favorite-button" iconUri="" focusedIconUri="" maxWidth="300" minWidth="300" />
|
||||||
</ButtonGroupHoriz>
|
</ButtonGroupHoriz>
|
||||||
<Label id="tagline" />
|
<Label id="tagline" />
|
||||||
<Label id="overview" wrap="true" maxLines="8" />
|
<Label id="overview" wrap="true" maxLines="8" />
|
||||||
</LayoutGroup>
|
</LayoutGroup>
|
||||||
</LayoutGroup>
|
</LayoutGroup>
|
||||||
|
<MovieVideoOptions id="videoOptions" visible="false" />
|
||||||
|
<MovieAudioOptions id="audioOptions" visible="false" />
|
||||||
<!-- "Cast and Crew" row -->
|
<!-- "Cast and Crew" row -->
|
||||||
<extrasSlider id="movieExtras" />
|
<extrasSlider id="movieExtras" />
|
||||||
<MovieOptions id="options" visible="false" />
|
|
||||||
</children>
|
</children>
|
||||||
<interface>
|
<interface>
|
||||||
<field id="itemContent" type="node" onChange="itemContentChanged" />
|
<field id="itemContent" type="node" />
|
||||||
<field id="selectedAudioStreamIndex" type="integer" />
|
<field id="selectedAudioStreamIndex" type="integer" />
|
||||||
|
<field id="selectedVideoStreamId" type="string" />
|
||||||
</interface>
|
</interface>
|
||||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||||
<script type="text/brightscript" uri="MovieDetails.brs" />
|
<script type="text/brightscript" uri="MovieDetails.brs" />
|
||||||
|
|
112
components/movies/MovieVideoOptions.brs
Normal file
112
components/movies/MovieVideoOptions.brs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
sub init()
|
||||||
|
|
||||||
|
m.buttons = m.top.findNode("buttons")
|
||||||
|
m.buttons.buttons = [tr("Version")]
|
||||||
|
m.buttons.setFocus(true)
|
||||||
|
|
||||||
|
m.selectedItem = 0
|
||||||
|
m.selectedVideoIndex = 0
|
||||||
|
|
||||||
|
m.menus = []
|
||||||
|
m.menus.push(m.top.findNode("videoMenu"))
|
||||||
|
|
||||||
|
m.viewNames = []
|
||||||
|
|
||||||
|
' Set button color to global
|
||||||
|
m.top.findNode("videoMenu").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")
|
||||||
|
|
||||||
|
end sub
|
||||||
|
|
||||||
|
sub optionsSet()
|
||||||
|
|
||||||
|
' Views Tab
|
||||||
|
if m.top.options.views <> invalid
|
||||||
|
viewContent = CreateObject("roSGNode", "ContentNode")
|
||||||
|
index = 0
|
||||||
|
selectedViewIndex = 0
|
||||||
|
|
||||||
|
for each view in m.top.options.views
|
||||||
|
entry = viewContent.CreateChild("VideoTrackListData")
|
||||||
|
entry.title = view.Title
|
||||||
|
entry.description = view.Description
|
||||||
|
entry.streamId = view.streamId
|
||||||
|
entry.video_codec = view.video_codec
|
||||||
|
m.viewNames.push(view.Name)
|
||||||
|
if view.Selected <> invalid and view.Selected = true
|
||||||
|
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.selectedVideoIndex = selectedViewIndex
|
||||||
|
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
|
||||||
|
|
||||||
|
if m.selectedVideoIndex = selIndex
|
||||||
|
else
|
||||||
|
selMenu.content.GetChild(m.selectedVideoIndex).selected = false
|
||||||
|
newSelection = selMenu.content.GetChild(selIndex)
|
||||||
|
newSelection.selected = true
|
||||||
|
m.selectedVideoIndex = selIndex
|
||||||
|
m.top.videoStreamId = newSelection.streamId
|
||||||
|
m.top.video_codec = newSelection.video_codec
|
||||||
|
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
|
36
components/movies/MovieVideoOptions.xml
Normal file
36
components/movies/MovieVideoOptions.xml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<component name="MovieVideoOptions" 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>
|
||||||
|
<MarkupList
|
||||||
|
id="videoMenu"
|
||||||
|
itemComponentName="VideoTrackListItem"
|
||||||
|
itemSize="[600, 70]"
|
||||||
|
itemSpacing="[20,20]"
|
||||||
|
numrows = "8"
|
||||||
|
vertFocusAnimationStyle="floatingFocus"
|
||||||
|
focusFootprintBlendColor="#00000000"
|
||||||
|
/>
|
||||||
|
</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="videoStreamId" type="string" />
|
||||||
|
<field id="video_codec" type="string" />
|
||||||
|
</interface>
|
||||||
|
<script type="text/brightscript" uri="MovieVideoOptions.brs" />
|
||||||
|
</component>
|
8
components/movies/VideoTrackListData.xml
Normal file
8
components/movies/VideoTrackListData.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<component name="VideoTrackListData" extends="ContentNode">
|
||||||
|
<interface>
|
||||||
|
<field id="selected" type="boolean" value="false" />
|
||||||
|
<field id="streamId" type="string" />
|
||||||
|
<field id="video_codec" type="string" />
|
||||||
|
</interface>
|
||||||
|
</component>
|
33
components/movies/VideoTrackListItem.brs
Normal file
33
components/movies/VideoTrackListItem.brs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
sub init()
|
||||||
|
m.title = m.top.findNode("title")
|
||||||
|
m.description = m.top.findNode("description")
|
||||||
|
m.selectedIcon = m.top.findNode("selectedIcon")
|
||||||
|
end sub
|
||||||
|
|
||||||
|
sub itemContentChanged()
|
||||||
|
m.title.text = m.top.itemContent.title
|
||||||
|
m.description.text = m.top.itemContent.description
|
||||||
|
|
||||||
|
if m.top.itemContent.description = ""
|
||||||
|
m.title.translation = [50, 20]
|
||||||
|
end if
|
||||||
|
|
||||||
|
if m.top.itemContent.selected
|
||||||
|
m.selectedIcon.uri = m.global.constants.icons.check_white
|
||||||
|
else
|
||||||
|
m.selectedIcon.uri = ""
|
||||||
|
end if
|
||||||
|
|
||||||
|
end sub
|
||||||
|
|
||||||
|
'
|
||||||
|
'Scroll description if focused
|
||||||
|
sub focusChanged()
|
||||||
|
|
||||||
|
if m.top.itemHasFocus = true
|
||||||
|
m.description.repeatCount = -1
|
||||||
|
else
|
||||||
|
m.description.repeatCount = 0
|
||||||
|
end if
|
||||||
|
|
||||||
|
end sub
|
13
components/movies/VideoTrackListItem.xml
Normal file
13
components/movies/VideoTrackListItem.xml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<component name="VideoTrackListItem" extends="Group">
|
||||||
|
<children>
|
||||||
|
<Poster width="32" height="32" id="selectedIcon" translation="[5,19]" uri="" />
|
||||||
|
<Label id="title" font="font:MediumSystemFont" color="0xffffffff" translation="[50,3]" />
|
||||||
|
<ScrollingLabel id="description" font="font:SmallestSystemFont" color="0xabababff" maxWidth="800" repeatCount="0" translation="[50,45]" />
|
||||||
|
</children>
|
||||||
|
<interface>
|
||||||
|
<field id="itemContent" type="node" onChange="itemContentChanged"/>
|
||||||
|
<field id="itemHasFocus" type="boolean" onChange="focusChanged" />
|
||||||
|
</interface>
|
||||||
|
<script type="text/brightscript" uri="VideoTrackListItem.brs" />
|
||||||
|
</component>
|
|
@ -461,5 +461,9 @@
|
||||||
<translation>An error was encountered while playing this item. Server did not provide required transcoding data.</translation>
|
<translation>An error was encountered while playing this item. Server did not provide required transcoding data.</translation>
|
||||||
<extracomment>Content of message box when trying to play an item which requires transcoding, and the server did not provide transcode url</extracomment>
|
<extracomment>Content of message box when trying to play an item which requires transcoding, and the server did not provide transcode url</extracomment>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Version</source>
|
||||||
|
<translation>Version</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|
|
@ -195,14 +195,20 @@ sub Main (args as dynamic) as void
|
||||||
btn = getButton(msg)
|
btn = getButton(msg)
|
||||||
group = sceneManager.callFunc("getActiveScene")
|
group = sceneManager.callFunc("getActiveScene")
|
||||||
if btn <> invalid and btn.id = "play-button"
|
if btn <> invalid and btn.id = "play-button"
|
||||||
' Check is a specific Audio Stream was selected
|
' Check if a specific Audio Stream was selected
|
||||||
audio_stream_idx = 1
|
audio_stream_idx = 1
|
||||||
if group.selectedAudioStreamIndex <> invalid
|
if group.selectedAudioStreamIndex <> invalid
|
||||||
audio_stream_idx = group.selectedAudioStreamIndex
|
audio_stream_idx = group.selectedAudioStreamIndex
|
||||||
end if
|
end if
|
||||||
|
|
||||||
|
' Check to see if a specific video "version" was selected
|
||||||
|
mediaSourceId = invalid
|
||||||
|
if group.selectedVideoStreamId <> invalid
|
||||||
|
mediaSourceId = group.selectedVideoStreamId
|
||||||
|
end if
|
||||||
video_id = group.id
|
video_id = group.id
|
||||||
video = CreateVideoPlayerGroup(video_id, audio_stream_idx)
|
|
||||||
|
video = CreateVideoPlayerGroup(video_id, mediaSourceId, audio_stream_idx)
|
||||||
if video <> invalid
|
if video <> invalid
|
||||||
sceneManager.callFunc("pushScene", video)
|
sceneManager.callFunc("pushScene", video)
|
||||||
end if
|
end if
|
||||||
|
|
|
@ -373,9 +373,9 @@ sub CreateSidePanel(buttons, options)
|
||||||
group.options = options
|
group.options = options
|
||||||
end sub
|
end sub
|
||||||
|
|
||||||
function CreateVideoPlayerGroup(video_id, audio_stream_idx = 1)
|
function CreateVideoPlayerGroup(video_id, mediaSourceId = invalid, audio_stream_idx = 1)
|
||||||
' Video is Playing
|
' Video is Playing
|
||||||
video = VideoPlayer(video_id, audio_stream_idx)
|
video = VideoPlayer(video_id, mediaSourceId, audio_stream_idx)
|
||||||
if video = invalid then return invalid
|
if video = invalid then return invalid
|
||||||
video.observeField("selectSubtitlePressed", m.port)
|
video.observeField("selectSubtitlePressed", m.port)
|
||||||
video.observeField("state", m.port)
|
video.observeField("state", m.port)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
function VideoPlayer(id, audio_stream_idx = 1, subtitle_idx = -1)
|
function VideoPlayer(id, mediaSourceId = invalid, audio_stream_idx = 1, subtitle_idx = -1)
|
||||||
|
|
||||||
' Get video controls and UI
|
' Get video controls and UI
|
||||||
video = CreateObject("roSGNode", "JFVideo")
|
video = CreateObject("roSGNode", "JFVideo")
|
||||||
video.id = id
|
video.id = id
|
||||||
AddVideoContent(video, audio_stream_idx, subtitle_idx)
|
AddVideoContent(video, mediaSourceId, audio_stream_idx, subtitle_idx)
|
||||||
|
|
||||||
if video.content = invalid
|
if video.content = invalid
|
||||||
return invalid
|
return invalid
|
||||||
|
@ -16,7 +16,7 @@ function VideoPlayer(id, audio_stream_idx = 1, subtitle_idx = -1)
|
||||||
return video
|
return video
|
||||||
end function
|
end function
|
||||||
|
|
||||||
sub AddVideoContent(video, audio_stream_idx = 1, subtitle_idx = -1, playbackPosition = -1)
|
sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -1, playbackPosition = -1)
|
||||||
|
|
||||||
video.content = createObject("RoSGNode", "ContentNode")
|
video.content = createObject("RoSGNode", "ContentNode")
|
||||||
|
|
||||||
|
@ -64,12 +64,15 @@ sub AddVideoContent(video, audio_stream_idx = 1, subtitle_idx = -1, playbackPosi
|
||||||
video.content.PlayStart = int(playbackPosition / 10000000)
|
video.content.PlayStart = int(playbackPosition / 10000000)
|
||||||
|
|
||||||
' Call PlayInfo from server
|
' Call PlayInfo from server
|
||||||
mediaSourceId = video.id
|
if mediaSourceId = invalid
|
||||||
|
mediaSourceId = video.id
|
||||||
|
end if
|
||||||
if meta.live then mediaSourceId = "" ' Don't send mediaSourceId for Live media
|
if meta.live then mediaSourceId = "" ' Don't send mediaSourceId for Live media
|
||||||
|
|
||||||
playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
|
playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
|
||||||
|
|
||||||
video.videoId = video.id
|
video.videoId = video.id
|
||||||
video.mediaSourceId = video.id
|
video.mediaSourceId = mediaSourceId
|
||||||
video.audioIndex = audio_stream_idx
|
video.audioIndex = audio_stream_idx
|
||||||
|
|
||||||
if playbackInfo = invalid
|
if playbackInfo = invalid
|
||||||
|
@ -112,6 +115,9 @@ sub AddVideoContent(video, audio_stream_idx = 1, subtitle_idx = -1, playbackPosi
|
||||||
"PlaySessionId": video.PlaySessionId,
|
"PlaySessionId": video.PlaySessionId,
|
||||||
"AudioStreamIndex": audio_stream_idx
|
"AudioStreamIndex": audio_stream_idx
|
||||||
})
|
})
|
||||||
|
if mediaSourceId <> ""
|
||||||
|
params.MediaSourceId = mediaSourceId
|
||||||
|
end if
|
||||||
video.content.url = buildURL(Substitute("Videos/{0}/stream", video.id), params)
|
video.content.url = buildURL(Substitute("Videos/{0}/stream", video.id), params)
|
||||||
video.isTranscoded = false
|
video.isTranscoded = false
|
||||||
video.audioTrack = (audio_stream_idx + 1).ToStr() ' Roku's track indexes count from 1. Our index is zero based
|
video.audioTrack = (audio_stream_idx + 1).ToStr() ' Roku's track indexes count from 1. Our index is zero based
|
||||||
|
|
|
@ -49,7 +49,7 @@ sub changeSubtitleDuringPlayback(newid)
|
||||||
|
|
||||||
' Switching to Encoded Subtitle stream
|
' Switching to Encoded Subtitle stream
|
||||||
video.control = "stop"
|
video.control = "stop"
|
||||||
AddVideoContent(video, video.audioIndex, newSubtitles.Index, video.position * 10000000)
|
AddVideoContent(video, video.mediaSourceId, video.audioIndex, newSubtitles.Index, video.position * 10000000)
|
||||||
video.control = "play"
|
video.control = "play"
|
||||||
video.globalCaptionMode = "Off" ' Using encoded subtitles - so turn off text subtitles
|
video.globalCaptionMode = "Off" ' Using encoded subtitles - so turn off text subtitles
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ sub changeSubtitleDuringPlayback(newid)
|
||||||
|
|
||||||
' Switching from an Encoded stream to a text stream
|
' Switching from an Encoded stream to a text stream
|
||||||
video.control = "stop"
|
video.control = "stop"
|
||||||
AddVideoContent(video, video.audioIndex, -1, video.position * 10000000)
|
AddVideoContent(video, video.mediaSourceId, video.audioIndex, -1, video.position * 10000000)
|
||||||
video.control = "play"
|
video.control = "play"
|
||||||
video.globalCaptionMode = "On"
|
video.globalCaptionMode = "On"
|
||||||
video.subtitleTrack = video.availableSubtitleTracks[newSubtitles.TextIndex].TrackName
|
video.subtitleTrack = video.availableSubtitleTracks[newSubtitles.TextIndex].TrackName
|
||||||
|
@ -82,7 +82,7 @@ sub turnoffSubtitles()
|
||||||
' Check if Enoded subtitles are being displayed, and turn off
|
' Check if Enoded subtitles are being displayed, and turn off
|
||||||
if current > -1 and video.Subtitles[current].IsEncoded
|
if current > -1 and video.Subtitles[current].IsEncoded
|
||||||
video.control = "stop"
|
video.control = "stop"
|
||||||
AddVideoContent(video, video.audioIndex, -1, video.position * 10000000)
|
AddVideoContent(video, video.mediaSourceId, video.audioIndex, -1, video.position * 10000000)
|
||||||
video.control = "play"
|
video.control = "play"
|
||||||
end if
|
end if
|
||||||
end sub
|
end sub
|
||||||
|
|
Loading…
Reference in New Issue
Block a user