mirror of
https://github.com/jellyfin/jellyfin-roku.git
synced 2024-11-23 14:19:40 +00:00
Merge branch 'master' of https://github.com/jellyfin/jellyfin-roku into ssdp-scan
This commit is contained in:
commit
fa2452c037
5
.gitignore
vendored
5
.gitignore
vendored
@ -15,3 +15,8 @@ rooibosFunctionMap.brs
|
||||
*/buildinfo.brs
|
||||
.vscode
|
||||
logs
|
||||
|
||||
#Eclipse
|
||||
.buildpath
|
||||
.project
|
||||
.settings
|
2
Makefile
2
Makefile
@ -12,7 +12,7 @@
|
||||
##########################################################################
|
||||
|
||||
APPNAME = Jellyfin_Roku
|
||||
VERSION = 0.0.1
|
||||
VERSION = 1.4.9
|
||||
ROKU_TEST_ID = 1
|
||||
ROKU_TEST_WAIT_DURATION = 5
|
||||
|
||||
|
@ -6,5 +6,6 @@
|
||||
"images/**/*.*",
|
||||
"resources/**/*.*",
|
||||
"locale/**/*.*"
|
||||
]
|
||||
],
|
||||
"plugins": [ "@rokucommunity/bslint" ]
|
||||
}
|
@ -34,7 +34,7 @@ end sub
|
||||
'
|
||||
' When options are fully displayed, set focus and selected option
|
||||
sub renderChanged()
|
||||
if m.top.renderTracking = "full" then
|
||||
if m.top.renderTracking = "full"
|
||||
highlightSelected(m.selectedFocusedIndex, false)
|
||||
m.top.setfocus(true)
|
||||
end if
|
||||
@ -77,7 +77,7 @@ sub highlightSelected(index as integer, animate = true)
|
||||
val = m.buttonGroup.getChild(index)
|
||||
rect = val.ancestorBoundingRect(m.top)
|
||||
|
||||
if animate = true then
|
||||
if animate = true
|
||||
m.focusAnimTranslation.keyValue = [m.focusRing.translation, [rect.x - 25, rect.y - 30]]
|
||||
m.focusAnimWidth.keyValue = [m.focusRing.width, val.width + 50]
|
||||
m.focusAnimHeight.keyValue = [m.focusRing.height, val.height + 60]
|
||||
@ -92,7 +92,7 @@ end sub
|
||||
|
||||
' Change opacity of the highlighted menu item based on focus
|
||||
sub focusChanged()
|
||||
if m.top.isInFocusChain() then
|
||||
if m.top.isInFocusChain()
|
||||
m.focusRing.opacity = 1
|
||||
else
|
||||
m.focusRing.opacity = 0.6
|
||||
@ -105,12 +105,12 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "left"
|
||||
if(m.selectedFocusedIndex > 0) m.selectedFocusedIndex = m.selectedFocusedIndex - 1
|
||||
if m.selectedFocusedIndex > 0 then m.selectedFocusedIndex = m.selectedFocusedIndex - 1
|
||||
highlightSelected(m.selectedFocusedIndex)
|
||||
m.top.focusedIndex = m.selectedFocusedIndex
|
||||
return true
|
||||
else if key = "right"
|
||||
if(m.selectedFocusedIndex < m.buttonCount - 1) m.selectedFocusedIndex = m.selectedFocusedIndex + 1
|
||||
if m.selectedFocusedIndex < m.buttonCount - 1 then m.selectedFocusedIndex = m.selectedFocusedIndex + 1
|
||||
highlightSelected(m.selectedFocusedIndex)
|
||||
m.top.focusedIndex = m.selectedFocusedIndex
|
||||
return true
|
||||
|
@ -1,33 +0,0 @@
|
||||
Sub Init()
|
||||
m.top.functionName = "listenInput"
|
||||
End Sub
|
||||
|
||||
function ListenInput()
|
||||
port=createobject("romessageport")
|
||||
InputObject=createobject("roInput")
|
||||
InputObject.setmessageport(port)
|
||||
|
||||
while true
|
||||
msg=port.waitmessage(500)
|
||||
if type(msg)="roInputEvent" then
|
||||
print "INPUT EVENT!"
|
||||
if msg.isInput()
|
||||
inputData = msg.getInfo()
|
||||
'print inputData'
|
||||
for each item in inputData
|
||||
print item +": " inputData[item]
|
||||
end for
|
||||
|
||||
' pass the deeplink to UI
|
||||
if inputData.DoesExist("mediaType") and inputData.DoesExist("contentID")
|
||||
deeplink = {
|
||||
id: inputData.contentID
|
||||
type: inputData.mediaType
|
||||
}
|
||||
print "got input deeplink= "; deeplink
|
||||
m.top.inputData = deeplink
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
end while
|
||||
end function
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<component name="InputTask" extends="Task">
|
||||
<interface>
|
||||
<field id="inputData" type="assocarray" />
|
||||
</interface>
|
||||
|
||||
<script type="text/brightscript" uri="InputTask.brs" />
|
||||
</component>
|
@ -29,22 +29,23 @@ sub itemContentChanged()
|
||||
|
||||
if itemData = invalid then return
|
||||
|
||||
if itemData.type = "Movie" then
|
||||
if itemData.type = "Movie"
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else if itemData.type = "Series" then
|
||||
else if itemData.type = "Series"
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else if itemData.type = "Boxset" then
|
||||
else if itemData.type = "Boxset"
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else if itemData.type = "TvChannel" then
|
||||
else if itemData.type = "TvChannel"
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else if itemData.type = "Folder" then
|
||||
else if itemData.type = "Folder"
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemIcon.uri = itemData.iconUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else if itemData.type = "Video" then
|
||||
else if itemData.type = "Video"
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else
|
||||
@ -52,7 +53,7 @@ sub itemContentChanged()
|
||||
end if
|
||||
|
||||
'If Poster not loaded, ensure "blue box" is shown until loaded
|
||||
if m.itemPoster.loadStatus <> "ready" then
|
||||
if m.itemPoster.loadStatus <> "ready"
|
||||
m.backdrop.visible = true
|
||||
m.posterText.visible = true
|
||||
end if
|
||||
@ -72,7 +73,7 @@ end sub
|
||||
'Display or hide title Visibility on focus change
|
||||
sub focusChanged()
|
||||
|
||||
if m.top.itemHasFocus = true then
|
||||
if m.top.itemHasFocus = true
|
||||
m.itemText.visible = true
|
||||
m.itemText.repeatCount = -1
|
||||
else
|
||||
@ -84,7 +85,7 @@ end sub
|
||||
|
||||
'Hide backdrop and text when poster loaded
|
||||
sub onPosterLoadStatusChanged()
|
||||
if m.itemPoster.loadStatus = "ready" then
|
||||
if m.itemPoster.loadStatus = "ready"
|
||||
m.backdrop.visible = false
|
||||
m.posterText.visible = false
|
||||
end if
|
||||
|
@ -33,7 +33,6 @@ sub init()
|
||||
m.filter = "All"
|
||||
|
||||
m.loadItemsTask = createObject("roSGNode", "LoadItemsTask2")
|
||||
m.loadItemsTask.observeField("content", "ItemDataLoaded")
|
||||
|
||||
end sub
|
||||
|
||||
@ -41,7 +40,7 @@ end sub
|
||||
'Load initial set of Data
|
||||
sub loadInitialItems()
|
||||
|
||||
if m.top.parentItem.backdropUrl <> invalid then
|
||||
if m.top.parentItem.backdropUrl <> invalid
|
||||
SetBackground(m.top.parentItem.backdropUrl)
|
||||
end if
|
||||
|
||||
@ -51,29 +50,30 @@ sub loadInitialItems()
|
||||
m.loadItemsTask.filter = m.filter
|
||||
m.loadItemsTask.startIndex = 0
|
||||
|
||||
if m.top.parentItem.collectionType = "movies" then
|
||||
if m.top.parentItem.collectionType = "movies"
|
||||
m.loadItemsTask.itemType = "Movie"
|
||||
else if m.top.parentItem.collectionType = "tvshows" then
|
||||
else if m.top.parentItem.collectionType = "tvshows"
|
||||
m.loadItemsTask.itemType = "Series"
|
||||
else if m.top.parentItem.collectionType = "livetv" then
|
||||
else if m.top.parentItem.collectionType = "livetv"
|
||||
m.loadItemsTask.itemType = "LiveTV"
|
||||
|
||||
'For LiveTV, we want to "Fit" the item images, not zoom
|
||||
m.top.imageDisplayMode = "scaleToFit"
|
||||
|
||||
if get_user_setting("display.livetv.landing") = "guide" then
|
||||
if get_user_setting("display.livetv.landing") = "guide"
|
||||
showTvGuid()
|
||||
end if
|
||||
|
||||
else if m.top.parentItem.collectionType = "CollectionFolder" OR m.top.parentItem.collectionType = "boxsets" then
|
||||
else if m.top.parentItem.collectionType = "CollectionFolder" OR m.top.parentItem.collectionType = "boxsets" or m.top.parentItem.Type = "Folder" or m.top.parentItem.Type = "Channel"
|
||||
' Non-recursive, to not show subfolder contents
|
||||
m.loadItemsTask.recursive = false
|
||||
else if m.top.parentItem.collectionType = "Channel" then
|
||||
else if m.top.parentItem.collectionType = "Channel"
|
||||
m.top.imageDisplayMode = "scaleToFit"
|
||||
else
|
||||
print "Unknown Type: " m.top.parentItem
|
||||
print "[ItemGrid] Unknown Type: " m.top.parentItem
|
||||
end if
|
||||
|
||||
m.loadItemsTask.observeField("content", "ItemDataLoaded")
|
||||
m.loadItemsTask.control = "RUN"
|
||||
|
||||
SetUpOptions()
|
||||
@ -87,7 +87,7 @@ sub SetUpOptions()
|
||||
options.filter = []
|
||||
|
||||
'Movies
|
||||
if m.top.parentItem.collectionType = "movies" then
|
||||
if m.top.parentItem.collectionType = "movies"
|
||||
options.views = [
|
||||
{ "Title": tr("Movies"), "Name": "movies" },
|
||||
]
|
||||
@ -106,8 +106,21 @@ sub SetUpOptions()
|
||||
{ "Title": tr("All"), "Name": "All" },
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" }
|
||||
]
|
||||
'Boxsets
|
||||
else if m.top.parentItem.collectionType = "boxsets"
|
||||
options.views = [{ "Title": tr("Shows"), "Name": "shows" }]
|
||||
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" }
|
||||
]
|
||||
'TV Shows
|
||||
else if m.top.parentItem.collectionType = "tvshows" then
|
||||
else if m.top.parentItem.collectionType = "tvshows"
|
||||
options.views = [{ "Title": tr("Shows"), "Name": "shows" }]
|
||||
options.sort = [
|
||||
{ "Title": tr("TITLE"), "Name": "SortName" },
|
||||
@ -119,7 +132,7 @@ sub SetUpOptions()
|
||||
]
|
||||
options.filter = []
|
||||
'Live TV
|
||||
else if m.top.parentItem.collectionType = "livetv" then
|
||||
else if m.top.parentItem.collectionType = "livetv"
|
||||
options.views = [
|
||||
{"Title": tr("Channels"), "Name": "livetv" },
|
||||
{"Title": tr("TV Guide"), "Name": "tvGuide", "Selected": get_user_setting("display.livetv.landing") = "guide" }
|
||||
@ -139,14 +152,14 @@ sub SetUpOptions()
|
||||
end if
|
||||
|
||||
for each o in options.sort
|
||||
if o.Name = m.sortField then
|
||||
if o.Name = m.sortField
|
||||
o.Selected = true
|
||||
o.Ascending = m.sortAscending
|
||||
end if
|
||||
end for
|
||||
|
||||
for each o in options.filter
|
||||
if o.Name = m.filter then
|
||||
if o.Name = m.filter
|
||||
o.Selected = true
|
||||
end if
|
||||
end for
|
||||
@ -161,11 +174,10 @@ end sub
|
||||
sub ItemDataLoaded(msg)
|
||||
|
||||
itemData = msg.GetData()
|
||||
data = msg.getField()
|
||||
m.loadItemsTask.unobserveField("content")
|
||||
m.loadItemsTask.content = []
|
||||
|
||||
if itemData = invalid then
|
||||
if itemData = invalid
|
||||
m.Loading = false
|
||||
return
|
||||
end if
|
||||
@ -180,7 +192,7 @@ sub ItemDataLoaded(msg)
|
||||
m.Loading = false
|
||||
|
||||
'If there are no items to display, show message
|
||||
if m.loadedItems = 0 then
|
||||
if m.loadedItems = 0
|
||||
m.emptyText.text = tr("NO_ITEMS").Replace("%1", m.top.parentItem.Type)
|
||||
m.emptyText.visible = true
|
||||
end if
|
||||
@ -194,7 +206,7 @@ end sub
|
||||
sub SetBackground(backgroundUri as string)
|
||||
|
||||
'If a new image is being loaded, or transitioned to, store URL to load next
|
||||
if m.swapAnimation.state <> "stopped" or m.newBackdrop.loadStatus = "loading" then
|
||||
if m.swapAnimation.state <> "stopped" or m.newBackdrop.loadStatus = "loading"
|
||||
m.queuedBGUri = backgroundUri
|
||||
return
|
||||
end if
|
||||
@ -211,7 +223,7 @@ sub onItemFocused()
|
||||
itemInt = m.itemGrid.itemFocused
|
||||
|
||||
' If no selected item, set background to parent backdrop
|
||||
if itemInt = -1 then
|
||||
if itemInt = -1
|
||||
return
|
||||
end if
|
||||
|
||||
@ -219,7 +231,7 @@ sub onItemFocused()
|
||||
SetBackground(m.itemGrid.content.getChild(m.itemGrid.itemFocused).backdropUrl)
|
||||
|
||||
' Load more data if focus is within last 3 rows, and there are more items to load
|
||||
if focusedRow >= m.loadedRows - 3 and m.loadeditems < m.loadItemsTask.totalRecordCount then
|
||||
if focusedRow >= m.loadedRows - 3 and m.loadeditems < m.loadItemsTask.totalRecordCount
|
||||
loadMoreData()
|
||||
end if
|
||||
end sub
|
||||
@ -237,7 +249,7 @@ end sub
|
||||
'Swap Complete
|
||||
sub swapDone()
|
||||
|
||||
if m.swapAnimation.state = "stopped" then
|
||||
if m.swapAnimation.state = "stopped"
|
||||
|
||||
'Set main BG node image and hide transitioning node
|
||||
m.backdrop.uri = m.newBackdrop.uri
|
||||
@ -245,7 +257,7 @@ sub swapDone()
|
||||
m.newBackdrop.opacity = 0
|
||||
|
||||
'If there is another one to load
|
||||
if m.newBackdrop.uri <> m.queuedBGUri and m.queuedBGUri <> "" then
|
||||
if m.newBackdrop.uri <> m.queuedBGUri and m.queuedBGUri <> ""
|
||||
SetBackground(m.queuedBGUri)
|
||||
m.queuedBGUri = ""
|
||||
end if
|
||||
@ -275,21 +287,21 @@ end sub
|
||||
'Check if options updated and any reloading required
|
||||
sub optionsClosed()
|
||||
|
||||
if (m.options.view = "tvGuide") then
|
||||
if m.options.view = "tvGuide"
|
||||
showTVGuid()
|
||||
return
|
||||
else if m.tvGuide <> invalid then
|
||||
else if m.tvGuide <> invalid
|
||||
' Try to hide the TV Guide
|
||||
m.top.removeChild(m.tvGuide)
|
||||
end if
|
||||
|
||||
reload = false
|
||||
if m.options.sortField <> m.sortField or m.options.sortAscending <> m.sortAscending then
|
||||
if m.options.sortField <> m.sortField or m.options.sortAscending <> m.sortAscending
|
||||
m.sortField = m.options.sortField
|
||||
m.sortAscending = m.options.sortAscending
|
||||
reload = true
|
||||
end if
|
||||
if m.options.filter <> m.filter then
|
||||
if m.options.filter <> m.filter
|
||||
m.filter = m.options.filter
|
||||
reload = true
|
||||
end if
|
||||
@ -305,7 +317,7 @@ end sub
|
||||
|
||||
sub showTVGuid()
|
||||
m.top.signalBeacon("EPGLaunchInitiate") ' Required Roku Performance monitoring
|
||||
if m.tvGuide = invalid then
|
||||
if m.tvGuide = invalid
|
||||
m.tvGuide = createObject("roSGNode", "Schedule")
|
||||
endif
|
||||
m.tvGuide.observeField("watchChannel", "onChannelSelected")
|
||||
@ -316,7 +328,7 @@ end sub
|
||||
sub onChannelSelected(msg)
|
||||
node = msg.getRoSGNode()
|
||||
m.top.lastFocus = lastFocusedChild(node)
|
||||
if node.watchChannel <> invalid then
|
||||
if node.watchChannel <> invalid
|
||||
' Clone the node when it's reused/update in the TimeGrid it doesn't automatically start playing
|
||||
m.top.selectedItem = node.watchChannel.clone(false)
|
||||
end if
|
||||
@ -327,7 +339,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "options"
|
||||
if m.options.visible = true then
|
||||
if m.options.visible = true
|
||||
m.options.visible = false
|
||||
m.top.removeChild(m.options)
|
||||
optionsClosed()
|
||||
@ -337,16 +349,16 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
m.options.setFocus(true)
|
||||
end if
|
||||
return true
|
||||
else if key = "back" then
|
||||
if m.options.visible = true then
|
||||
else if key = "back"
|
||||
if m.options.visible = true
|
||||
m.options.visible = false
|
||||
optionsClosed()
|
||||
return true
|
||||
end if
|
||||
else if key = "play" then
|
||||
else if key = "play"
|
||||
markupGrid = m.top.getChild(2)
|
||||
itemToPlay = markupGrid.content.getChild(markupGrid.itemFocused)
|
||||
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode") then
|
||||
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode")
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
end if
|
||||
return true
|
||||
|
@ -30,7 +30,7 @@ end sub
|
||||
sub optionsSet()
|
||||
|
||||
' Views Tab
|
||||
if m.top.options.views <> invalid then
|
||||
if m.top.options.views <> invalid
|
||||
viewContent = CreateObject("roSGNode", "ContentNode")
|
||||
index = 0
|
||||
selectedViewIndex = 0
|
||||
@ -39,7 +39,7 @@ sub optionsSet()
|
||||
entry = viewContent.CreateChild("ContentNode")
|
||||
entry.title = view.Title
|
||||
m.viewNames.push(view.Name)
|
||||
if (view.selected <> invalid and view.selected = true) or viewContent.Name = m.top.view then
|
||||
if (view.selected <> invalid and view.selected = true) or viewContent.Name = m.top.view
|
||||
selectedViewIndex = index
|
||||
end if
|
||||
index = index + 1
|
||||
@ -49,7 +49,7 @@ sub optionsSet()
|
||||
end if
|
||||
|
||||
' Sort Tab
|
||||
if m.top.options.sort <> invalid then
|
||||
if m.top.options.sort <> invalid
|
||||
sortContent = CreateObject("roSGNode", "ContentNode")
|
||||
index = 0
|
||||
m.selectedSortIndex = 0
|
||||
@ -58,9 +58,9 @@ sub optionsSet()
|
||||
entry = sortContent.CreateChild("ContentNode")
|
||||
entry.title = sortItem.Title
|
||||
m.sortNames.push(sortItem.Name)
|
||||
if sortItem.Selected <> invalid and sortItem.Selected = true then
|
||||
if sortItem.Selected <> invalid and sortItem.Selected = true
|
||||
m.selectedSortIndex = index
|
||||
if sortItem.Ascending <> invalid and sortItem.Ascending = false then
|
||||
if sortItem.Ascending <> invalid and sortItem.Ascending = false
|
||||
m.top.sortAscending = 0
|
||||
else
|
||||
m.top.sortAscending = 1
|
||||
@ -71,7 +71,7 @@ sub optionsSet()
|
||||
m.menus[1].content = sortContent
|
||||
m.menus[1].checkedItem = m.selectedSortIndex
|
||||
|
||||
if m.top.sortAscending = 1 then
|
||||
if m.top.sortAscending = 1
|
||||
m.menus[1].focusedCheckedIconUri = m.global.constants.icons.ascending_black
|
||||
m.menus[1].checkedIconUri = m.global.constants.icons.ascending_white
|
||||
else
|
||||
@ -81,7 +81,7 @@ sub optionsSet()
|
||||
end if
|
||||
|
||||
' Filter Tab
|
||||
if m.top.options.filter <> invalid then
|
||||
if m.top.options.filter <> invalid
|
||||
filterContent = CreateObject("roSGNode", "ContentNode")
|
||||
index = 0
|
||||
m.selectedFilterIndex = 0
|
||||
@ -90,7 +90,7 @@ sub optionsSet()
|
||||
entry = filterContent.CreateChild("ContentNode")
|
||||
entry.title = filterItem.Title
|
||||
m.filterNames.push(filterItem.Name)
|
||||
if filterItem.selected <> invalid and filterItem.selected = true then
|
||||
if filterItem.selected <> invalid and filterItem.selected = true
|
||||
m.selectedFilterIndex = index
|
||||
end if
|
||||
index = index + 1
|
||||
@ -121,13 +121,13 @@ end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
|
||||
if key = "down" or (key = "OK" and m.top.findNode("buttons").hasFocus()) then
|
||||
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" then
|
||||
if key = "down"
|
||||
m.menus[m.selectedItem].jumpToItem = 0
|
||||
else
|
||||
m.menus[m.selectedItem].jumpToItem = m.menus[m.selectedItem].itemSelected
|
||||
@ -135,16 +135,16 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
|
||||
return true
|
||||
else if key = "OK"
|
||||
if(m.menus[m.selectedItem].isInFocusChain()) then
|
||||
if m.menus[m.selectedItem].isInFocusChain()
|
||||
' Handle View Screen
|
||||
if(m.selectedItem = 0) then
|
||||
if m.selectedItem = 0
|
||||
m.selectedViewIndex = m.menus[0].itemSelected
|
||||
m.top.view = m.viewNames[m.selectedViewIndex]
|
||||
end if
|
||||
|
||||
' Handle Sort screen
|
||||
if(m.selectedItem = 1) then
|
||||
if m.menus[1].itemSelected <> m.selectedSortIndex then
|
||||
if m.selectedItem = 1
|
||||
if m.menus[1].itemSelected <> m.selectedSortIndex
|
||||
m.menus[1].focusedCheckedIconUri = m.global.constants.icons.ascending_black
|
||||
m.menus[1].checkedIconUri = m.global.constants.icons.ascending_white
|
||||
|
||||
@ -153,7 +153,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
m.top.sortField = m.sortNames[m.selectedSortIndex]
|
||||
else
|
||||
|
||||
if m.top.sortAscending = true then
|
||||
if m.top.sortAscending = true
|
||||
m.top.sortAscending = false
|
||||
m.menus[1].focusedCheckedIconUri = m.global.constants.icons.descending_black
|
||||
m.menus[1].checkedIconUri = m.global.constants.icons.descending_white
|
||||
@ -165,14 +165,14 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
end if
|
||||
end if
|
||||
' Handle Filter screen
|
||||
if(m.selectedItem = 2) then
|
||||
if m.selectedItem = 2
|
||||
m.selectedFilterIndex = m.menus[2].itemSelected
|
||||
m.top.filter = m.filterNames[m.selectedFilterIndex]
|
||||
end if
|
||||
end if
|
||||
return true
|
||||
else if key = "back" or key = "up"
|
||||
if m.menus[m.selectedItem].isInFocusChain() then
|
||||
if m.menus[m.selectedItem].isInFocusChain()
|
||||
m.buttons.setFocus(true)
|
||||
m.menus[m.selectedItem].drawFocusFeedback = false
|
||||
return true
|
||||
|
@ -8,7 +8,7 @@ sub loadItems()
|
||||
|
||||
sort_field = m.top.sortField
|
||||
|
||||
if m.top.sortAscending = true then
|
||||
if m.top.sortAscending = true
|
||||
sort_order = "Ascending"
|
||||
else
|
||||
sort_order = "Descending"
|
||||
@ -26,17 +26,17 @@ sub loadItems()
|
||||
}
|
||||
|
||||
filter = m.top.filter
|
||||
if filter = "All" or filter = "all" then
|
||||
if filter = "All" or filter = "all"
|
||||
' do nothing
|
||||
else if filter = "Favorites" then
|
||||
else if filter = "Favorites"
|
||||
params.append({ Filters: "IsFavorite"})
|
||||
end if
|
||||
|
||||
if m.top.ItemType <> "" then
|
||||
if m.top.ItemType <> ""
|
||||
params.append({ IncludeItemTypes: m.top.ItemType})
|
||||
end if
|
||||
|
||||
if m.top.ItemType = "LiveTV" then
|
||||
if m.top.ItemType = "LiveTV"
|
||||
url = "LiveTv/Channels"
|
||||
else
|
||||
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
|
||||
@ -44,30 +44,30 @@ sub loadItems()
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
|
||||
if data.TotalRecordCount <> invalid then
|
||||
if data.TotalRecordCount <> invalid
|
||||
m.top.totalRecordCount = data.TotalRecordCount
|
||||
end if
|
||||
|
||||
for each item in data.Items
|
||||
|
||||
tmp = invalid
|
||||
if item.Type = "Movie" then
|
||||
if item.Type = "Movie"
|
||||
tmp = CreateObject("roSGNode", "MovieData")
|
||||
else if item.Type = "Series" then
|
||||
else if item.Type = "Series"
|
||||
tmp = CreateObject("roSGNode", "SeriesData")
|
||||
else if item.Type = "BoxSet" then
|
||||
else if item.Type = "BoxSet"
|
||||
tmp = CreateObject("roSGNode", "CollectionData")
|
||||
else if item.Type = "TvChannel" then
|
||||
else if item.Type = "TvChannel"
|
||||
tmp = CreateObject("roSGNode", "ChannelData")
|
||||
else if item.Type = "Folder" then
|
||||
else if item.Type = "Folder" or item.Type = "ChannelFolderItem" or item.Type = "CollectionFolder"
|
||||
tmp = CreateObject("roSGNode", "FolderData")
|
||||
else if item.Type = "Video" then
|
||||
else if item.Type = "Video"
|
||||
tmp = CreateObject("roSGNode", "VideoData")
|
||||
else
|
||||
print "Unknown Type: " item.Type
|
||||
print "[LoadItems] Unknown Type: " item.Type
|
||||
end if
|
||||
|
||||
if tmp <> invalid then
|
||||
if tmp <> invalid
|
||||
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
|
@ -42,7 +42,7 @@ sub redraw()
|
||||
fontHeight = m.top.fontHeight
|
||||
fontWidth = m.top.fontWidth
|
||||
|
||||
if text.text.len() > 0 then
|
||||
if text.text.len() > 0
|
||||
textWidth = boxWidth - ( border * 2 )
|
||||
text.width = textWidth
|
||||
text.numLines = int(fontWidth / textWidth) + 1
|
||||
@ -58,7 +58,7 @@ sub redraw()
|
||||
options.itemSpacing = "[0,20]"
|
||||
|
||||
options.numRows = m.top.options.count()
|
||||
if options.numRows > maxRows then
|
||||
if options.numRows > maxRows
|
||||
options.numRows = maxRows
|
||||
options.wrapDividerHeight = 0
|
||||
options.vertFocusAnimationStyle= "fixedFocusWrap"
|
||||
|
@ -1,8 +1,5 @@
|
||||
sub init()
|
||||
m.top.id = "overhang"
|
||||
' set opacity
|
||||
bgg = m.top.findNode("overlayBackgroundGroup")
|
||||
bgg.opacity = 0.333
|
||||
' hide seperators till they're needed
|
||||
leftSeperator = m.top.findNode("overlayLeftSeperator")
|
||||
leftSeperator.visible = "false"
|
||||
@ -32,9 +29,9 @@ sub init()
|
||||
end sub
|
||||
|
||||
|
||||
function updateTitle()
|
||||
sub updateTitle()
|
||||
leftSeperator = m.top.findNode("overlayLeftSeperator")
|
||||
if m.top.title <> "" then
|
||||
if m.top.title <> ""
|
||||
leftSeperator.visible = "true"
|
||||
else
|
||||
leftSeperator.visible = "false"
|
||||
@ -42,21 +39,21 @@ function updateTitle()
|
||||
title = m.top.findNode("overlayTitle")
|
||||
title.text = m.top.title
|
||||
resetTime()
|
||||
end function
|
||||
end sub
|
||||
|
||||
function updateUser()
|
||||
sub updateUser()
|
||||
rightSeperator = m.top.findNode("overlayRightSeperator")
|
||||
if m.top.currentUser <> "" then
|
||||
if m.top.currentUser <> ""
|
||||
rightSeperator.visible = "true"
|
||||
else
|
||||
rightSeperator.visible = "false"
|
||||
end if
|
||||
user = m.top.findNode("overlayCurrentUser")
|
||||
user.text = m.top.currentUser
|
||||
end function
|
||||
end sub
|
||||
|
||||
function updateTime()
|
||||
if (m.currentMinutes + 1) > 59 then
|
||||
sub updateTime()
|
||||
if (m.currentMinutes + 1) > 59
|
||||
m.currentHours = m.currentHours + 1
|
||||
m.currentMinutes = 0
|
||||
else
|
||||
@ -64,9 +61,9 @@ function updateTime()
|
||||
end if
|
||||
|
||||
updateTimeDisplay()
|
||||
end function
|
||||
end sub
|
||||
|
||||
function resetTime()
|
||||
sub resetTime()
|
||||
m.currentTimeTimer.control = "stop"
|
||||
|
||||
currentTime = CreateObject("roDateTime")
|
||||
@ -78,31 +75,31 @@ function resetTime()
|
||||
m.currentMinutes = currentTime.GetMinutes()
|
||||
|
||||
updateTimeDisplay()
|
||||
end function
|
||||
end sub
|
||||
|
||||
function updateTimeDisplay()
|
||||
sub updateTimeDisplay()
|
||||
overlayHours = m.top.findNode("overlayHours")
|
||||
overlayMinutes = m.top.findNode("overlayMinutes")
|
||||
overlayMeridian = m.top.findNode("overlayMeridian")
|
||||
|
||||
if m.clockFormat = "24h" then
|
||||
if m.clockFormat = "24h"
|
||||
overlayMeridian.text = ""
|
||||
if m.currentHours < 10 then
|
||||
if m.currentHours < 10
|
||||
overlayHours.text = "0" + StrI(m.currentHours).trim()
|
||||
else
|
||||
overlayHours.text = m.currentHours
|
||||
end if
|
||||
else
|
||||
if m.currentHours < 12 then
|
||||
if m.currentHours < 12
|
||||
overlayMeridian.text = "AM"
|
||||
if m.currentHours = 0 then
|
||||
if m.currentHours = 0
|
||||
overlayHours.text = "12"
|
||||
else
|
||||
overlayHours.text = m.currentHours
|
||||
end if
|
||||
else
|
||||
overlayMeridian.text = "PM"
|
||||
if m.currentHours = 12 then
|
||||
if m.currentHours = 12
|
||||
overlayHours.text = "12"
|
||||
else
|
||||
overlayHours.text = m.currentHours - 12
|
||||
@ -110,21 +107,21 @@ function updateTimeDisplay()
|
||||
end if
|
||||
end if
|
||||
|
||||
if m.currentMinutes < 10 then
|
||||
if m.currentMinutes < 10
|
||||
overlayMinutes.text = "0" + StrI(m.currentMinutes).trim()
|
||||
else
|
||||
overlayMinutes.text = m.currentMinutes
|
||||
end if
|
||||
end function
|
||||
end sub
|
||||
|
||||
function updateOptions()
|
||||
sub updateOptions()
|
||||
optionText = m.top.findNode("overlayOptionsText")
|
||||
optionStar = m.top.findNode("overlayOptionsStar")
|
||||
if m.top.showOptions = true then
|
||||
if m.top.showOptions = true
|
||||
optionText.visible = true
|
||||
optionStar.visible = true
|
||||
else
|
||||
optionText.visible = false
|
||||
optionStar.visible = false
|
||||
end if
|
||||
end function
|
||||
end sub
|
@ -1,14 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="JFOverhang" extends="Group">
|
||||
<children>
|
||||
<LayoutGroup id="overlayBackgroundGroup" layoutDirection="horiz" translation="[0, 0]" >
|
||||
<Rectangle
|
||||
id="overlayBackground"
|
||||
color="#000000"
|
||||
width="1920"
|
||||
height="125"
|
||||
translation="[0,0]" />
|
||||
</LayoutGroup>
|
||||
<Poster id="overlayLogo"
|
||||
uri="pkg:/images/logo.png"
|
||||
translation="[70, 53]"
|
||||
|
@ -1,6 +1,8 @@
|
||||
sub init()
|
||||
m.top.observeField("state", "onState")
|
||||
m.bufferPercentage = 0 ' Track whether content is being loaded
|
||||
m.top.transcodeReasons = []
|
||||
|
||||
end sub
|
||||
|
||||
|
||||
@ -9,13 +11,13 @@ end sub
|
||||
sub onState(msg)
|
||||
|
||||
' When buffering, start timer to monitor buffering process
|
||||
if m.top.state = "buffering" and m.bufferCheckTimer <> invalid then
|
||||
if m.top.state = "buffering" and m.bufferCheckTimer <> invalid
|
||||
|
||||
' start timer
|
||||
m.bufferCheckTimer = m.top.findNode("bufferCheckTimer")
|
||||
m.bufferCheckTimer.control = "start"
|
||||
m.bufferCheckTimer.ObserveField("fire", "bufferCheck")
|
||||
else if m.top.state = "error" then
|
||||
else if m.top.state = "error"
|
||||
|
||||
' If an error was encountered, Display dialog
|
||||
dialog = createObject("roSGNode", "Dialog")
|
||||
@ -43,10 +45,10 @@ sub bufferCheck(msg)
|
||||
return
|
||||
end if
|
||||
|
||||
if m.top.bufferingStatus <> invalid then
|
||||
if m.top.bufferingStatus <> invalid
|
||||
|
||||
' Check that the buffering percentage is increasing
|
||||
if m.top.bufferingStatus["percentage"] > m.bufferPercentage then
|
||||
if m.top.bufferingStatus["percentage"] > m.bufferPercentage
|
||||
m.bufferPercentage = m.top.bufferingStatus["percentage"]
|
||||
else
|
||||
' If buffering has stopped Display dialog
|
||||
@ -78,7 +80,7 @@ end sub
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if m.top.Subtitles.count() and key = "down" then
|
||||
if m.top.Subtitles.count() and key = "down"
|
||||
m.top.selectSubtitlePressed = true
|
||||
return true
|
||||
end if
|
||||
|
@ -7,13 +7,19 @@
|
||||
<field id="Subtitles" type="array" />
|
||||
<field id="SelectedSubtitle" type="integer" />
|
||||
<field id="captionMode" type="string" />
|
||||
<field id="transcodeParams" type="assocarray" />
|
||||
<field id="container" type="string" />
|
||||
<field id="directPlaySupported" type="boolean" />
|
||||
<field id="decodeAudioSupported" type="boolean" />
|
||||
<field id="isTranscoded" type="boolean" />
|
||||
<field id="systemOverlay" type="boolean" value="false" />
|
||||
<field id="showID" type="string" />
|
||||
|
||||
<field id="transcodeParams" type="assocarray" />
|
||||
<field id="isTranscoded" type="boolean" />
|
||||
<field id="transcodeReasons" type="array" />
|
||||
|
||||
<field id="videoId" type="string" />
|
||||
<field id="mediaSourceId" type="string" />
|
||||
<field id="audioIndex" type="integer" />
|
||||
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="JFVideo.brs" />
|
||||
<children>
|
||||
|
@ -59,25 +59,25 @@ sub updateSize()
|
||||
|
||||
end sub
|
||||
|
||||
function itemContentChanged() as void
|
||||
m.poster = m.top.findNode("poster")
|
||||
itemData = m.top.itemContent
|
||||
m.title.text = itemData.title
|
||||
if itemData.json.lookup("Type") = "Episode" and itemData.json.IndexNumber <> invalid
|
||||
m.title.text = StrI(itemData.json.IndexNumber) + ". " + m.title.text
|
||||
end if
|
||||
m.staticTitle.text = m.title.text
|
||||
sub itemContentChanged() as void
|
||||
m.poster = m.top.findNode("poster")
|
||||
itemData = m.top.itemContent
|
||||
m.title.text = itemData.title
|
||||
if itemData.json.lookup("Type") = "Episode" and itemData.json.IndexNumber <> invalid
|
||||
m.title.text = StrI(itemData.json.IndexNumber) + ". " + m.title.text
|
||||
end if
|
||||
m.staticTitle.text = m.title.text
|
||||
|
||||
m.poster.uri = itemData.posterUrl
|
||||
m.poster.uri = itemData.posterUrl
|
||||
|
||||
updateSize()
|
||||
end function
|
||||
updateSize()
|
||||
end sub
|
||||
|
||||
'
|
||||
' Enable title scrolling based on item Focus
|
||||
sub focusChanged()
|
||||
|
||||
if m.top.itemHasFocus = true then
|
||||
if m.top.itemHasFocus = true
|
||||
m.title.repeatCount = -1
|
||||
m.staticTitle.visible = false
|
||||
m.title.visible = true
|
||||
|
@ -1,127 +0,0 @@
|
||||
sub init()
|
||||
m.top.currentPage = 0
|
||||
m.top.maxPages = 0
|
||||
m.top.layoutDirection = "horiz"
|
||||
m.top.horizAlignment = "center"
|
||||
m.top.vertAlignment = "center"
|
||||
end sub
|
||||
|
||||
sub recountPages()
|
||||
if m.top.currentPage = 0 or m.top.maxPages = 0
|
||||
return
|
||||
end if
|
||||
while m.top.getChildCount() > 0
|
||||
m.top.removeChildIndex(0)
|
||||
end while
|
||||
|
||||
currentPage = m.top.currentPage
|
||||
maxPages = m.top.maxPages
|
||||
|
||||
minShown = 1
|
||||
maxShown = maxPages
|
||||
|
||||
if currentPage > 1
|
||||
addPage("<")
|
||||
minShown = currentPage - 3
|
||||
end if
|
||||
|
||||
if minShown <= 0 then minShown = 1
|
||||
|
||||
if currentPage < maxPages then maxShown = currentPage + 3
|
||||
if maxShown >= maxPages then maxShown = maxPages
|
||||
|
||||
for i=minShown to maxShown step 1
|
||||
addPage(i)
|
||||
end for
|
||||
|
||||
if currentPage <> maxPages
|
||||
addPage(">")
|
||||
end if
|
||||
|
||||
m.top.pageFocused = m.top.findNode(stri(currentPage).trim())
|
||||
|
||||
m.top.pageFocused.color = "#00A4DCff"
|
||||
|
||||
updateLayout()
|
||||
end sub
|
||||
|
||||
sub updateLayout()
|
||||
dimensions = m.top.getScene().currentDesignResolution
|
||||
height = 115
|
||||
m.top.translation = [dimensions.width / 2, dimensions.height - (height / 2)]
|
||||
end sub
|
||||
|
||||
sub addPage(i)
|
||||
p = CreateObject("roSGNode", "Label")
|
||||
p.height = 50
|
||||
p.width = 50
|
||||
p.color = "#a1a1a1FF"
|
||||
if type(i) = "roInt"
|
||||
i = stri(i).trim()
|
||||
end if
|
||||
p.id = i
|
||||
p.text = i
|
||||
m.top.appendChild(p)
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "OK"
|
||||
p = m.top.focusedChild.id
|
||||
if p = ">"
|
||||
m.top.pageSelected = m.top.currentPage + 1
|
||||
else if p = "<"
|
||||
m.top.pageSelected = m.top.currentPage - 1
|
||||
else
|
||||
m.top.pageSelected = m.top.focusedChild.id
|
||||
end if
|
||||
m.top.currentPage = m.top.pageSelected
|
||||
recountPages()
|
||||
return true
|
||||
else if key = "left"
|
||||
focusPrev()
|
||||
return true
|
||||
else if key = "right"
|
||||
focusNext()
|
||||
return true
|
||||
else if key = "up"
|
||||
if m.top.getParent().findNode("picker") <> invalid
|
||||
m.top.getParent().findNode("picker").setFocus(true)
|
||||
else if m.top.getParent().lastFocus <> invalid
|
||||
m.top.getParent().lastFocus.setFocus(true)
|
||||
else
|
||||
m.top.getParent().setFocus(true)
|
||||
end if
|
||||
return true
|
||||
end if
|
||||
|
||||
return false
|
||||
end function
|
||||
|
||||
sub focusNext()
|
||||
i = getFocusIndex()
|
||||
if (i + 1) = m.top.getChildCount() then return
|
||||
|
||||
m.top.pageFocused.color = "#a1a1a1FF"
|
||||
m.top.pageFocused = m.top.getChild(i + 1)
|
||||
m.top.pageFocused.color = "#00A4DCff"
|
||||
m.top.pageFocused.setFocus(true)
|
||||
end sub
|
||||
|
||||
sub focusPrev()
|
||||
i = getFocusIndex()
|
||||
if i = 0 then return
|
||||
|
||||
m.top.pageFocused.color = "#a1a1a1FF"
|
||||
m.top.pageFocused = m.top.getChild(i - 1)
|
||||
m.top.pageFocused.color = "#00A4DCff"
|
||||
m.top.pageFocused.setFocus(true)
|
||||
end sub
|
||||
|
||||
function getFocusIndex()
|
||||
for i=0 to m.top.getChildCount() step 1
|
||||
if m.top.getChild(i).id = m.top.pageFocused.id then return i
|
||||
end for
|
||||
return invalid
|
||||
end function
|
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="Pager" extends="LayoutGroup" >
|
||||
<interface>
|
||||
<field id="currentPage" type="integer" onChange="recountPages" />
|
||||
<field id="maxPages" type="integer" onChange="recountPages" />
|
||||
<field id="pageFocused" type="node" />
|
||||
<field id="pageSelected" type="string" />
|
||||
<field id="escape" type="boolean" alwaysNotify="true" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="Pager.brs" />
|
||||
</component>
|
@ -1,116 +0,0 @@
|
||||
sub init()
|
||||
|
||||
m.rowList = m.top.findNode("RowList") ' createObject("roSGNode", "RowList")
|
||||
' m.top.appendChild(m.rowList)
|
||||
|
||||
m.rowList.itemComponentName = "HomeItem"
|
||||
|
||||
formatRowList()
|
||||
|
||||
m.rowList.setfocus(true)
|
||||
|
||||
m.rowList.observeField("rowItemSelected", "itemSelected")
|
||||
|
||||
end sub
|
||||
|
||||
sub formatRowList()
|
||||
|
||||
' how many rows are visible on the screen
|
||||
m.rowList.numRows = 2
|
||||
|
||||
m.rowList.rowFocusAnimationStyle = "fixedFocusWrap"
|
||||
m.rowList.vertFocusAnimationStyle = "fixedFocus"
|
||||
|
||||
m.rowList.showRowLabel = [true]
|
||||
m.rowList.rowLabelOffset = [0, 20]
|
||||
m.rowList.showRowCounter = [true]
|
||||
|
||||
sideborder = 100
|
||||
m.rowList.translation = [111, 155]
|
||||
|
||||
m.rowItemSizes = []
|
||||
|
||||
itemWidth = 480
|
||||
itemHeight = 330
|
||||
|
||||
m.rowList.itemSize = [1920 - 111 - 27, itemHeight]
|
||||
' spacing between rows
|
||||
m.rowList.itemSpacing = [0, 105]
|
||||
|
||||
' spacing between items in a row
|
||||
m.rowList.rowItemSpacing = [20, 0]
|
||||
|
||||
m.rowList.visible = true
|
||||
end sub
|
||||
|
||||
|
||||
sub setupRows()
|
||||
|
||||
for each item in m.top.objects.Items
|
||||
|
||||
homeItem = CreateObject("roSGNode", "HomeData")
|
||||
homeItem.json = item.json
|
||||
|
||||
if homeItem.Type = "Video" or homeItem.Type = "Movie" or homeItem.Type = "Episode" then
|
||||
|
||||
if m.videoRow = invalid then
|
||||
m.videoRow = CreateObject("roSGNode", "HomeRow")
|
||||
m.videoRow.title = tr("Videos")
|
||||
m.videoRow.usePoster = true
|
||||
m.videoRow.imageWidth = 180
|
||||
end if
|
||||
|
||||
m.videoRow.appendChild(homeItem)
|
||||
|
||||
else if homeItem.Type = "MusicAlbum"
|
||||
|
||||
if m.albumRow = invalid then
|
||||
m.albumRow = CreateObject("roSGNode", "HomeRow")
|
||||
m.albumRow.imageWidth = 261
|
||||
m.albumRow.title = tr("Albums")
|
||||
m.albumRow.usePoster = true
|
||||
end if
|
||||
|
||||
m.albumRow.appendChild(homeItem)
|
||||
|
||||
else if homeItem.Type = "Series"
|
||||
|
||||
if m.seriesRow = invalid then
|
||||
m.seriesRow = CreateObject("roSGNode", "HomeRow")
|
||||
m.seriesRow.title = tr("Series")
|
||||
m.seriesRow.usePoster = true
|
||||
m.seriesRow.imageWidth = 180
|
||||
end if
|
||||
|
||||
m.seriesRow.appendChild(homeItem)
|
||||
|
||||
else
|
||||
print "Collection - Unknown Type ", homeItem.Type
|
||||
end if
|
||||
end for
|
||||
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
|
||||
if m.videoRow <> invalid then
|
||||
data.appendChild(m.videoRow)
|
||||
m.rowItemSizes.push([188, 331])
|
||||
end if
|
||||
|
||||
if m.seriesRow <> invalid then
|
||||
data.appendChild(m.seriesRow)
|
||||
m.rowItemSizes.push([188, 331])
|
||||
end if
|
||||
|
||||
if m.albumRow <> invalid then
|
||||
data.appendChild(m.albumRow)
|
||||
m.rowItemSizes.push([261, 331])
|
||||
end if
|
||||
|
||||
m.rowList.rowItemSize = m.rowItemSizes
|
||||
m.rowList.content = data
|
||||
|
||||
end sub
|
||||
|
||||
function itemSelected()
|
||||
m.top.selectedItem = m.rowList.content.getChild(m.rowList.rowItemSelected[0]).getChild(m.rowList.rowItemSelected[1])
|
||||
end function
|
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="CollectionDetail" extends="JFGroup">
|
||||
<children>
|
||||
<RowList id="rowList" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="collectionId" type="string" onChange="collectionIdChanged" />
|
||||
<field id="selectedItem" type="node" alwaysNotify="true" />
|
||||
<field id="objects" type="assocarray" onChange="setupRows" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="CollectionDetail.brs" />
|
||||
</component>
|
@ -13,22 +13,20 @@ sub init()
|
||||
|
||||
end sub
|
||||
|
||||
function setData()
|
||||
sub setData()
|
||||
items = m.top.configItems
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
data.appendChildren(items)
|
||||
|
||||
m.top.content = data
|
||||
end function
|
||||
end sub
|
||||
|
||||
function onItemSelected()
|
||||
print "HI"
|
||||
sub onItemSelected()
|
||||
i = m.top.itemSelected
|
||||
itemField = m.top.content.getchild(i)
|
||||
|
||||
show_dialog(itemField)
|
||||
|
||||
end function
|
||||
end sub
|
||||
|
||||
function onDialogButton()
|
||||
d = m.dialog
|
||||
@ -42,6 +40,7 @@ function onDialogButton()
|
||||
dismiss_dialog()
|
||||
return true
|
||||
end if
|
||||
return false
|
||||
end function
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ function onKeyEvent(key as String, press as Boolean) as Boolean
|
||||
|
||||
list = m.top.findNode("configOptions")
|
||||
button = m.top.findNode("submit")
|
||||
if key = "back" then
|
||||
if key = "back"
|
||||
m.top.backPressed = true
|
||||
else if key = "down" and button.focusedChild = invalid
|
||||
limit = list.content.getChildren(-1, 0).count() - 1
|
||||
|
@ -19,8 +19,10 @@
|
||||
translation="[150, 450]" />
|
||||
<label text=""
|
||||
id="alert"
|
||||
wrap="true"
|
||||
width="1620"
|
||||
font="font:MediumSystemFont"
|
||||
translation="[150, 555]" />
|
||||
translation="[150, 580]" />
|
||||
</children>
|
||||
<script type="text/brightscript" uri="ConfigScene.brs"/>
|
||||
</component>
|
||||
|
@ -11,7 +11,7 @@ end sub
|
||||
sub setPoster()
|
||||
if m.top.image <> invalid
|
||||
m.top.posterURL = m.top.image.url
|
||||
else if m.top.json.ImageTags <> invalid and m.top.json.ImageTags.Primary <> invalid then
|
||||
else if m.top.json.ImageTags <> invalid and 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)
|
||||
end if
|
||||
|
@ -17,16 +17,16 @@ sub setPoster()
|
||||
m.top.posterURL = m.top.image.url
|
||||
else
|
||||
|
||||
if m.top.json.ImageTags.Primary <> invalid then
|
||||
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 <> invalid then
|
||||
else if m.top.json.BackdropImageTags <> invalid
|
||||
imgParams = { "maxHeight": 440, "Tag" : m.top.json.BackdropImageTags[0] }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
' Add Backdrop Image
|
||||
if m.top.json.BackdropImageTags <> invalid then
|
||||
if m.top.json.BackdropImageTags <> invalid
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag" : m.top.json.BackdropImageTags[0] }
|
||||
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
@ -6,4 +6,19 @@ sub setFields()
|
||||
m.top.Type = "Folder"
|
||||
|
||||
m.top.iconUrl = "pkg:/images/media_type_icons/folder_white.png"
|
||||
' This is a temporary measure to avoid displaying landscape photos
|
||||
' in GridItem components that only support portrait. It will be fixed
|
||||
' after the ItemGrid is reworked.
|
||||
if m.top.json.Type <> "CollectionFolder"
|
||||
setPoster()
|
||||
end if
|
||||
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)
|
||||
end if
|
||||
end sub
|
||||
|
@ -1,4 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="FolderData" extends="JFContentItem">
|
||||
<script type="text/brightscript" uri="FolderData.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>
|
||||
|
@ -7,7 +7,7 @@ sub setData()
|
||||
m.top.name = datum.name
|
||||
m.top.type = datum.type
|
||||
|
||||
if datum.CollectionType = invalid then
|
||||
if datum.CollectionType = invalid
|
||||
m.top.CollectionType = datum.type
|
||||
else
|
||||
m.top.CollectionType = datum.CollectionType
|
||||
@ -15,27 +15,29 @@ sub setData()
|
||||
|
||||
' Set appropriate Images for Wide and Tall based on type
|
||||
|
||||
if datum.type = "CollectionFolder" OR datum.type = "UserView" then
|
||||
if datum.type = "CollectionFolder" OR datum.type = "UserView"
|
||||
params = { "Tag" : datum.ImageTags.Primary, "maxHeight" : 261, "maxWidth" : 464 }
|
||||
m.top.thumbnailURL = ImageURL(datum.id, "Primary", params)
|
||||
m.top.widePosterUrl = m.top.thumbnailURL
|
||||
|
||||
' Add Icon URLs for display if there is no Poster
|
||||
if datum.CollectionType = "livetv" then
|
||||
if datum.CollectionType = "livetv"
|
||||
m.top.iconUrl = "pkg:/images/media_type_icons/live_tv_white.png"
|
||||
else if datum.CollectionType = "folders"
|
||||
m.top.iconUrl = "pkg:/images/media_type_icons/folder_white.png"
|
||||
end if
|
||||
|
||||
else if datum.type = "Episode" then
|
||||
else if datum.type = "Episode"
|
||||
imgParams = { "AddPlayedIndicator": datum.UserData.Played }
|
||||
|
||||
if datum.UserData.PlayedPercentage <> invalid then
|
||||
if datum.UserData.PlayedPercentage <> invalid
|
||||
imgParams.Append({ "PercentPlayed": datum.UserData.PlayedPercentage })
|
||||
end if
|
||||
|
||||
imgParams.Append({ "maxHeight": 261 })
|
||||
imgParams.Append({ "maxWidth": 464 })
|
||||
|
||||
if datum.ImageTags.Primary <> invalid then
|
||||
if datum.ImageTags.Primary <> invalid
|
||||
param = { "Tag" : datum.ImageTags.Primary }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
@ -43,51 +45,51 @@ sub setData()
|
||||
m.top.thumbnailURL = ImageURL(datum.id, "Primary", imgParams)
|
||||
|
||||
' Add Wide Poster (Series Backdrop)
|
||||
if datum.ParentThumbImageTag <> invalid then
|
||||
if datum.ParentThumbImageTag <> invalid
|
||||
imgParams["Tag"] = datum.ParentThumbImageTag
|
||||
m.top.widePosterUrl = ImageURL(datum.ParentThumbItemId, "Thumb", imgParams)
|
||||
else if datum.ParentBackdropImageTags <> invalid then
|
||||
else if datum.ParentBackdropImageTags <> invalid
|
||||
imgParams["Tag"] = datum.ParentBackdropImageTags[0]
|
||||
m.top.widePosterUrl = ImageURL(datum.ParentBackdropItemId, "Backdrop", imgParams)
|
||||
else if datum.ImageTags.Primary <> invalid then
|
||||
else if datum.ImageTags.Primary <> invalid
|
||||
imgParams["Tag"] = datum.SeriesPrimaryImageTag
|
||||
m.top.widePosterUrl = ImageURL(datum.id, "Primary", imgParams)
|
||||
end if
|
||||
|
||||
else if datum.type = "Series" then
|
||||
else if datum.type = "Series"
|
||||
imgParams = { "maxHeight": 261 }
|
||||
imgParams.Append({ "maxWidth": 464 })
|
||||
|
||||
if datum.UserData.UnplayedItemCount > 0 then
|
||||
if datum.UserData.UnplayedItemCount > 0
|
||||
imgParams["UnplayedCount"] = datum.UserData.UnplayedItemCount
|
||||
end if
|
||||
|
||||
if datum.ImageTags.Primary <> invalid then
|
||||
if datum.ImageTags.Primary <> invalid
|
||||
imgParams["Tag"] = datum.ImageTags.Primary
|
||||
end if
|
||||
|
||||
m.top.posterURL = ImageURL(datum.id, "Primary", imgParams)
|
||||
|
||||
' Add Wide Poster (Series Backdrop)
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid then
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid
|
||||
imgParams["Tag"] = datum.imageTags.Thumb
|
||||
m.top.widePosterUrl = ImageURL(datum.Id, "Thumb", imgParams)
|
||||
else if datum.BackdropImageTags <> invalid then
|
||||
else if datum.BackdropImageTags <> invalid
|
||||
imgParams["Tag"] = datum.BackdropImageTags[0]
|
||||
m.top.widePosterUrl = ImageURL(datum.Id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
else if datum.type = "Movie" then
|
||||
else if datum.type = "Movie"
|
||||
imgParams = { AddPlayedIndicator: datum.UserData.Played }
|
||||
|
||||
if datum.UserData.PlayedPercentage <> invalid then
|
||||
if datum.UserData.PlayedPercentage <> invalid
|
||||
imgParams.Append({ "PercentPlayed": datum.UserData.PlayedPercentage })
|
||||
end if
|
||||
|
||||
imgParams.Append({ "maxHeight": 261 })
|
||||
imgParams.Append({ "maxWidth": 175 })
|
||||
|
||||
if datum.ImageTags.Primary <> invalid then
|
||||
if datum.ImageTags.Primary <> invalid
|
||||
param = { "Tag" : datum.ImageTags.Primary }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
@ -97,25 +99,25 @@ sub setData()
|
||||
' For wide image, use backdrop
|
||||
imgParams["maxWidth"] = 464
|
||||
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid then
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid
|
||||
imgParams["Tag"] = datum.imageTags.Thumb
|
||||
m.top.thumbnailUrl = ImageURL(datum.Id, "Thumb", imgParams)
|
||||
else if datum.BackdropImageTags[0] <> invalid then
|
||||
else if datum.BackdropImageTags[0] <> invalid
|
||||
imgParams["Tag"] = datum.BackdropImageTags[0]
|
||||
m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
else if datum.type = "Video" then
|
||||
else if datum.type = "Video"
|
||||
imgParams = { AddPlayedIndicator: datum.UserData.Played }
|
||||
|
||||
if datum.UserData.PlayedPercentage <> invalid then
|
||||
if datum.UserData.PlayedPercentage <> invalid
|
||||
imgParams.Append({ "PercentPlayed": datum.UserData.PlayedPercentage })
|
||||
end if
|
||||
|
||||
imgParams.Append({ "maxHeight": 261 })
|
||||
imgParams.Append({ "maxWidth": 175 })
|
||||
|
||||
if datum.ImageTags.Primary <> invalid then
|
||||
if datum.ImageTags.Primary <> invalid
|
||||
param = { "Tag" : datum.ImageTags.Primary }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
@ -125,20 +127,20 @@ sub setData()
|
||||
' For wide image, use backdrop
|
||||
imgParams["maxWidth"] = 464
|
||||
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid then
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid
|
||||
imgParams["Tag"] = datum.imageTags.Thumb
|
||||
m.top.thumbnailUrl = ImageURL(datum.Id, "Thumb", imgParams)
|
||||
else if datum.BackdropImageTags[0] <> invalid then
|
||||
else if datum.BackdropImageTags[0] <> invalid
|
||||
imgParams["Tag"] = datum.BackdropImageTags[0]
|
||||
m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams)
|
||||
end if
|
||||
else if datum.type = "MusicAlbum" then
|
||||
else if datum.type = "MusicAlbum"
|
||||
params = { "Tag" : datum.ImageTags.Primary, "maxHeight" : 261, "maxWidth" : 261 }
|
||||
m.top.thumbnailURL = ImageURL(datum.id, "Primary", params)
|
||||
m.top.widePosterUrl = m.top.thumbnailURL
|
||||
m.top.posterUrl = m.top.thumbnailURL
|
||||
|
||||
else if datum.type = "TvChannel" OR datum.type = "Channel" then
|
||||
else if datum.type = "TvChannel" OR datum.type = "Channel"
|
||||
params = { "Tag" : datum.ImageTags.Primary, "maxHeight" : 261, "maxWidth" : 464 }
|
||||
m.top.thumbnailURL = ImageURL(datum.id, "Primary", params)
|
||||
m.top.widePosterUrl = m.top.thumbnailURL
|
||||
|
@ -8,13 +8,13 @@ sub setFields()
|
||||
m.top.watched = json.UserData.played
|
||||
m.top.Type = "Movie"
|
||||
|
||||
if json.ProductionYear <> invalid then
|
||||
if json.ProductionYear <> invalid
|
||||
m.top.SubTitle = json.ProductionYear
|
||||
end if
|
||||
|
||||
if json.OfficialRating <> invalid and json.OfficialRating <> "" then
|
||||
if json.OfficialRating <> invalid and json.OfficialRating <> ""
|
||||
m.top.Rating = json.OfficialRating
|
||||
if m.top.SubTitle <> "" then
|
||||
if m.top.SubTitle <> ""
|
||||
m.top.SubTitle = m.top.SubTitle + " - " + m.top.Rating
|
||||
else
|
||||
m.top.SubTitle = m.top.Rating
|
||||
@ -30,17 +30,19 @@ sub setPoster()
|
||||
m.top.posterURL = m.top.image.url
|
||||
else
|
||||
|
||||
if m.top.json.ImageTags.Primary <> invalid then
|
||||
|
||||
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 <> invalid then
|
||||
imgParams = { "maxHeight": 440, "Tag" : m.top.json.BackdropImageTags[0] }
|
||||
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 <> invalid then
|
||||
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
|
||||
|
@ -18,7 +18,7 @@ sub setFields()
|
||||
m.top.endDate = json.endDate
|
||||
m.top.channelId = json.channelId
|
||||
|
||||
if json.IsSeries <> invalid and json.IsSeries = true then
|
||||
if json.IsSeries <> invalid and json.IsSeries = true
|
||||
if json.IndexNumber <> invalid
|
||||
m.top.episodeNumber = json.IndexNumber
|
||||
end if
|
||||
@ -35,7 +35,7 @@ sub setFields()
|
||||
if m.top.image <> invalid
|
||||
m.top.posterURL = m.top.image.url
|
||||
else
|
||||
if m.top.json.ImageTags <> invalid and m.top.json.ImageTags.Thumb <> invalid then
|
||||
if m.top.json.ImageTags <> invalid and m.top.json.ImageTags.Thumb <> invalid
|
||||
imgParams = { "maxHeight": 500, "maxWidth": 500, "Tag" : m.top.json.ImageTags.Thumb }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Thumb", imgParams)
|
||||
end if
|
||||
|
@ -9,13 +9,13 @@ sub setFields()
|
||||
m.top.Type = "Series"
|
||||
m.top.overview = json.overview
|
||||
|
||||
if json.ProductionYear <> invalid then
|
||||
if json.ProductionYear <> invalid
|
||||
m.top.SubTitle = json.ProductionYear
|
||||
end if
|
||||
|
||||
if json.OfficialRating <> invalid and json.OfficialRating <> "" then
|
||||
if json.OfficialRating <> invalid and json.OfficialRating <> ""
|
||||
m.top.Rating = json.OfficialRating
|
||||
if m.top.SubTitle <> "" then
|
||||
if m.top.SubTitle <> ""
|
||||
m.top.SubTitle = m.top.SubTitle + " - " + m.top.Rating
|
||||
else
|
||||
m.top.SubTitle = m.top.Rating
|
||||
@ -30,17 +30,17 @@ sub setPoster()
|
||||
m.top.posterURL = m.top.image.url
|
||||
else
|
||||
|
||||
if m.top.json.ImageTags.Primary <> invalid then
|
||||
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 <> invalid then
|
||||
else if m.top.json.BackdropImageTags <> invalid
|
||||
imgParams = { "maxHeight": 440, "Tag" : m.top.json.BackdropImageTags[0] }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
' Add Backdrop Image
|
||||
if m.top.json.BackdropImageTags <> invalid then
|
||||
if m.top.json.BackdropImageTags <> invalid
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag" : m.top.json.BackdropImageTags[0] }
|
||||
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
@ -3,21 +3,21 @@ sub setDataFromJSON()
|
||||
loadFromJSON(json)
|
||||
end sub
|
||||
|
||||
function loadFromJSON(json)
|
||||
sub loadFromJSON(json)
|
||||
m.top.id = json.User.id
|
||||
|
||||
m.top.username = json.User.name
|
||||
m.top.token = json.AccessToken
|
||||
end function
|
||||
end sub
|
||||
|
||||
function loadFromRegistry(id as string)
|
||||
sub loadFromRegistry(id as string)
|
||||
m.top.id = id
|
||||
|
||||
m.top.username = get_user_setting("username")
|
||||
m.top.token = get_user_setting("token")
|
||||
end function
|
||||
end sub
|
||||
|
||||
function saveToRegistry()
|
||||
sub saveToRegistry()
|
||||
set_user_setting("username", m.top.username)
|
||||
set_user_setting("token", m.top.token)
|
||||
|
||||
@ -34,9 +34,9 @@ function saveToRegistry()
|
||||
})
|
||||
set_setting("available_users", formatJson(users))
|
||||
end if
|
||||
end function
|
||||
end sub
|
||||
|
||||
function removeFromRegistry()
|
||||
sub removeFromRegistry()
|
||||
new_users = []
|
||||
users = parseJson(get_setting("available_users", "[]"))
|
||||
for each user in users
|
||||
@ -44,7 +44,7 @@ function removeFromRegistry()
|
||||
end for
|
||||
|
||||
set_setting("available_users", formatJson(new_users))
|
||||
end function
|
||||
end sub
|
||||
|
||||
function getPreference(key as string, default as string)
|
||||
return get_user_setting("pref-" + key, default)
|
||||
@ -54,10 +54,10 @@ function setPreference(key as string, value as string)
|
||||
return set_user_setting("pref-" + key, value)
|
||||
end function
|
||||
|
||||
function setActive()
|
||||
sub setActive()
|
||||
set_setting("active_user", m.top.id)
|
||||
end function
|
||||
end sub
|
||||
|
||||
function setServer(hostname as string)
|
||||
sub setServer(hostname as string)
|
||||
m.top.server = hostname
|
||||
end function
|
||||
end sub
|
@ -14,7 +14,7 @@ end sub
|
||||
sub setPoster()
|
||||
if m.top.image <> invalid
|
||||
m.top.posterURL = m.top.image.url
|
||||
else if m.top.json.ImageTags.Primary <> invalid then
|
||||
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)
|
||||
end if
|
||||
|
@ -3,10 +3,10 @@ sub init()
|
||||
m.top.optionsAvailable = true
|
||||
end sub
|
||||
|
||||
function refresh()
|
||||
sub refresh()
|
||||
m.top.findNode("homeRows").callFunc("updateHomeRows")
|
||||
end function
|
||||
end sub
|
||||
|
||||
function loadLibraries()
|
||||
sub loadLibraries()
|
||||
m.top.findNode("homeRows").callFunc("loadLibraries")
|
||||
end function
|
||||
end sub
|
@ -33,13 +33,13 @@ sub itemContentChanged()
|
||||
end if
|
||||
|
||||
' Format the Data based on the type of Home Data
|
||||
if itemData.type = "CollectionFolder" OR itemData.type = "UserView" OR itemData.type = "Channel" then
|
||||
if itemData.type = "CollectionFolder" OR itemData.type = "UserView" OR itemData.type = "Channel"
|
||||
m.itemText.text = itemData.name
|
||||
m.itemPoster.uri = itemData.widePosterURL
|
||||
return
|
||||
end if
|
||||
|
||||
if itemData.type = "UserView" then
|
||||
if itemData.type = "UserView"
|
||||
m.itemPoster.width = "96"
|
||||
m.itemPoster.height = "96"
|
||||
m.itemPoster.translation = "[192, 88]"
|
||||
@ -57,10 +57,10 @@ sub itemContentChanged()
|
||||
m.itemTextExtra.font.size = 22
|
||||
|
||||
|
||||
if itemData.type = "Episode" then
|
||||
if itemData.type = "Episode"
|
||||
m.itemText.text = itemData.json.SeriesName
|
||||
|
||||
if itemData.usePoster = true then
|
||||
if itemData.usePoster = true
|
||||
m.itemPoster.uri = itemData.widePosterURL
|
||||
else
|
||||
m.itemPoster.uri = itemData.thumbnailURL
|
||||
@ -68,13 +68,13 @@ sub itemContentChanged()
|
||||
|
||||
' Set Series and Episode Number for Extra Text
|
||||
extraPrefix = ""
|
||||
if itemData.json.ParentIndexNumber <> invalid then
|
||||
if itemData.json.ParentIndexNumber <> invalid
|
||||
extraPrefix = "S" + StrI(itemData.json.ParentIndexNumber).trim()
|
||||
end if
|
||||
if itemData.json.IndexNumber <> invalid then
|
||||
if itemData.json.IndexNumber <> invalid
|
||||
extraPrefix = extraPrefix + "E" + StrI(itemData.json.IndexNumber).trim()
|
||||
end if
|
||||
if extraPrefix.len() > 0 then
|
||||
if extraPrefix.len() > 0
|
||||
extraPrefix = extraPrefix + " - "
|
||||
end if
|
||||
|
||||
@ -82,7 +82,7 @@ sub itemContentChanged()
|
||||
return
|
||||
end if
|
||||
|
||||
if itemData.type = "Movie" then
|
||||
if itemData.type = "Movie"
|
||||
m.itemText.text = itemData.name
|
||||
|
||||
' Use best image, but fallback to secondary if it's empty
|
||||
@ -94,11 +94,11 @@ sub itemContentChanged()
|
||||
|
||||
' Set Release Year and Age Rating for Extra Text
|
||||
textExtra = ""
|
||||
if itemData.json.ProductionYear <> invalid then
|
||||
if itemData.json.ProductionYear <> invalid
|
||||
textExtra = StrI(itemData.json.ProductionYear).trim()
|
||||
end if
|
||||
if itemData.json.OfficialRating <> invalid then
|
||||
if textExtra <> "" then
|
||||
if itemData.json.OfficialRating <> invalid
|
||||
if textExtra <> ""
|
||||
textExtra = textExtra + " - " + itemData.json.OfficialRating
|
||||
else
|
||||
textExtra = itemData.json.OfficialRating
|
||||
@ -109,7 +109,7 @@ sub itemContentChanged()
|
||||
return
|
||||
end if
|
||||
|
||||
if itemData.type = "Video" then
|
||||
if itemData.type = "Video"
|
||||
m.itemText.text = itemData.name
|
||||
|
||||
if itemData.imageWidth = 180
|
||||
@ -119,12 +119,12 @@ sub itemContentChanged()
|
||||
end if
|
||||
return
|
||||
end if
|
||||
if itemData.type = "Series" then
|
||||
if itemData.type = "Series"
|
||||
|
||||
m.itemText.text = itemData.name
|
||||
|
||||
if itemData.usePoster = true then
|
||||
if itemData.imageWidth = 180 then
|
||||
if itemData.usePoster = true
|
||||
if itemData.imageWidth = 180
|
||||
m.itemPoster.uri = itemData.posterURL
|
||||
else
|
||||
m.itemPoster.uri = itemData.widePosterURL
|
||||
@ -134,12 +134,12 @@ sub itemContentChanged()
|
||||
end if
|
||||
|
||||
textExtra = ""
|
||||
if itemData.json.ProductionYear <> invalid then
|
||||
if itemData.json.ProductionYear <> invalid
|
||||
textExtra = StrI(itemData.json.ProductionYear).trim()
|
||||
end if
|
||||
|
||||
' Set Years Run for Extra Text
|
||||
if itemData.json.Status = "Continuing" then
|
||||
if itemData.json.Status = "Continuing"
|
||||
textExtra = textExtra + " - Present"
|
||||
else if itemData.json.Status = "Ended" and itemData.json.EndDate <> invalid
|
||||
textExtra = textExtra + " - " + LEFT(itemData.json.EndDate, 4)
|
||||
@ -149,7 +149,7 @@ sub itemContentChanged()
|
||||
return
|
||||
end if
|
||||
|
||||
if itemData.type = "MusicAlbum" then
|
||||
if itemData.type = "MusicAlbum"
|
||||
m.itemText.text = itemData.name
|
||||
m.itemTextExtra.text = itemData.json.AlbumArtist
|
||||
m.itemPoster.uri = itemData.posterURL
|
||||
@ -164,7 +164,7 @@ end sub
|
||||
' Enable title scrolling based on item Focus
|
||||
sub focusChanged()
|
||||
|
||||
if m.top.itemHasFocus = true then
|
||||
if m.top.itemHasFocus = true
|
||||
m.itemText.repeatCount = -1
|
||||
else
|
||||
m.itemText.repeatCount = 0
|
||||
@ -174,7 +174,7 @@ end sub
|
||||
|
||||
'Hide backdrop and icon when poster loaded
|
||||
sub onPosterLoadStatusChanged()
|
||||
if m.itemPoster.loadStatus = "ready" and m.itemPoster.uri <> "" then
|
||||
if m.itemPoster.loadStatus = "ready" and m.itemPoster.uri <> ""
|
||||
m.backdrop.visible = false
|
||||
m.itemIcon.visible = false
|
||||
else
|
||||
|
@ -26,15 +26,12 @@ sub init()
|
||||
m.LoadNextUpTask.itemsToLoad = "nextUp"
|
||||
end sub
|
||||
|
||||
function loadLibraries()
|
||||
sub loadLibraries()
|
||||
m.LoadLibrariesTask.control = "RUN"
|
||||
end function
|
||||
end sub
|
||||
|
||||
sub updateSize()
|
||||
sideborder = 100
|
||||
m.top.translation = [111, 180]
|
||||
|
||||
itemWidth = 480
|
||||
itemHeight = 330
|
||||
|
||||
'Set width of Rows to cut off at edge of Safe Zone
|
||||
@ -68,7 +65,7 @@ sub onLibrariesLoaded()
|
||||
[464, 331] ' Next Up
|
||||
]
|
||||
' validate library data
|
||||
if (m.libraryData <> invalid and m.libraryData.count() > 0) then
|
||||
if m.libraryData <> invalid and m.libraryData.count() > 0
|
||||
userConfig = m.top.userConfig
|
||||
' populate My Media row
|
||||
filteredMedia = filterNodeArray(m.libraryData, "id", userConfig.MyMediaExcludes)
|
||||
@ -78,7 +75,7 @@ sub onLibrariesLoaded()
|
||||
' create a "Latest In" row for each library
|
||||
filteredLatest = filterNodeArray(m.libraryData, "id", userConfig.LatestItemsExcludes)
|
||||
for each lib in filteredLatest
|
||||
if lib.collectionType <> "boxsets" and lib.collectionType <> "livetv" then
|
||||
if lib.collectionType <> "boxsets" and lib.collectionType <> "livetv"
|
||||
latestInRow = content.CreateChild("HomeRow")
|
||||
latestInRow.title = tr("Latest in") + " " + lib.name + " >"
|
||||
sizeArray.Push([464, 331])
|
||||
@ -94,23 +91,23 @@ sub onLibrariesLoaded()
|
||||
m.LoadContinueTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
function updateHomeRows()
|
||||
sub updateHomeRows()
|
||||
m.LoadContinueTask.observeField("content", "updateContinueItems")
|
||||
m.LoadContinueTask.control = "RUN"
|
||||
end function
|
||||
end sub
|
||||
|
||||
function updateContinueItems()
|
||||
sub updateContinueItems()
|
||||
itemData = m.LoadContinueTask.content
|
||||
m.LoadContinueTask.unobserveField("content")
|
||||
m.LoadContinueTask.content = []
|
||||
|
||||
if itemData = invalid then return false
|
||||
if itemData = invalid then return
|
||||
|
||||
homeRows = m.top.content
|
||||
continueRowIndex = getRowIndex("Continue Watching")
|
||||
|
||||
if itemData.count() < 1 then
|
||||
if continueRowIndex <> invalid then
|
||||
if itemData.count() < 1
|
||||
if continueRowIndex <> invalid
|
||||
' remove the row
|
||||
deleteFromSizeArray(continueRowIndex)
|
||||
homeRows.removeChildIndex(continueRowIndex)
|
||||
@ -126,7 +123,7 @@ function updateContinueItems()
|
||||
row.appendChild(item)
|
||||
end for
|
||||
|
||||
if continueRowIndex = invalid then
|
||||
if continueRowIndex = invalid
|
||||
' insert new row under "My Media"
|
||||
updateSizeArray(itemSize, 1)
|
||||
homeRows.insertChild(row, 1)
|
||||
@ -138,20 +135,20 @@ function updateContinueItems()
|
||||
|
||||
m.LoadNextUpTask.observeField("content", "updateNextUpItems")
|
||||
m.LoadNextUpTask.control = "RUN"
|
||||
end function
|
||||
end sub
|
||||
|
||||
function updateNextUpItems()
|
||||
sub updateNextUpItems()
|
||||
itemData = m.LoadNextUpTask.content
|
||||
m.LoadNextUpTask.unobserveField("content")
|
||||
m.LoadNextUpTask.content = []
|
||||
|
||||
if itemData = invalid then return false
|
||||
if itemData = invalid then return
|
||||
|
||||
homeRows = m.top.content
|
||||
nextUpRowIndex = getRowIndex("Next Up >")
|
||||
|
||||
if itemData.count() < 1 then
|
||||
if nextUpRowIndex <> invalid then
|
||||
if itemData.count() < 1
|
||||
if nextUpRowIndex <> invalid
|
||||
' remove the row
|
||||
deleteFromSizeArray(nextUpRowIndex)
|
||||
homeRows.removeChildIndex(nextUpRowIndex)
|
||||
@ -167,10 +164,10 @@ function updateNextUpItems()
|
||||
row.appendChild(item)
|
||||
end for
|
||||
|
||||
if nextUpRowIndex = invalid then
|
||||
if nextUpRowIndex = invalid
|
||||
' insert new row under "Continue Watching"
|
||||
continueRowIndex = getRowIndex("Continue Watching")
|
||||
if continueRowIndex <> invalid then
|
||||
if continueRowIndex <> invalid
|
||||
updateSizeArray(itemSize, continueRowIndex + 1)
|
||||
homeRows.insertChild(row, continueRowIndex + 1)
|
||||
else
|
||||
@ -185,7 +182,7 @@ function updateNextUpItems()
|
||||
end if
|
||||
|
||||
' consider home screen loaded when above rows are loaded
|
||||
if m.global.app_loaded = false then
|
||||
if m.global.app_loaded = false
|
||||
m.top.signalBeacon("AppLaunchComplete") ' Roku Performance monitoring
|
||||
m.global.app_loaded = true
|
||||
end if
|
||||
@ -195,7 +192,7 @@ function updateNextUpItems()
|
||||
userConfig = m.top.userConfig
|
||||
filteredLatest = filterNodeArray(m.libraryData, "id", userConfig.LatestItemsExcludes)
|
||||
for each lib in filteredLatest
|
||||
if lib.collectionType <> "livetv" and lib.collectionType <> "boxsets" then
|
||||
if lib.collectionType <> "livetv" and lib.collectionType <> "boxsets"
|
||||
loadLatest = createObject("roSGNode", "LoadItemsTask")
|
||||
loadLatest.itemsToLoad = "latest"
|
||||
loadLatest.itemId = lib.id
|
||||
@ -208,24 +205,23 @@ function updateNextUpItems()
|
||||
loadLatest.control = "RUN"
|
||||
end if
|
||||
end for
|
||||
end function
|
||||
end sub
|
||||
|
||||
function updateLatestItems(msg)
|
||||
sub updateLatestItems(msg)
|
||||
itemData = msg.GetData()
|
||||
|
||||
data = msg.getField()
|
||||
node = msg.getRoSGNode()
|
||||
node.unobserveField("content")
|
||||
node.content = []
|
||||
|
||||
if itemData = invalid then return false
|
||||
if itemData = invalid then return
|
||||
|
||||
homeRows = m.top.content
|
||||
rowIndex = getRowIndex(tr("Latest in") + " " + node.metadata.title + " >")
|
||||
|
||||
if itemData.count() < 1 then
|
||||
if itemData.count() < 1
|
||||
' remove row
|
||||
if rowIndex <> invalid then
|
||||
if rowIndex <> invalid
|
||||
deleteFromSizeArray(rowIndex)
|
||||
homeRows.removeChildIndex(rowIndex)
|
||||
end if
|
||||
@ -235,10 +231,10 @@ function updateLatestItems(msg)
|
||||
row.title = tr("Latest in") + " " + node.metadata.title + " >"
|
||||
row.usePoster = true
|
||||
' Handle specific types with different item widths
|
||||
if node.metadata.contentType = "movies" then
|
||||
if node.metadata.contentType = "movies"
|
||||
row.imageWidth = 180
|
||||
itemSize = [188, 331]
|
||||
else if node.metadata.contentType = "music" then
|
||||
else if node.metadata.contentType = "music"
|
||||
row.imageWidth = 261
|
||||
itemSize = [261, 331]
|
||||
else
|
||||
@ -252,7 +248,7 @@ function updateLatestItems(msg)
|
||||
row.appendChild(item)
|
||||
end for
|
||||
|
||||
if rowIndex = invalid then
|
||||
if rowIndex = invalid
|
||||
' append new row
|
||||
updateSizeArray(itemSize)
|
||||
homeRows.appendChild(row)
|
||||
@ -262,14 +258,14 @@ function updateLatestItems(msg)
|
||||
homeRows.replaceChild(row, rowIndex)
|
||||
end if
|
||||
end if
|
||||
end function
|
||||
end sub
|
||||
|
||||
function getRowIndex(rowTitle as string)
|
||||
rowIndex = invalid
|
||||
for i = 1 to m.top.content.getChildCount() - 1
|
||||
' skip row 0 since it's always "My Media"
|
||||
tmpRow = m.top.content.getChild(i)
|
||||
if tmpRow.title = rowTitle then
|
||||
if tmpRow.title = rowTitle
|
||||
rowIndex = i
|
||||
exit for
|
||||
end if
|
||||
@ -280,22 +276,22 @@ end function
|
||||
sub updateSizeArray(rowItemSize, rowIndex = invalid, action = "insert")
|
||||
sizeArray = m.top.rowItemSize
|
||||
' append by default
|
||||
if rowIndex = invalid then
|
||||
if rowIndex = invalid
|
||||
rowIndex = sizeArray.count()
|
||||
end if
|
||||
|
||||
newSizeArray = []
|
||||
for i = 0 to sizeArray.count()
|
||||
if rowIndex = i then
|
||||
if action = "replace" then
|
||||
if rowIndex = i
|
||||
if action = "replace"
|
||||
newSizeArray.Push(rowItemSize)
|
||||
else if action = "insert" then
|
||||
else if action = "insert"
|
||||
newSizeArray.Push(rowItemSize)
|
||||
if sizeArray[i] <> invalid then
|
||||
if sizeArray[i] <> invalid
|
||||
newSizeArray.Push(sizeArray[i])
|
||||
end if
|
||||
end if
|
||||
else if sizeArray[i] <> invalid then
|
||||
else if sizeArray[i] <> invalid
|
||||
newSizeArray.Push(sizeArray[i])
|
||||
end if
|
||||
end for
|
||||
@ -306,16 +302,16 @@ sub deleteFromSizeArray(rowIndex)
|
||||
updateSizeArray([0, 0], rowIndex, "delete")
|
||||
end sub
|
||||
|
||||
function itemSelected()
|
||||
sub itemSelected()
|
||||
m.top.selectedItem = m.top.content.getChild(m.top.rowItemSelected[0]).getChild(m.top.rowItemSelected[1])
|
||||
end function
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
handled = false
|
||||
if press then
|
||||
if key = "play" then
|
||||
if press
|
||||
if key = "play"
|
||||
itemToPlay = m.top.content.getChild(m.top.rowItemFocused[0]).getChild(m.top.rowItemFocused[1])
|
||||
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode") then
|
||||
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode")
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
end if
|
||||
handled = true
|
||||
@ -331,11 +327,11 @@ function filterNodeArray(nodeArray as object, nodeKey as string, excludeArray as
|
||||
for each node in nodeArray
|
||||
excludeThisNode = false
|
||||
for each exclude in excludeArray
|
||||
if node[nodeKey] = exclude then
|
||||
if node[nodeKey] = exclude
|
||||
excludeThisNode = true
|
||||
end if
|
||||
end for
|
||||
if excludeThisNode = false then
|
||||
if excludeThisNode = false
|
||||
newNodeArray.Push(node)
|
||||
end if
|
||||
end for
|
||||
|
@ -8,8 +8,8 @@ sub init()
|
||||
|
||||
params = {
|
||||
UserId: get_setting("active_user")
|
||||
limit: m.top.limit,
|
||||
StartIndex: m.top.startIndex
|
||||
'limit: m.top.limit,
|
||||
'StartIndex: m.top.startIndex
|
||||
}
|
||||
|
||||
url = "LiveTv/Channels"
|
||||
@ -17,7 +17,7 @@ sub init()
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
|
||||
if data.TotalRecordCount = invalid then
|
||||
if data.TotalRecordCount = invalid
|
||||
m.top.channels = results
|
||||
return
|
||||
end if
|
||||
@ -31,4 +31,4 @@ sub init()
|
||||
|
||||
m.top.channels = results
|
||||
|
||||
end sub
|
||||
end sub
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<component name="LoadChannelsTask" extends="Task">
|
||||
<interface>
|
||||
<field id="limit" type="integer" value="500" />
|
||||
<field id="limit" type="integer" value="" />
|
||||
<field id="startIndex" type="integer" value="0" />
|
||||
|
||||
<!-- Total records available from server-->
|
||||
@ -12,4 +12,4 @@
|
||||
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
||||
<!-- <script type="text/brightscript" uri="pkg:/source/api/Image.brs" /> -->
|
||||
</component>
|
||||
</component>
|
||||
|
@ -17,15 +17,15 @@ sub loadProgramDetails()
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
|
||||
if data = invalid then
|
||||
if data = invalid
|
||||
m.top.programDetails = {}
|
||||
return
|
||||
end if
|
||||
|
||||
program = createObject("roSGNode", "ScheduleProgramData")
|
||||
program.json = data
|
||||
program.channelIndex = ChannelIndex
|
||||
program.programIndex = ProgramIndex
|
||||
program.channelIndex = channelIndex
|
||||
program.programIndex = programIndex
|
||||
program.fullyLoaded = true
|
||||
m.top.programDetails = program
|
||||
|
||||
|
@ -22,7 +22,7 @@ sub init()
|
||||
resp = APIRequest(url)
|
||||
data = postJson(resp, FormatJson(params))
|
||||
|
||||
if data = invalid then
|
||||
if data = invalid
|
||||
m.top.schedule = results
|
||||
return
|
||||
end if
|
||||
|
@ -60,7 +60,7 @@ sub channelUpdated()
|
||||
else
|
||||
m.top.findNode("noInfoChannelName").text = m.top.channel.Title
|
||||
m.channelName.text= m.top.channel.Title
|
||||
if m.top.programDetails = invalid then
|
||||
if m.top.programDetails = invalid
|
||||
m.image.uri = m.top.channel.posterURL
|
||||
end if
|
||||
end if
|
||||
@ -73,7 +73,7 @@ sub programUpdated()
|
||||
prog = m.top.programDetails
|
||||
|
||||
' If no program selected, hide details view
|
||||
if prog = invalid then
|
||||
if prog = invalid
|
||||
channelUpdated()
|
||||
m.detailsView.visible = "false"
|
||||
m.noInfoView.visible = "true"
|
||||
@ -85,20 +85,20 @@ sub programUpdated()
|
||||
|
||||
m.episodeDetailsGroup.removeChildrenIndex(m.episodeDetailsGroup.getChildCount(), 0)
|
||||
|
||||
if prog.isLive then
|
||||
if prog.isLive
|
||||
m.episodeDetailsGroup.appendChild(m.isLiveGroup)
|
||||
else if prog.isRepeat then
|
||||
else if prog.isRepeat
|
||||
m.episodeDetailsGroup.appendChild(m.isRepeatGroup)
|
||||
end if
|
||||
|
||||
' Episode Number
|
||||
if prog.seasonNumber > 0 and prog.episodeNumber > 0 then
|
||||
if prog.seasonNumber > 0 and prog.episodeNumber > 0
|
||||
m.episodeNumber.text = "S" + StrI(prog.seasonNumber).trim() + ":E" + StrI(prog.episodeNumber).trim()
|
||||
if prog.episodeTitle <> "" then m.episodeNumber.text = m.episodeNumber.text + " -" ' Add a Dash if showing Episode Number and Title
|
||||
m.episodeDetailsGroup.appendChild(m.episodeNumber)
|
||||
end if
|
||||
|
||||
if prog.episodeTitle <> invalid and prog.episodeTitle <> "" then
|
||||
if prog.episodeTitle <> invalid and prog.episodeTitle <> ""
|
||||
m.episodeTitle.text = prog.episodeTitle
|
||||
m.episodeTitle.visible = true
|
||||
m.episodeDetailsGroup.appendChild(m.episodeTitle)
|
||||
@ -115,23 +115,28 @@ sub programUpdated()
|
||||
|
||||
day = getRelativeDayName(startDate)
|
||||
|
||||
if startDate.AsSeconds() < now.AsSeconds() and endDate.AsSeconds() > now.AsSeconds() then
|
||||
if day = "today" then
|
||||
m.broadcastDetails.text = tr("Started at") + " " + formatTime(startDate)
|
||||
' Get Start Date in local timezone for display to user
|
||||
localStartDate = createObject("roDateTime")
|
||||
localStartDate.FromISO8601String(prog.StartDate)
|
||||
localStartDate.ToLocalTime()
|
||||
|
||||
if startDate.AsSeconds() < now.AsSeconds() and endDate.AsSeconds() > now.AsSeconds()
|
||||
if day = "today"
|
||||
m.broadcastDetails.text = tr("Started at") + " " + formatTime(localStartDate)
|
||||
else
|
||||
m.broadcastDetails.text = tr("Started") + " " + tr(day) + ", " + formatTime(startDate)
|
||||
m.broadcastDetails.text = tr("Started") + " " + tr(day) + ", " + formatTime(localStartDate)
|
||||
end if
|
||||
else if startDate.AsSeconds() > now.AsSeconds()
|
||||
if day = "today" then
|
||||
m.broadcastDetails.text = tr("Starts at") + " " + formatTime(startDate)
|
||||
if day = "today"
|
||||
m.broadcastDetails.text = tr("Starts at") + " " + formatTime(localStartDate)
|
||||
else
|
||||
m.broadcastDetails.text = tr("Starts") + " " + tr(day) + ", " + formatTime(startDate)
|
||||
m.broadcastDetails.text = tr("Starts") + " " + tr(day) + ", " + formatTime(localStartDate)
|
||||
end if
|
||||
else
|
||||
if day = "today" then
|
||||
m.broadcastDetails.text = tr("Ended at") + " " + formatTime(endDate)
|
||||
if day = "today"
|
||||
m.broadcastDetails.text = tr("Ended at") + " " + formatTime(localStartDate)
|
||||
else
|
||||
m.broadcastDetails.text = tr("Ended") + " " + tr(day) + ", " + formatTime(endDate)
|
||||
m.broadcastDetails.text = tr("Ended") + " " + tr(day) + ", " + formatTime(localStartDate)
|
||||
end if
|
||||
end if
|
||||
|
||||
@ -152,7 +157,7 @@ function getRelativeDayName(date) as string
|
||||
now = createObject("roDateTime")
|
||||
|
||||
' Check for Today
|
||||
if now.AsDateString("short-date-dashes") = date.AsDateString("short-date-dashes") then
|
||||
if now.AsDateString("short-date-dashes") = date.AsDateString("short-date-dashes")
|
||||
return "today"
|
||||
end if
|
||||
|
||||
@ -160,11 +165,11 @@ function getRelativeDayName(date) as string
|
||||
todayMidnight = now.AsSeconds() - (now.AsSeconds() MOD 86400)
|
||||
dateMidnight = date.AsSeconds() - (date.AsSeconds() MOD 86400)
|
||||
|
||||
if todayMidnight - dateMidnight = 86400 then
|
||||
if todayMidnight - dateMidnight = 86400
|
||||
return "yesterday"
|
||||
end if
|
||||
|
||||
if dateMidnight - todayMidnight = 86400 then
|
||||
if dateMidnight - todayMidnight = 86400
|
||||
return "tomorrow"
|
||||
end if
|
||||
|
||||
@ -179,12 +184,12 @@ function getDurationStringFromSeconds(seconds) as string
|
||||
hours = 0
|
||||
minutes = seconds / 60.0
|
||||
|
||||
if minutes > 60 then
|
||||
if minutes > 60
|
||||
hours = (minutes - (minutes MOD 60)) / 60
|
||||
minutes = minutes MOD 60
|
||||
end if
|
||||
|
||||
if hours > 0 then
|
||||
if hours > 0
|
||||
return "%1h %2m".Replace("%1", StrI(hours).trim()).Replace("%2", StrI(minutes).trim())
|
||||
else
|
||||
return "%1m".Replace("%1", StrI(minutes).trim())
|
||||
@ -195,7 +200,7 @@ end function
|
||||
'
|
||||
' Show view channel button when item has Focus
|
||||
sub focusChanged()
|
||||
if m.top.hasFocus = true then
|
||||
if m.top.hasFocus = true
|
||||
m.overview.maxLines = m.maxDetailLines
|
||||
m.focusAnimationOpacity.keyValue = [0, 1]
|
||||
else
|
||||
@ -208,7 +213,7 @@ sub focusChanged()
|
||||
end sub
|
||||
|
||||
sub onAnimationComplete()
|
||||
if m.focusAnimation.state = "stopped" and m.top.hasFocus = false then
|
||||
if m.focusAnimation.state = "stopped" and m.top.hasFocus = false
|
||||
m.overview.maxLines = m.maxPreviewLines
|
||||
end if
|
||||
end sub
|
||||
@ -216,12 +221,12 @@ end sub
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "OK" then
|
||||
if key = "OK"
|
||||
m.top.watchSelectedChannel = true
|
||||
return true
|
||||
end if
|
||||
|
||||
if key = "left" or key = "right" or key = "up" or key = "down" then
|
||||
if key = "left" or key = "right" or key = "up" or key = "down"
|
||||
return true
|
||||
end if
|
||||
|
||||
|
@ -66,10 +66,10 @@ sub onScheduleLoaded()
|
||||
|
||||
channel = m.scheduleGrid.content.GetChild(m.channelIndex[item.ChannelId])
|
||||
|
||||
if channel.PosterUrl <> "" then
|
||||
if channel.PosterUrl <> ""
|
||||
item.channelLogoUri = channel.PosterUrl
|
||||
end if
|
||||
if channel.Title <> "" then
|
||||
if channel.Title <> ""
|
||||
item.channelName = channel.Title
|
||||
end if
|
||||
|
||||
@ -88,14 +88,14 @@ sub onProgramFocused()
|
||||
m.detailsPane.channel = channel
|
||||
|
||||
' Exit if Channels not yet loaded
|
||||
if channel.getChildCount() = 0 then
|
||||
if channel.getChildCount() = 0
|
||||
m.detailsPane.programDetails = invalid
|
||||
return
|
||||
end if
|
||||
|
||||
prog = channel.GetChild(m.scheduleGrid.programFocusedDetails.focusIndex)
|
||||
|
||||
if prog <> invalid and prog.fullyLoaded = false then
|
||||
if prog <> invalid and prog.fullyLoaded = false
|
||||
m.LoadProgramDetailsTask.programId = prog.Id
|
||||
m.LoadProgramDetailsTask.channelIndex = m.scheduleGrid.programFocusedDetails.focusChannelIndex
|
||||
m.LoadProgramDetailsTask.programIndex = m.scheduleGrid.programFocusedDetails.focusIndex
|
||||
@ -111,7 +111,7 @@ sub onProgramDetailsLoaded()
|
||||
channel = m.scheduleGrid.content.GetChild(m.LoadProgramDetailsTask.programDetails.channelIndex)
|
||||
|
||||
' If TV Show does not have its own image, use the channel logo
|
||||
if m.LoadProgramDetailsTask.programDetails.PosterUrl = invalid or m.LoadProgramDetailsTask.programDetails.PosterUrl = "" then
|
||||
if m.LoadProgramDetailsTask.programDetails.PosterUrl = invalid or m.LoadProgramDetailsTask.programDetails.PosterUrl = ""
|
||||
m.LoadProgramDetailsTask.programDetails.PosterUrl = channel.PosterUrl
|
||||
end if
|
||||
|
||||
@ -122,7 +122,7 @@ end sub
|
||||
|
||||
sub onProgramSelected()
|
||||
' If there is no program data - view the channel
|
||||
if m.detailsPane.programDetails = invalid then
|
||||
if m.detailsPane.programDetails = invalid
|
||||
m.top.watchChannel = m.scheduleGrid.content.GetChild(m.scheduleGrid.programFocusedDetails.focusChannelIndex)
|
||||
return
|
||||
end if
|
||||
@ -138,7 +138,7 @@ sub focusProgramDetails(setFocused)
|
||||
if h < 400 then h = 400
|
||||
h = h + 160 + 80
|
||||
|
||||
if setFocused = true then
|
||||
if setFocused = true
|
||||
m.gridMoveAnimationPosition.keyValue = [ [0,600], [0, h] ]
|
||||
m.detailsPane.setFocus(true)
|
||||
m.detailsPane.hasFocus = true
|
||||
@ -168,10 +168,10 @@ end sub
|
||||
sub onGridScrolled()
|
||||
|
||||
' If we're within 12 hours of end of grid, load next 24hrs of data
|
||||
if m.scheduleGrid.leftEdgeTargetTime + (12 * 60 * 60) > m.gridEndDate.AsSeconds() then
|
||||
if m.scheduleGrid.leftEdgeTargetTime + (12 * 60 * 60) > m.gridEndDate.AsSeconds()
|
||||
|
||||
' Ensure the task is not already (still) running,
|
||||
if m.LoadScheduleTask.state <> "run" then
|
||||
if m.LoadScheduleTask.state <> "run"
|
||||
m.LoadScheduleTask.startTime = m.gridEndDate.ToISOString()
|
||||
m.gridEndDate.FromSeconds(m.gridEndDate.AsSeconds() + (24 * 60 * 60))
|
||||
m.LoadScheduleTask.endTime = m.gridEndDate.ToISOString()
|
||||
@ -183,7 +183,7 @@ end sub
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "back" and m.detailsPane.isInFocusChain() then
|
||||
if key = "back" and m.detailsPane.isInFocusChain()
|
||||
focusProgramDetails(false)
|
||||
return true
|
||||
end if
|
||||
|
@ -8,7 +8,7 @@ sub itemContentChanged()
|
||||
profileImage = m.top.findNode("profileImage")
|
||||
profileName = m.top.findNode("profileName")
|
||||
|
||||
if itemData.imageURL = "" then
|
||||
if itemData.imageURL = ""
|
||||
profileImage.uri = "pkg://images/baseline_person_white_48dp.png"
|
||||
else
|
||||
profileImage.uri = itemData.imageURL
|
||||
|
@ -8,12 +8,6 @@ sub init()
|
||||
end sub
|
||||
|
||||
sub updateSize()
|
||||
dimensions = m.top.getScene().currentDesignResolution
|
||||
|
||||
border = 200
|
||||
'm.top.translation = [border, border + 115]
|
||||
|
||||
textHeight = 80
|
||||
itemWidth = 300
|
||||
itemHeight = 364
|
||||
|
||||
@ -32,7 +26,7 @@ end sub
|
||||
|
||||
|
||||
function setData()
|
||||
if m.top.itemContent = invalid then
|
||||
if m.top.itemContent = invalid
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
return data
|
||||
end if
|
||||
|
@ -14,7 +14,7 @@ sub redraw()
|
||||
itemWidth = 300
|
||||
itemSpacing = 40
|
||||
|
||||
if userCount < 5 then
|
||||
if userCount < 5
|
||||
leftBorder = (1920 - ((userCount * itemWidth) + ((userCount - 1) * itemSpacing))) / 2
|
||||
end if
|
||||
' break()
|
||||
@ -24,15 +24,15 @@ end sub
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "back" then
|
||||
if key = "back"
|
||||
m.top.backPressed = true
|
||||
else if key = "up" then
|
||||
if m.top.focusedChild.isSubType("LabelList") then
|
||||
else if key = "up"
|
||||
if m.top.focusedChild.isSubType("LabelList")
|
||||
m.top.findNode("UserRow").setFocus(true)
|
||||
return true
|
||||
end if
|
||||
else if key = "down" then
|
||||
if m.top.focusedChild.isSubType("UserRow") then
|
||||
else if key = "down"
|
||||
if m.top.focusedChild.isSubType("UserRow")
|
||||
m.top.findNode("alternateOptions").setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
@ -1,30 +1,30 @@
|
||||
function init()
|
||||
sub init()
|
||||
m.title = m.top.findNode("title")
|
||||
m.description = m.top.findNode("description")
|
||||
m.selectedIcon = m.top.findNode("selectedIcon")
|
||||
end function
|
||||
end sub
|
||||
|
||||
function itemContentChanged()
|
||||
sub itemContentChanged()
|
||||
m.title.text = m.top.itemContent.title
|
||||
m.description.text = m.top.itemContent.description
|
||||
|
||||
if m.top.itemContent.description = "" then
|
||||
if m.top.itemContent.description = ""
|
||||
m.title.translation = [50, 20]
|
||||
end if
|
||||
|
||||
if m.top.itemContent.selected then
|
||||
if m.top.itemContent.selected
|
||||
m.selectedIcon.uri = m.global.constants.icons.check_white
|
||||
else
|
||||
m.selectedIcon.uri = ""
|
||||
end if
|
||||
|
||||
end function
|
||||
end sub
|
||||
|
||||
'
|
||||
'Scroll description if focused
|
||||
sub focusChanged()
|
||||
|
||||
if m.top.itemHasFocus = true then
|
||||
if m.top.itemHasFocus = true
|
||||
m.description.repeatCount = -1
|
||||
else
|
||||
m.description.repeatCount = 0
|
||||
|
@ -16,12 +16,11 @@ sub itemContentChanged()
|
||||
item = m.top.itemContent
|
||||
itemData = item.json
|
||||
m.top.id = itemData.id
|
||||
|
||||
m.top.findNode("moviePoster").uri = m.top.itemContent.posterURL
|
||||
|
||||
' Find first Audio Stream and set that as default
|
||||
For i=0 To itemData.mediaStreams.Count() - 1
|
||||
if itemData.mediaStreams[i].Type = "Audio" then
|
||||
if itemData.mediaStreams[i].Type = "Audio"
|
||||
m.top.selectedAudioStreamIndex = i
|
||||
exit for
|
||||
end if
|
||||
@ -33,16 +32,16 @@ sub itemContentChanged()
|
||||
setFieldText("officialRating", itemData.officialRating)
|
||||
setFieldText("overview", itemData.overview)
|
||||
|
||||
if itemData.communityRating <> invalid then
|
||||
if itemData.communityRating <> invalid
|
||||
setFieldText("communityRating", itemData.communityRating)
|
||||
else
|
||||
' hide the star icon
|
||||
m.top.findNode("communityRatingGroup").visible = false
|
||||
end if
|
||||
|
||||
if itemData.CriticRating <> invalid then
|
||||
if itemData.CriticRating <> invalid
|
||||
setFieldText("criticRatingLabel" , itemData.criticRating)
|
||||
if itemData.CriticRating > 60 then
|
||||
if itemData.CriticRating > 60
|
||||
tomato = "pkg:/images/fresh.png"
|
||||
else
|
||||
tomato = "pkg:/images/rotten.png"
|
||||
@ -60,18 +59,28 @@ sub itemContentChanged()
|
||||
if itemData.genres.count() > 0
|
||||
setFieldText("genres", tr("Genres") + ": " + itemData.genres.join(", "))
|
||||
end if
|
||||
director = invalid
|
||||
|
||||
' show tags if there are no genres to display
|
||||
if itemData.genres.count() = 0 and itemData.tags.count() > 0
|
||||
setFieldText("genres", tr("Tags") + ": " + itemData.tags.join(", "))
|
||||
end if
|
||||
|
||||
directors = []
|
||||
for each person in itemData.people
|
||||
if person.type = "Director"
|
||||
director = person.name
|
||||
exit for
|
||||
directors.push(person.name)
|
||||
end if
|
||||
end for
|
||||
if director <> invalid
|
||||
setFieldText("director", tr("Director") + ": " + director)
|
||||
if directors.count() > 0
|
||||
setFieldText("director", tr("Director") + ": " + directors.join(", "))
|
||||
end if
|
||||
|
||||
if itemData.mediaStreams[0] <> invalid
|
||||
setFieldText("video_codec", tr("Video") + ": " + itemData.mediaStreams[0].displayTitle)
|
||||
end if
|
||||
if itemData.mediaStreams[m.top.selectedAudioStreamIndex] <> invalid
|
||||
setFieldText("audio_codec", tr("Audio") + ": " + itemData.mediaStreams[m.top.selectedAudioStreamIndex].displayTitle)
|
||||
end if
|
||||
setFieldText("video_codec", tr("Video") + ": " + itemData.mediaStreams[0].displayTitle)
|
||||
setFieldText("audio_codec", tr("Audio") + ": " + itemData.mediaStreams[m.top.selectedAudioStreamIndex].displayTitle)
|
||||
' TODO - cmon now. these are buttons, not words
|
||||
if itemData.taglines.count() > 0
|
||||
setFieldText("tagline", itemData.taglines[0])
|
||||
@ -87,7 +96,7 @@ sub SetUpOptions(streams)
|
||||
tracks = []
|
||||
|
||||
for i=0 To streams.Count() - 1
|
||||
if streams[i].Type = "Audio" then
|
||||
if streams[i].Type = "Audio"
|
||||
tracks.push({"Title": streams[i].displayTitle, "Description" : streams[i].Title, "Selected" : m.top.selectedAudioStreamIndex = i, "StreamIndex" : i})
|
||||
end if
|
||||
end for
|
||||
@ -104,11 +113,11 @@ sub setFieldText(field, value)
|
||||
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" then
|
||||
if type(value) = "roInt" or type(value) = "Integer"
|
||||
value = str(value)
|
||||
else if type(value) = "roFloat" or type(value) = "Float" then
|
||||
else if type(value) = "roFloat" or type(value) = "Float"
|
||||
value = str(value)
|
||||
else if type(value) <> "roString" and type(value) <> "String" then
|
||||
else if type(value) <> "roString" and type(value) <> "String"
|
||||
value = ""
|
||||
end if
|
||||
|
||||
@ -175,7 +184,7 @@ end function
|
||||
'
|
||||
'Check if options updated and any reloading required
|
||||
sub optionsClosed()
|
||||
if m.options.audioSteamIndex <> m.top.selectedAudioStreamIndex then
|
||||
if m.options.audioSteamIndex <> m.top.selectedAudioStreamIndex
|
||||
m.top.selectedAudioStreamIndex = m.options.audioSteamIndex
|
||||
setFieldText("audio_codec", tr("Audio") + ": " + m.top.itemContent.json.mediaStreams[m.top.selectedAudioStreamIndex].displayTitle)
|
||||
end if
|
||||
@ -187,7 +196,7 @@ 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
|
||||
' 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("audio-button").isInFocusChain()
|
||||
m.options.visible = true
|
||||
m.options.setFocus(true)
|
||||
end if
|
||||
@ -195,7 +204,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "options"
|
||||
if m.options.visible = true then
|
||||
if m.options.visible = true
|
||||
m.options.visible = false
|
||||
optionsClosed()
|
||||
else
|
||||
@ -203,8 +212,8 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
m.options.setFocus(true)
|
||||
end if
|
||||
return true
|
||||
else if key = "back" then
|
||||
if m.options.visible = true then
|
||||
else if key = "back"
|
||||
if m.options.visible = true
|
||||
m.options.visible = false
|
||||
optionsClosed()
|
||||
return true
|
||||
|
@ -27,7 +27,7 @@ end sub
|
||||
sub optionsSet()
|
||||
|
||||
' Views Tab
|
||||
if m.top.options.views <> invalid then
|
||||
if m.top.options.views <> invalid
|
||||
viewContent = CreateObject("roSGNode", "ContentNode")
|
||||
index = 0
|
||||
selectedViewIndex = 0
|
||||
@ -38,7 +38,7 @@ sub optionsSet()
|
||||
entry.description = view.Description
|
||||
entry.streamIndex = view.StreamIndex
|
||||
m.viewNames.push(view.Name)
|
||||
if view.Selected <> invalid and view.Selected = true then
|
||||
if view.Selected <> invalid and view.Selected = true
|
||||
selectedViewIndex = index
|
||||
entry.selected = true
|
||||
m.top.audioSteamIndex = view.streamIndex
|
||||
@ -65,13 +65,13 @@ end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
|
||||
if key = "down" or (key = "OK" and m.top.findNode("buttons").hasFocus()) then
|
||||
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" then
|
||||
if key = "down"
|
||||
m.menus[m.selectedItem].jumpToItem = 0
|
||||
else
|
||||
m.menus[m.selectedItem].jumpToItem = m.menus[m.selectedItem].itemSelected
|
||||
@ -79,13 +79,12 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
|
||||
return true
|
||||
else if key = "OK"
|
||||
if(m.menus[m.selectedItem].isInFocusChain()) then
|
||||
if m.menus[m.selectedItem].isInFocusChain()
|
||||
|
||||
selMenu = m.menus[m.selectedItem]
|
||||
selIndex = selMenu.itemSelected
|
||||
child = selMenu.content.GetChild(selIndex)
|
||||
|
||||
if m.selectedAudioIndex = selIndex then
|
||||
if m.selectedAudioIndex = selIndex
|
||||
else
|
||||
selMenu.content.GetChild(m.selectedAudioIndex).selected = false
|
||||
newSelection = selMenu.content.GetChild(selIndex)
|
||||
@ -96,7 +95,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
end if
|
||||
return true
|
||||
else if key = "back" or key = "up"
|
||||
if m.menus[m.selectedItem].isInFocusChain() then
|
||||
if m.menus[m.selectedItem].isInFocusChain()
|
||||
m.buttons.setFocus(true)
|
||||
m.menus[m.selectedItem].drawFocusFeedback = false
|
||||
return true
|
||||
|
@ -1,4 +1,4 @@
|
||||
function init()
|
||||
sub init()
|
||||
' backgroundUri must be set to an empty string before backgroundColor can be set
|
||||
m.top.backgroundUri = ""
|
||||
m.top.backgroundColor = "#000000"
|
||||
@ -8,4 +8,4 @@ function init()
|
||||
|
||||
m.BounceAnimation = m.top.findNode("BounceAnimation")
|
||||
m.BounceAnimation.control = "start" 'Start BounceAnimation
|
||||
end function
|
||||
end sub
|
@ -35,19 +35,19 @@ sub updateSize()
|
||||
end sub
|
||||
|
||||
function getData()
|
||||
if m.top.itemData = invalid then
|
||||
if m.top.itemData = invalid
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
return data
|
||||
end if
|
||||
|
||||
itemData = m.top.itemData
|
||||
rowSize = m.top.rowSize
|
||||
|
||||
' todo - Or get the old data? I can't remember...
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
' Do this to keep the ordering, AssociateArrays have no order
|
||||
type_array = ["Movie", "Series", "Episode", "AlbumArtist", "Album", "Audio", "Person"]
|
||||
type_array = ["Movie", "Series", "TvChannel", "Episode", "AlbumArtist", "Album", "Audio", "Person"]
|
||||
content_types = {
|
||||
"TvChannel": {"label": "Channels", "count": 0},
|
||||
"Movie": {"label": "Movies", "count": 0},
|
||||
"Series": {"label": "Shows", "count": 0},
|
||||
"Episode": {"label": "Episodes", "count": 0},
|
||||
@ -74,7 +74,7 @@ function getData()
|
||||
return data
|
||||
end function
|
||||
|
||||
function addRow(data, title, type_filter)
|
||||
sub addRow(data, title, type_filter)
|
||||
itemData = m.top.itemData
|
||||
row = data.CreateChild("ContentNode")
|
||||
row.title = title
|
||||
@ -83,4 +83,4 @@ function addRow(data, title, type_filter)
|
||||
row.appendChild(item)
|
||||
end if
|
||||
end for
|
||||
end function
|
||||
end sub
|
||||
|
@ -17,7 +17,6 @@ sub updateSize()
|
||||
border = 96
|
||||
m.top.translation = [border, 75 + 115]
|
||||
|
||||
textHeight = 80
|
||||
itemWidth = (dimensions["width"] - border*2)
|
||||
itemHeight = 300
|
||||
|
||||
@ -34,16 +33,16 @@ sub updateSize()
|
||||
m.top.rowItemSpacing = [ 20, 0 ]
|
||||
end sub
|
||||
|
||||
function setupRows()
|
||||
sub setupRows()
|
||||
updateSize()
|
||||
objects = m.top.objects
|
||||
m.top.numRows = objects.items.count()
|
||||
m.top.content = setData()
|
||||
end function
|
||||
end sub
|
||||
|
||||
function setData()
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
if m.top.objects = invalid then
|
||||
if m.top.objects = invalid
|
||||
' Return an empty node just to return something; we'll update once we have data
|
||||
return data
|
||||
end if
|
||||
|
@ -8,10 +8,10 @@ end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
handled = false
|
||||
if press then
|
||||
if key = "play" then
|
||||
if press
|
||||
if key = "play"
|
||||
itemToPlay = m.top.focusedChild.content.getChild(m.top.focusedChild.rowItemFocused[0]).getChild(0)
|
||||
if itemToPlay <> invalid and itemToPlay.id <> "" then
|
||||
if itemToPlay <> invalid and itemToPlay.id <> ""
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
end if
|
||||
handled = true
|
||||
|
@ -3,10 +3,10 @@ sub init()
|
||||
m.title.text = tr("Loading...")
|
||||
end sub
|
||||
|
||||
function itemContentChanged() as void
|
||||
sub itemContentChanged()
|
||||
item = m.top.itemContent
|
||||
itemData = item.json
|
||||
if itemData.indexNumber <> invalid then
|
||||
if itemData.indexNumber <> invalid
|
||||
indexNumber = itemData.indexNumber.toStr() + ". "
|
||||
else
|
||||
indexNumber = ""
|
||||
@ -19,13 +19,13 @@ function itemContentChanged() as void
|
||||
m.top.findNode("runtime").text = stri(getRuntime()).trim() + " mins"
|
||||
m.top.findNode("endtime").text = tr("Ends at %1").Replace("%1", getEndTime())
|
||||
end if
|
||||
if itemData.communityRating <> invalid then
|
||||
if itemData.communityRating <> invalid
|
||||
m.top.findNode("star").visible = true
|
||||
m.top.findNode("communityRating").text = str(int(itemData.communityRating*10)/10)
|
||||
else
|
||||
m.top.findNode("star").visible = false
|
||||
end if
|
||||
end function
|
||||
end sub
|
||||
|
||||
function getRuntime() as integer
|
||||
itemData = m.top.itemContent.json
|
||||
|
@ -14,7 +14,6 @@ sub init()
|
||||
end sub
|
||||
|
||||
sub updateSize()
|
||||
textHeight = 80
|
||||
itemWidth = 200
|
||||
itemHeight = 380 ' width * 1.5 + text
|
||||
|
||||
@ -32,13 +31,12 @@ sub updateSize()
|
||||
end sub
|
||||
|
||||
function getData()
|
||||
if m.top.TVSeasonData = invalid then
|
||||
if m.top.TVSeasonData = invalid
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
return data
|
||||
end if
|
||||
|
||||
seasonData = m.top.TVSeasonData
|
||||
rowsize = m.top.rowSize
|
||||
data = CreateObject("roSGNode", "ContentNode")
|
||||
row = data.CreateChild("ContentNode")
|
||||
row.title = "Seasons"
|
||||
|
@ -34,17 +34,14 @@ sub itemContentChanged()
|
||||
if itemData.genres.count() > 0
|
||||
setFieldText("genres", itemData.genres.join(", "))
|
||||
end if
|
||||
director = invalid
|
||||
for each person in itemData.people
|
||||
if person.type = "Director"
|
||||
director = person.name
|
||||
exit for
|
||||
end if
|
||||
end for
|
||||
if itemData.taglines.count() > 0
|
||||
setFieldText("tagline", itemData.taglines[0])
|
||||
end if
|
||||
' m.top.findNode("TVSeasonSelect").TVSeasonData = m.top.itemContent.seasons
|
||||
end sub
|
||||
|
||||
sub setFieldText(field, value)
|
||||
@ -52,9 +49,9 @@ sub setFieldText(field, value)
|
||||
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" then
|
||||
if type(value) = "roInt" or type(value) = "Integer"
|
||||
value = str(value)
|
||||
else if type(value) <> "roString" and type(value) <> "String" then
|
||||
else if type(value) <> "roString" and type(value) <> "String"
|
||||
value = ""
|
||||
end if
|
||||
|
||||
@ -77,7 +74,7 @@ function getEndTime() as string
|
||||
date.fromSeconds(date.asSeconds() + duration_s)
|
||||
date.toLocalTime()
|
||||
|
||||
formatTime(date)
|
||||
return formatTime(date)
|
||||
end function
|
||||
|
||||
function getHistory() as string
|
||||
|
@ -16,7 +16,7 @@ sub itemContentChanged()
|
||||
m.top.overhangTitle = itemData.name
|
||||
setFieldText("releaseYear", itemData.productionYear)
|
||||
setFieldText("officialRating", itemData.officialRating)
|
||||
if itemData.communityRating <> invalid then
|
||||
if itemData.communityRating <> invalid
|
||||
m.top.findNode("star").visible = true
|
||||
setFieldText("communityRating", itemData.communityRating)
|
||||
' m.top.findNode("communityRating").text = str(int(itemData.communityRating*10)/10)
|
||||
@ -35,17 +35,14 @@ sub itemContentChanged()
|
||||
if itemData.genres.count() > 0
|
||||
setFieldText("genres", itemData.genres.join(", "))
|
||||
end if
|
||||
director = invalid
|
||||
for each person in itemData.people
|
||||
if person.type = "Director"
|
||||
director = person.name
|
||||
exit for
|
||||
end if
|
||||
end for
|
||||
if itemData.taglines.count() > 0
|
||||
setFieldText("tagline", itemData.taglines[0])
|
||||
end if
|
||||
' m.top.findNode("TVSeasonSelect").TVSeasonData = m.top.itemContent.seasons
|
||||
end sub
|
||||
|
||||
sub setFieldText(field, value)
|
||||
@ -53,11 +50,11 @@ sub setFieldText(field, value)
|
||||
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" then
|
||||
if type(value) = "roInt" or type(value) = "Integer"
|
||||
value = str(value).trim()
|
||||
else if type(value) = "roFloat" or type(value) = "Float" then
|
||||
else if type(value) = "roFloat" or type(value) = "Float"
|
||||
value = str(value).trim()
|
||||
else if type(value) <> "roString" and type(value) <> "String" then
|
||||
else if type(value) <> "roString" and type(value) <> "String"
|
||||
value = ""
|
||||
end if
|
||||
|
||||
@ -80,7 +77,7 @@ function getEndTime() as string
|
||||
date.fromSeconds(date.asSeconds() + duration_s)
|
||||
date.toLocalTime()
|
||||
|
||||
formatTime(date)
|
||||
return formatTime(date)
|
||||
end function
|
||||
|
||||
function getHistory() as string
|
||||
|
@ -587,5 +587,419 @@
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Ansicht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Verbindung zum Server wird hergestellt</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>TV-Programm</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Kanäle</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Wiederholung</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>Live</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Endete</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Endete um</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Beginnt</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Beginnt um</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Seit</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Läuft seit</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Samstag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Freitag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Donnerstag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Mittwoch</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Dienstag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Montag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Sonntag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>morgen</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>gestern</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>heute</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Sortierung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Ansicht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Laufzeit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Veröffentlichungsdatum</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Wiedergaben</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Altersbeschränkung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Wiedergegeben am</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Hinzugefügt am</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Kritikerwertung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>IMDb-Wertung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>%1 enthält keine Elemente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Laden der Kanaldaten vom Server war nicht möglich</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Fehler beim Laden der Kanaldaten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Lade Kanaldaten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Bei der Wiedergabe dieses Elements ist ein Fehler aufgetreten.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Beim Laden der Daten dieses Elements ist ein Fehler aufgetreten.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Wiedergabefehler</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Fehler beim Laden des Inhalts</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Ausloggen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Server wechseln</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>Der aufgerufene Inhalt existiert nicht auf dem Server</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>Nicht gefunden</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Kanäle</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Verbindung zum Server wird hergestellt</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>TV-Programm</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Wiederholung</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>Live</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Endete</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Endete um</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Beginnt</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Beginnt um</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Seit</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Läuft seit</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Samstag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Freitag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Donnerstag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Mittwoch</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Dienstag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Montag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Sonntag</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>morgen</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>gestern</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>heute</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Sortierung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Ansicht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Laufzeit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Veröffentlichungsdatum</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Wiedergaben</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Altersfreigabe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Wiedergegeben am</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Hinzugefügt am</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Kritikerwertung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>IMDb Bewertung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Diese %1 enthält keine Elemente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Laden der Kanaldaten vom Server war nicht möglich</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Fehler beim Laden der Kanaldaten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Lade Kanaldaten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Bei der Wiedergabe dieses Elements ist ein Fehler aufgetreten.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Beim Laden der Daten dieses Elements ist ein Fehler aufgetreten.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Wiedergabefehler</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Fehler beim Laden des Inhalts</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Abmelden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Server wechseln</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -1,316 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.0" language="en_US" sourcelanguage="en_US">
|
||||
<defaultcodec>UTF-8</defaultcodec>
|
||||
<context>
|
||||
<name>default</name>
|
||||
<message>
|
||||
<source>192.168.1.100:8096 or https://example.com/jellyfin</source>
|
||||
<translation>192.168.1.100:8096 or https://example.com/jellyfin</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel</source>
|
||||
<translation>Cancel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Server</source>
|
||||
<translation>Connect to Server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at %1</source>
|
||||
<translation>Ends at %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter Configuration</source>
|
||||
<translation>Enter Configuration</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Favorite</source>
|
||||
<translation>Favorite</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading...</source>
|
||||
<translation>Loading...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Login attempt failed.</source>
|
||||
<translation>Login attempt failed.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OK</source>
|
||||
<translation>OK</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options</source>
|
||||
<translation>Options</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Play</source>
|
||||
<translation>Play</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Please sign in</source>
|
||||
<translation>Please sign in</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search</source>
|
||||
<translation>Search</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Server not found, is it online?</source>
|
||||
<translation>Server not found, is it online?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Shuffle</source>
|
||||
<translation>Shuffle</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign In</source>
|
||||
<translation>Sign In</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Submit</source>
|
||||
<translation>Submit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Watched</source>
|
||||
<translation>Watched</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Change Server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Sign Out</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Profile</source>
|
||||
<translation>Profile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>My Media</source>
|
||||
<translation>My Media</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Continue Watching</source>
|
||||
<translation>Continue Watching</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Next Up</source>
|
||||
<translation>Next Up</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Latest in</source>
|
||||
<translation>Latest in</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Home</source>
|
||||
<translation>Home</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter a value...</source>
|
||||
<translation>Enter a value...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort Field</source>
|
||||
<translation>Sort By</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date Added</source>
|
||||
<translation>Date Added</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Release Date</source>
|
||||
<translation>Release Date</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Name</source>
|
||||
<translation>Name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort Order</source>
|
||||
<translation>Sort Order</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Descending</source>
|
||||
<translation>Descending</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ascending</source>
|
||||
<translation>Ascending</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Password</source>
|
||||
<translation>Password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Username</source>
|
||||
<translation>Username</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Genres</source>
|
||||
<translation>Genres</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Director</source>
|
||||
<translation>Director</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Video</source>
|
||||
<translation>Video</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Audio</source>
|
||||
<translation>Audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Server</source>
|
||||
<translation>Server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Error Retrieving Content</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Error During Playback</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>There was an error retrieving data for this item from the server.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>An error was encountered while playing this item.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Loading Channel Data</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Error loading Channel Data</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Unable to load Channel Data from the server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>today</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>yesterday</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>tomorrow</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Sunday</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Monday</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Tuesday</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Wednesday</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Thursday</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Friday</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Saturday</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Started at</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Started</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Starts at</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Starts</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Ended at</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Ends at</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>Live</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Repeat</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Channels</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>TV Guide</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name></name>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>This %1 contains no items</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add User</source>
|
||||
<translation>Add User</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
@ -144,21 +144,6 @@
|
||||
<translation>Enter a value...</translation>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Sort Field</source>
|
||||
<translation>Sort By</translation>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Date Added</source>
|
||||
<translation>Date Added</translation>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Release Date</source>
|
||||
<translation>Release Date</translation>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Name</source>
|
||||
<translation>Name</translation>
|
||||
@ -414,6 +399,20 @@
|
||||
<translation>TV Guide</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Connecting to Server</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>Not found</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>The requested content does not exist on the server</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -144,21 +144,6 @@
|
||||
<translation>Enter a value...</translation>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Sort Field</source>
|
||||
<translation>Sort By</translation>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Date Added</source>
|
||||
<translation>Date Added</translation>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Release Date</source>
|
||||
<translation>Release Date</translation>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Name</source>
|
||||
<translation>Name</translation>
|
||||
@ -414,6 +399,20 @@
|
||||
<translation>TV Guide</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Connecting to Server</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>Not found</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>The requested content does not exist on the server</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -507,5 +507,403 @@
|
||||
<translation>Se produjo un error al recuperar los datos de este elemento del servidor.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Conectando al Servidor</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Guía de Televisión</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Canales</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Repetir</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>En Vivo</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Terminó en</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Terminó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Empieza</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Empezado</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Empieza a las</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Empezó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Sabado</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Viernes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Jueves</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Miércoles</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Martes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Lunes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Domingo</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>Mañana</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>Ayer</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>Hoy</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Orden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Vista</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Duración</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Fecha de Estreno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Contador de Reproducciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Calificación de los padres</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Fecha de Reproducción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Fecha de adición</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Puntuación de la Critica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Puntuación IMDB</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Nombre</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Este %1 no contiene elementos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Incapaz de cargar datos del Canal desde el Servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Error al cargar datos del Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Cargando datos del Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Se ha encontrado un error mientras se reproducía este elemento.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Hubo un error al recuperar los datos de este elemento desde el servidor.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Error durante la Reproducción</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Error Recuperando Contenido</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>El contenido solicitado no existe en el server</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>No Encontrado</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Conectando al Servidor</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Guía de Televisión</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Canales</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Repetir</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>En Vivo</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Finaliza a las</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Finalizó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Empieza</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Empieza a las</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Empezado</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Empezó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Sábado</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Viernes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Jueves</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Miércoles</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Martes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Lunes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Domingo</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>mañana</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>Ayer</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>Hoy</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Orden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Vista</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Duración</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Fecha de Estreno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Contador de Reproducciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Control de Padres</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Fecha de Reproducción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Fecha de Inclusión</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Calificación de los Críticos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Calificación IMDb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Nombre</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Este %1 no contiene items</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Incapaz de cargar los datos del canal desde el server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Error cargando datos del Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Cargando datos del Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Ha ocurrido un error mientras se reproducía este ítem.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Ha ocurrido un error recuperando los datos para este ítem desde el server.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Error durante la reproducción</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Error recuperando contenido</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -510,5 +510,403 @@
|
||||
<translation>Error recuperando Contenido</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Conectando al Servidor</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Guía de Televisión</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Canales</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Repetir</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>En Vivo</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Finaliza a las</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Terminó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Empieza</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Empieza a las</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Empezado</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Empezó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Sabado</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Viernes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Jueves</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Miércoles</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Martes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Lunes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Domingo</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>Mañana</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>Ayer</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>Hoy</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Orden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Vista</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Duración</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Fecha de Estreno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Contador de Reproducciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Calificación de los padres</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Fecha de Reproducción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Fecha de adición</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Puntuación de la Critica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Puntuación IMDB</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Nombre</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Este %1 no contiene elementos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Incapaz de cargar datos del Canal desde el Servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Error al cargar datos del Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Cargando datos del Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Se ha encontrado un error mientras se reproducía este elemento.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Hubo un error al recuperar los datos de este elemento desde el servidor.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Error durante la Reproducción</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Error Recuperando Contenido</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>El contenido solicitado no existe en el server</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>No Encontrado</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Conectando al Servidor</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Guía de Televisión</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Canales</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Repetir</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>En Vivo</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Finaliza a las</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Finalizó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Empieza</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Empieza a las</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Empezado</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Empezó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Sábado</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Viernes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Jueves</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Miércoles</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Martes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Lunes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Domingo</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>Mañana</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>Ayer</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>Hoy</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Orden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Vista</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Duración</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Fecha de Estreno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Contador de Reproducciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Control de Padres</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Fecha de Reproducción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Fecha de Inclusión</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Calificación de los Críticos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Calificación IMDb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Nombre</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Este %1 no contiene items</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Incapaz de cargar los datos del canal desde el server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Error cargando datos del Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Cargando datos del Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Ha ocurrido un error mientras se reproducía este ítem.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Ha ocurrido un error recuperando los datos para este ítem desde el server.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Error durante la reproducción</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Error recuperando contenido</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -602,5 +602,217 @@
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Este %1 no contiene elementos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>El contenido no existe en el servidor</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>No encontrado</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Conectando al servidor</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Guía TV</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Canales</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Repetido</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>Directo</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Finaliza a las</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Terminó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Inicia el</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Inicia a las</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Iniciado</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Iniciado a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Sábado</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Viernes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Jueves</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Miércoles</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Martes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Lunes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Domingo</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>mañana</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>ayer</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>hoy</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Ordenar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Vista</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Tiempo de reproducción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Nombre</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Fecha de lanzamiento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Número de reproducciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Clasificación parental</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Fecha de reproducción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Fecha añadido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Calificación de la crítica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Calificación de IMDb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Este %1 no contiene elementos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>No es posible cargar los datos del canal desde el servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Error al cargar la información del canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Cargando información del canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Se encontró un error al reproducir este elemento.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Hubo un error extrayendo los datos de este objeto del servidor.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Error Durante la Reproducción</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Error recuperando contenido</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Cerrar sesión</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Cambiar servidor</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -534,5 +534,264 @@
|
||||
<translation>Relancer</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Chaînes</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Samedi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Vendredi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Jeudi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Mercredi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Mardi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Une erreur s'est produite lors de la lecture de cet élément.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Une erreur s'est produite lors de la récupération des informations pour cet élément sur le serveur.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Erreur pendant la lecture</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Erreur lors de la récupération du contenu</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Une erreur s'est produite lors de la récupération des informations pour cet élément sur le serveur.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Erreur lors de la récupération du contenu</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>Le contenu demandé n'existe pas sur le serveur</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>Non trouvé</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Connexion au serveur</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Guide des chaînes</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Chaînes</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Rediffusion</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>En direct</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Terminé à</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Terminé à</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Débutera</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Débute à</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Débuté</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Débuté à</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Samedi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Vendredi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Jeudi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Mercredi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Mardi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Lundi</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Dimanche</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>demain</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>hier</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Nombre de lectures</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Classification parentale</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Date de lecture</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Notations critiques</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>%1 ne contient aucun élément</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Erreur pendant la lecture</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>aujourd'hui</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtrer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Trier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Vue</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Durée</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Date de sortie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Date d'ajout</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Notation IMDb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Nom</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Impossible de charger les données de la chaîne depuis le serveur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Erreur lors du chargement des données de la chaîne</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Chargement des données de la chaîne</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Une erreur s'est produite durant la lecture de cet élément</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Une erreur s'est produite durant la lecture de cet élément.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -118,7 +118,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort Field</source>
|
||||
<translation>Rendezés a következő szerint:</translation>
|
||||
<translation>Rendezés a következő szerint</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Date Added</source>
|
||||
@ -453,5 +453,408 @@
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Szülői értékelés</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Csatornaadatok betöltése</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Hiba történt az elem lejátszása közben</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Hiba történt az elem(ek) adatainak lekérése során a szerverről.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Hiba történt a lejátszás közben</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Hiba a tartalom beolvasása közben</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Csatlakozás a szerverhez</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Műsorújság</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Csatornák</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Ismétlés</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>Élő</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Vége volt</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Vége lett</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Kezdődik</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>-kor kezdődik majd</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Kezdődött</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Kezdés</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Szombat</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Péntek</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Csütörtök</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Szerda</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Kedd</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Hétfő</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Vasárnap</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>holnap</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>tegnap</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>ma</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Szűrő</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Rendezés</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Nézet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Futásidő</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Megjelenés dátuma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Lejátszások száma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Korhatár</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Lejátszás dátuma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Hozzáadás dátuma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Kritikusok értékelése</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>IMDB Értékelés</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Név</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Ez a(z) %1 nem tartalmaz elemeket</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Nem lehet betölteni a csatornaadatokat a szerverről</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Hiba történt a csatorna adatainak betöltésekor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Hiba történt az elem lejátszása közben.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>A kért tartalom nem létezik a szerveren</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>Nem található</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Csatlakozás a szerverhez</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Műsorújság</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Csatornák</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Ismétlés</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>Élő</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Vége volt</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Vége lett</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Kezdődni fog</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Kezdődik majd</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Kezdődött</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Ekkor kezdődött</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Szombat</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Péntek</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Csütörtök</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Szerda</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Kedd</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Hétfő</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Vasárnap</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>holnap</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>tegnap</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>ma</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Szűrő</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Rendezés</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Nézet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Futásidő</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Megjelenés dátuma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Lejátszások száma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Korhatár</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Lejátszás dátuma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Hozzáadva</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Kritikusok értékelése</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>IMDb Értékelés</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Név</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Ez a(z) %1 nem tartalmaz elemeket</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Nem lehet betölteni a csatornaadatokat a szerverről</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Hiba történt a csatornaadatok betöltésekor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Csatornaadatok betöltése</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Hiba történt az elem lejátszása közben.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Hiba történt az elem(ek) betöltése során a szerverről.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Hiba történt a lejátszás közben</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Hiba a tartalom beolvasása közben</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
@ -582,5 +582,208 @@
|
||||
<source>Change Server</source>
|
||||
<translation>Mudar Servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Conectando no servidor</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Guia de TV</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Canais</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Repetir</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>Ao Vivo</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Termina</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Termina às</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Começa</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Começa às</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Começou</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Começou às</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Sábado</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Sexta feira</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Quinta feira</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Quarta feira</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Terça feira</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Segunda feira</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Domingo</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>amanhã</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>ontem</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>hoje</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Ordenar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Visualização</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Tempo de Execução</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Data de Lançamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Número de Execução</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Avaliação Parental</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Data da Execução</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Data de Adição</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Avaliação da crítica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Classificação IMDb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Nome</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)</comment>
|
||||
<source>NO_ITEMS</source>
|
||||
<translation>Este %1
|
||||
não contém itens</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>Incapaz de carregar os dados do canal do servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Erro ao carregar dados do canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Carregando dados do canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Foi encontrado um erro ao reproduzir este item.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Ocorreu um erro ao recuperar os dados para este item do servidor.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Erro durante a reprodução</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Erro ao recuperar conteúdo</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Sair</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Trocar Servidor</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
4
manifest
4
manifest
@ -1,8 +1,8 @@
|
||||
## Channel Details
|
||||
title=Jellyfin
|
||||
major_version=1
|
||||
minor_version=3
|
||||
build_version=5
|
||||
minor_version=4
|
||||
build_version=9
|
||||
|
||||
### Main Menu Icons / Channel Poster Artwork
|
||||
mm_icon_focus_fhd=pkg:/images/channel-poster_fhd.png
|
||||
|
468
package-lock.json
generated
468
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jellyfin-roku",
|
||||
"version": "1.4.0",
|
||||
"version": "1.4.9",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -10,6 +10,377 @@
|
||||
"integrity": "sha512-2ox6EUL+UTtccTbD4dbVjZK3QHa0PHCqpoKMF8lZz9ayzzEP3iVPF8KZR6hOi6bxsIcbGXVjqmtCVkpC4P9SrA==",
|
||||
"dev": true
|
||||
},
|
||||
"@rokucommunity/bslint": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.4.0.tgz",
|
||||
"integrity": "sha512-txIWxeSyoMlBHCV8ZFnyndCtZa7xFdPkCE+/5ZHrt+hw7jeQj8Y3XhCm4o6x1JApQRp/tT/olSwkwIOe29gsKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brighterscript": "^0.39.1",
|
||||
"fs-extra": "^10.0.0",
|
||||
"jsonc-parser": "^2.3.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"yargs": "^15.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"brighterscript": {
|
||||
"version": "0.39.3",
|
||||
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.39.3.tgz",
|
||||
"integrity": "sha512-wKF9Nyv78puQf8j0dFe3RMv2+PiHf4CLkxwhM6QrozcXJYtOe5vxx6EuFSjY6bz3dri15yiykxCmHKsJ7K63Tg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rokucommunity/bslib": "^0.1.1",
|
||||
"@xml-tools/parser": "^1.0.7",
|
||||
"array-flat-polyfill": "^1.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"chevrotain": "^7.0.1",
|
||||
"chokidar": "^3.5.1",
|
||||
"clear": "^0.1.0",
|
||||
"cross-platform-clear-console": "^2.3.0",
|
||||
"debounce-promise": "^3.1.0",
|
||||
"eventemitter3": "^4.0.0",
|
||||
"file-url": "^3.0.0",
|
||||
"fs-extra": "^7.0.1",
|
||||
"glob": "^7.1.6",
|
||||
"jsonc-parser": "^2.3.0",
|
||||
"long": "^3.2.0",
|
||||
"luxon": "^1.8.3",
|
||||
"minimatch": "^3.0.4",
|
||||
"moment": "^2.23.0",
|
||||
"p-settle": "^2.1.0",
|
||||
"parse-ms": "^2.1.0",
|
||||
"roku-deploy": "^3.4.1",
|
||||
"serialize-error": "^7.0.1",
|
||||
"source-map": "^0.7.3",
|
||||
"vscode-languageserver": "7.0.0",
|
||||
"vscode-languageserver-protocol": "3.16.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.1",
|
||||
"vscode-uri": "^2.1.1",
|
||||
"xml2js": "^0.4.19",
|
||||
"yargs": "^16.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.0",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^20.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
|
||||
"integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"anymatch": "~3.1.1",
|
||||
"braces": "~3.0.2",
|
||||
"fsevents": "~2.3.1",
|
||||
"glob-parent": "~5.1.0",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.5.0"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz",
|
||||
"integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonfile": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"dev": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
|
||||
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
|
||||
"integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@xml-tools/parser": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@xml-tools/parser/-/parser-1.0.11.tgz",
|
||||
@ -753,6 +1124,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
@ -1857,6 +2234,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
|
||||
"dev": true
|
||||
},
|
||||
"decode-uri-component": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||
@ -2088,6 +2471,16 @@
|
||||
"repeat-string": "^1.5.2"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"for-in": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||
@ -2663,10 +3056,19 @@
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.toarray": {
|
||||
@ -3004,6 +3406,32 @@
|
||||
"p-try": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"p-reflect": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-reflect/-/p-reflect-1.0.0.tgz",
|
||||
@ -3067,6 +3495,12 @@
|
||||
"util": "^0.10.3"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
@ -3310,6 +3744,12 @@
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"dev": true
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-url": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||
@ -3412,7 +3852,7 @@
|
||||
"es2015": "0.0.0",
|
||||
"fs-extra": "^5.0.0",
|
||||
"glob-all": "^3.1.0",
|
||||
"lodash": "^4.17.19",
|
||||
"lodash": "^4.17.21",
|
||||
"replace-ext": "^1.0.0",
|
||||
"request": "^2.88.0",
|
||||
"request-promise": "^4.2.4",
|
||||
@ -3456,6 +3896,12 @@
|
||||
"type-fest": "^0.13.1"
|
||||
}
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
|
||||
"dev": true
|
||||
},
|
||||
"set-immediate-shim": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
|
||||
@ -4054,6 +4500,12 @@
|
||||
"integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==",
|
||||
"dev": true
|
||||
},
|
||||
"which-module": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
@ -4152,9 +4604,9 @@
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "20.2.9",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
||||
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
||||
"version": "20.2.7",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz",
|
||||
"integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
{
|
||||
"name": "jellyfin-roku",
|
||||
"version": "1.4.0",
|
||||
"version": "1.4.9",
|
||||
"description": "Roku app for Jellyfin media server",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"test": "tests"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rokucommunity/bslint": "^0.4.0",
|
||||
"brighterscript": "^0.39.4",
|
||||
"rooibos-cli": "^1.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"validate": "npx bsc --copy-to-staging=false --create-package=false",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "bslint"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
211
source/Main.brs
211
source/Main.brs
@ -1,8 +1,8 @@
|
||||
sub Main()
|
||||
sub Main (args as Dynamic) as Void
|
||||
|
||||
' If the Rooibos files are included in deployment, run tests
|
||||
'bs:disable-next-line
|
||||
if (type(Rooibos__Init) = "Function") then Rooibos__Init()
|
||||
if type(Rooibos__Init) = "Function" then Rooibos__Init()
|
||||
|
||||
' The main function that runs when the application is launched.
|
||||
m.screen = CreateObject("roSGScreen")
|
||||
@ -22,8 +22,6 @@ sub Main()
|
||||
m.overhang = CreateObject("roSGNode", "JFOverhang")
|
||||
m.scene.insertChild(m.overhang, 0)
|
||||
|
||||
m.page_size = 48
|
||||
|
||||
app_start:
|
||||
m.overhang.title = ""
|
||||
' First thing to do is validate the ability to use the API
|
||||
@ -52,20 +50,41 @@ sub Main()
|
||||
m.device.setMessagePort(m.port)
|
||||
m.device.EnableScreensaverExitedEvent(true)
|
||||
|
||||
' Check if we were sent content to play with the startup command (Deep Link)
|
||||
if (args.mediaType <> invalid) and (args.contentId <> invalid)
|
||||
video = CreateVideoPlayerGroup(args.contentId)
|
||||
|
||||
if video <> invalid
|
||||
if group.lastFocus = invalid then group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
group = video
|
||||
m.scene.appendChild(group)
|
||||
group.setFocus(true)
|
||||
group.control = "play"
|
||||
ReportPlayback(group, "start")
|
||||
m.overhang.visible = false
|
||||
else
|
||||
dialog = createObject("roSGNode", "Dialog")
|
||||
dialog.id = "OKDialog"
|
||||
dialog.title = tr("Not found")
|
||||
dialog.message = tr("The requested content does not exist on the server")
|
||||
dialog.buttons = [tr("OK")]
|
||||
m.scene.dialog = dialog
|
||||
m.scene.dialog.observeField("buttonSelected", m.port)
|
||||
end if
|
||||
end if
|
||||
|
||||
' This is the core logic loop. Mostly for transitioning between scenes
|
||||
' This now only references m. fields so could be placed anywhere, in theory
|
||||
' "group" is always "whats on the screen"
|
||||
' m.scene's children is the "previous view" stack
|
||||
while(true)
|
||||
while true
|
||||
msg = wait(0, m.port)
|
||||
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed() then
|
||||
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed()
|
||||
print "CLOSING SCREEN"
|
||||
return
|
||||
else if isNodeEvent(msg, "buttonSelected")
|
||||
' Dialog Button Selected - If not handled more locally, just close the dialog
|
||||
dialog = msg.getRoSGNode()
|
||||
dialog.unobserveField("buttonSelected")
|
||||
dialog.close = true
|
||||
|
||||
else if isNodeEvent(msg, "backPressed")
|
||||
n = m.scene.getChildCount() - 1
|
||||
if msg.getRoSGNode().focusedChild <> invalid and msg.getRoSGNode().focusedChild.isSubtype("JFVideo")
|
||||
@ -91,9 +110,9 @@ sub Main()
|
||||
reportingNode = msg.getRoSGNode()
|
||||
itemNode = reportingNode.quickPlayNode
|
||||
if itemNode = invalid or itemNode.id = "" then return
|
||||
if itemNode.type = "Episode" or itemNode.type = "Movie" or itemNode.type = "Video" then
|
||||
if itemNode.type = "Episode" or itemNode.type = "Movie" or itemNode.type = "Video"
|
||||
video = CreateVideoPlayerGroup(itemNode.id)
|
||||
if video <> invalid then
|
||||
if video <> invalid
|
||||
group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
@ -108,7 +127,7 @@ sub Main()
|
||||
else if isNodeEvent(msg, "selectedItem")
|
||||
' If you select a library from ANYWHERE, follow this flow
|
||||
selectedItem = msg.getData()
|
||||
if selectedItem.type = "CollectionFolder" OR selectedItem.type = "UserView" OR selectedItem.type = "Folder"
|
||||
if selectedItem.type = "CollectionFolder" OR selectedItem.type = "UserView" OR selectedItem.type = "Folder" OR selectedItem.type = "Channel" OR selectedItem.type = "Boxset"
|
||||
group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
@ -116,21 +135,12 @@ sub Main()
|
||||
group = CreateItemGrid(selectedItem)
|
||||
group.overhangTitle = selectedItem.title
|
||||
m.scene.appendChild(group)
|
||||
else if selectedItem.type = "Folder"
|
||||
group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
|
||||
m.overhang.title = selectedItem.title
|
||||
group = CreateItemGrid(selectedItem)
|
||||
group.overhangTitle = selectedItem.title
|
||||
m.scene.appendChild(group)
|
||||
else if selectedItem.type = "Episode" then
|
||||
else if selectedItem.type = "Episode"
|
||||
' play episode
|
||||
' todo: create an episode page to link here
|
||||
video_id = selectedItem.id
|
||||
video = CreateVideoPlayerGroup(video_id)
|
||||
if video <> invalid then
|
||||
if video <> invalid
|
||||
group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
@ -141,7 +151,7 @@ sub Main()
|
||||
ReportPlayback(group, "start")
|
||||
m.overhang.visible = false
|
||||
end if
|
||||
else if selectedItem.type = "Series" then
|
||||
else if selectedItem.type = "Series"
|
||||
group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
@ -152,7 +162,7 @@ sub Main()
|
||||
group = CreateSeriesDetailsGroup(selectedItem.json)
|
||||
group.overhangTitle = selectedItem.title
|
||||
m.scene.appendChild(group)
|
||||
else if selectedItem.type = "Movie" then
|
||||
else if selectedItem.type = "Movie"
|
||||
' open movie detail page
|
||||
group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
@ -165,7 +175,7 @@ sub Main()
|
||||
group.overhangTitle = selectedItem.title
|
||||
m.scene.appendChild(group)
|
||||
|
||||
else if selectedItem.type = "TvChannel" or selectedItem.type = "Video" then
|
||||
else if selectedItem.type = "TvChannel" or selectedItem.type = "Video"
|
||||
' play channel feed
|
||||
video_id = selectedItem.id
|
||||
|
||||
@ -177,7 +187,7 @@ sub Main()
|
||||
video = CreateVideoPlayerGroup(video_id)
|
||||
dialog.close = true
|
||||
|
||||
if video <> invalid then
|
||||
if video <> invalid
|
||||
if group.lastFocus = invalid then group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
@ -189,6 +199,7 @@ sub Main()
|
||||
m.overhang.visible = false
|
||||
else
|
||||
dialog = createObject("roSGNode", "Dialog")
|
||||
dialog.id = "OKDialog"
|
||||
dialog.title = tr("Error loading Channel Data")
|
||||
dialog.message = tr("Unable to load Channel Data from the server")
|
||||
dialog.buttons = [tr("OK")]
|
||||
@ -198,7 +209,6 @@ sub Main()
|
||||
else
|
||||
' TODO - switch on more node types
|
||||
message_dialog("This type is not yet supported: " + selectedItem.type + ".")
|
||||
selectedItem = invalid
|
||||
end if
|
||||
else if isNodeEvent(msg, "movieSelected")
|
||||
' If you select a movie from ANYWHERE, follow this flow
|
||||
@ -249,7 +259,7 @@ sub Main()
|
||||
node = getMsgPicker(msg, "picker")
|
||||
video_id = node.id
|
||||
video = CreateVideoPlayerGroup(video_id)
|
||||
if video <> invalid then
|
||||
if video <> invalid
|
||||
group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
@ -267,21 +277,13 @@ sub Main()
|
||||
options.visible = true
|
||||
options.setFocus(true)
|
||||
|
||||
dialog = createObject("roSGNode", "ProgressDialog")
|
||||
dialog.title = tr("Loading Search Data")
|
||||
m.scene.dialog = dialog
|
||||
results = SearchMedia(query)
|
||||
dialog.close = true
|
||||
options.itemData = results
|
||||
options.query = query
|
||||
else if isNodeEvent(msg, "pageSelected")
|
||||
group.pageNumber = msg.getRoSGNode().pageSelected
|
||||
collectionType = group.subType()
|
||||
if collectionType = "Collections"
|
||||
CollectionLister(group, m.page_size)
|
||||
else if collectionType = "TVShows"
|
||||
SeriesLister(group, m.page_size)
|
||||
else if collectionType = "Channels"
|
||||
ChannelLister(group, m.page_size)
|
||||
end if
|
||||
' TODO - abstract away the "picker" node
|
||||
group.findNode("picker").setFocus(true)
|
||||
else if isNodeEvent(msg, "itemSelected")
|
||||
' Search item selected
|
||||
node = getMsgPicker(msg)
|
||||
@ -291,7 +293,7 @@ sub Main()
|
||||
|
||||
' TODO - swap this based on target.mediatype
|
||||
' types: [ Series (Show), Episode, Movie, Audio, Person, Studio, MusicArtist ]
|
||||
if node.type = "Series" then
|
||||
if node.type = "Series"
|
||||
group = CreateSeriesDetailsGroup(node)
|
||||
else
|
||||
group = CreateMovieDetailsGroup(node)
|
||||
@ -302,7 +304,7 @@ sub Main()
|
||||
else if isNodeEvent(msg, "buttonSelected")
|
||||
' If a button is selected, we have some determining to do
|
||||
btn = getButton(msg)
|
||||
if btn.id = "play-button"
|
||||
if btn <> invalid and btn.id = "play-button"
|
||||
' Check is a specific Audio Stream was selected
|
||||
audio_stream_idx = 1
|
||||
if group.selectedAudioStreamIndex <> invalid
|
||||
@ -313,7 +315,7 @@ sub Main()
|
||||
' This is currently page layout Group, button Group, then button
|
||||
video_id = group.id
|
||||
video = CreateVideoPlayerGroup(video_id, audio_stream_idx)
|
||||
if video <> invalid then
|
||||
if video <> invalid
|
||||
group.lastFocus = group.focusedChild.focusedChild.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
@ -324,7 +326,7 @@ sub Main()
|
||||
ReportPlayback(group, "start")
|
||||
m.overhang.visible = false
|
||||
end if
|
||||
else if btn.id = "watched-button"
|
||||
else if btn <> invalid and btn.id = "watched-button"
|
||||
movie = group.itemContent
|
||||
if movie.watched
|
||||
UnmarkItemWatched(movie.id)
|
||||
@ -332,7 +334,7 @@ sub Main()
|
||||
MarkItemWatched(movie.id)
|
||||
end if
|
||||
movie.watched = not movie.watched
|
||||
else if btn.id = "favorite-button"
|
||||
else if btn <> invalid and btn.id = "favorite-button"
|
||||
movie = group.itemContent
|
||||
if movie.favorite
|
||||
UnmarkItemFavorite(movie.id)
|
||||
@ -340,6 +342,13 @@ sub Main()
|
||||
MarkItemFavorite(movie.id)
|
||||
end if
|
||||
movie.favorite = not movie.favorite
|
||||
else
|
||||
' If there are no other button matches, check if this is a simple "OK" Dialog & Close if so
|
||||
dialog = msg.getRoSGNode()
|
||||
if dialog.id = "OKDialog"
|
||||
dialog.unobserveField("buttonSelected")
|
||||
dialog.close = true
|
||||
end if
|
||||
end if
|
||||
else if isNodeEvent(msg, "optionSelected")
|
||||
button = msg.getRoSGNode()
|
||||
@ -374,9 +383,9 @@ sub Main()
|
||||
end if
|
||||
else if isNodeEvent(msg, "selectSubtitlePressed")
|
||||
node = m.scene.focusedChild
|
||||
if node.isSubType("JFVideo") then
|
||||
if node.isSubType("JFVideo")
|
||||
trackSelected = selectSubtitleTrack(node.Subtitles, node.SelectedSubtitle)
|
||||
if trackSelected <> invalid and trackSelected <> node.SelectedSubtitle then
|
||||
if trackSelected <> invalid and trackSelected <> -2
|
||||
changeSubtitleDuringPlayback(trackSelected)
|
||||
end if
|
||||
end if
|
||||
@ -384,37 +393,22 @@ sub Main()
|
||||
ReportPlayback(group, "update")
|
||||
else if isNodeEvent(msg, "state")
|
||||
node = msg.getRoSGNode()
|
||||
if node.state = "finished" then
|
||||
if node.state = "finished"
|
||||
stopPlayback()
|
||||
if node.showID = invalid then
|
||||
if node.showID = invalid
|
||||
RemoveCurrentGroup()
|
||||
else
|
||||
nextEpisode =autoPlayNextEpisode(node.id, node.showID)
|
||||
if nextEpisode <> invalid then group = nextEpisode
|
||||
end if
|
||||
else if node.state = "playing" or node.state = "paused" then
|
||||
else if node.state = "playing" or node.state = "paused"
|
||||
ReportPlayback(group, "update")
|
||||
end if
|
||||
else if type(msg) = "roDeviceInfoEvent" then
|
||||
else if type(msg) = "roDeviceInfoEvent"
|
||||
event = msg.GetInfo()
|
||||
if event.appFocused <> invalid then
|
||||
child = m.scene.focusedChild
|
||||
if child <> invalid and child.isSubType("JFVideo") then
|
||||
child.systemOverlay = not event.appFocused
|
||||
if event.AppFocused = true then
|
||||
systemOverlayClosed()
|
||||
end if
|
||||
end if
|
||||
else if event.Mute <> invalid then
|
||||
m.mute = event.Mute
|
||||
child = m.scene.focusedChild
|
||||
if child <> invalid and child.isSubType("JFVideo") and areSubtitlesDisplayed() and child.systemOverlay = false then
|
||||
'Event will be called on caption change which includes the current mute status, but we do not want to call until the overlay is closed
|
||||
reviewSubtitleDisplay()
|
||||
end if
|
||||
else if event.exitedScreensaver = true then
|
||||
if event.exitedScreensaver = true
|
||||
m.overhang.callFunc("resetTime")
|
||||
if group.subtype() = "Home" then
|
||||
if group.subtype() = "Home"
|
||||
currentTime = CreateObject("roDateTime").AsSeconds()
|
||||
group.timeLastRefresh = currentTime
|
||||
group.callFunc("refresh")
|
||||
@ -424,6 +418,32 @@ sub Main()
|
||||
print "Unhandled roDeviceInfoEvent:"
|
||||
print msg.GetInfo()
|
||||
end if
|
||||
else if type(msg) = "roInputEvent"
|
||||
if msg.IsInput()
|
||||
info = msg.GetInfo()
|
||||
if info.DoesExist("mediatype") and info.DoesExist("contentid")
|
||||
video = CreateVideoPlayerGroup(info.contentId)
|
||||
if video <> invalid
|
||||
if group.lastFocus = invalid then group.lastFocus = group.focusedChild
|
||||
group.setFocus(false)
|
||||
group.visible = false
|
||||
group = video
|
||||
m.scene.appendChild(group)
|
||||
group.setFocus(true)
|
||||
group.control = "play"
|
||||
ReportPlayback(group, "start")
|
||||
m.overhang.visible = false
|
||||
else
|
||||
dialog = createObject("roSGNode", "Dialog")
|
||||
dialog.id = "OKDialog"
|
||||
dialog.title = tr("Not found")
|
||||
dialog.message = tr("The requested content does not exist on the server")
|
||||
dialog.buttons = [tr("OK")]
|
||||
m.scene.dialog = dialog
|
||||
m.scene.dialog.observeField("buttonSelected", m.port)
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
else
|
||||
print "Unhandled " type(msg)
|
||||
print msg
|
||||
@ -433,46 +453,59 @@ sub Main()
|
||||
end sub
|
||||
|
||||
function LoginFlow(startOver = false as boolean)
|
||||
if m.scene <> invalid then
|
||||
if m.scene <> invalid
|
||||
m.scene.unobserveField("backPressed")
|
||||
end if
|
||||
'Collect Jellyfin server and user information
|
||||
start_login:
|
||||
if get_setting("server") = invalid or ServerInfo() = invalid or startOver = true then
|
||||
|
||||
if get_setting("server") = invalid then startOver = true
|
||||
|
||||
invalidServer = true
|
||||
if not startOver
|
||||
' Show Connecting to Server spinner
|
||||
dialog = createObject("roSGNode", "ProgressDialog")
|
||||
dialog.title = tr("Connecting to Server")
|
||||
m.scene.dialog = dialog
|
||||
invalidServer = ServerInfo().Error
|
||||
dialog.close = true
|
||||
end if
|
||||
|
||||
if startOver or invalidServer
|
||||
print "Get server details"
|
||||
SendPerformanceBeacon("AppDialogInitiate") ' Roku Performance monitoring - Dialog Starting
|
||||
serverSelection = CreateServerGroup()
|
||||
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
|
||||
if serverSelection = "backPressed" then
|
||||
if serverSelection = "backPressed"
|
||||
print "backPressed"
|
||||
wipe_groups()
|
||||
return false
|
||||
end if
|
||||
end if
|
||||
|
||||
if get_setting("active_user") = invalid then
|
||||
if get_setting("active_user") = invalid
|
||||
SendPerformanceBeacon("AppDialogInitiate") ' Roku Performance monitoring - Dialog Starting
|
||||
publicUsers = GetPublicUsers()
|
||||
if publicUsers.count() then
|
||||
if publicUsers.count()
|
||||
publicUsersNodes = []
|
||||
for each item in publicUsers
|
||||
user = CreateObject("roSGNode", "PublicUserData")
|
||||
user.id = item.Id
|
||||
user.name = item.Name
|
||||
if item.PrimaryImageTag <> invalid then
|
||||
if item.PrimaryImageTag <> invalid
|
||||
user.ImageURL = UserImageURL(user.id, { "tag": item.PrimaryImageTag })
|
||||
end if
|
||||
publicUsersNodes.push(user)
|
||||
end for
|
||||
userSelected = CreateUserSelectGroup(publicUsersNodes)
|
||||
m.scene.focusedChild.visible = false
|
||||
if userSelected = "backPressed" then
|
||||
if userSelected = "backPressed"
|
||||
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
|
||||
return LoginFlow(true)
|
||||
else
|
||||
'Try to login without password. If the token is valid, we're done
|
||||
get_token(userSelected, "")
|
||||
if get_setting("active_user") <> invalid then
|
||||
if get_setting("active_user") <> invalid
|
||||
m.user = AboutMe()
|
||||
LoadUserPreferences()
|
||||
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
|
||||
@ -484,7 +517,7 @@ function LoginFlow(startOver = false as boolean)
|
||||
end if
|
||||
passwordEntry = CreateSigninGroup(userSelected)
|
||||
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
|
||||
if passwordEntry = "backPressed" then
|
||||
if passwordEntry = "backPressed"
|
||||
m.scene.focusedChild.visible = false
|
||||
return LoginFlow(true)
|
||||
end if
|
||||
@ -514,12 +547,12 @@ sub RunScreenSaver()
|
||||
m.port = createObject("roMessagePort")
|
||||
screen.setMessagePort(m.port)
|
||||
|
||||
scene = screen.createScene("Screensaver")
|
||||
screen.createScene("Screensaver")
|
||||
screen.Show()
|
||||
|
||||
while(true)
|
||||
while true
|
||||
msg = wait(8000, m.port)
|
||||
if (msg <> invalid)
|
||||
if msg <> invalid
|
||||
msgType = type(msg)
|
||||
if msgType = "roSGScreenEvent"
|
||||
if msg.isScreenClosed() then return
|
||||
@ -531,7 +564,7 @@ end sub
|
||||
|
||||
sub wipe_groups()
|
||||
' The 1 remaining child should be the overhang
|
||||
while(m.scene.getChildCount() > 1)
|
||||
while m.scene.getChildCount() > 1
|
||||
m.scene.removeChildIndex(1)
|
||||
end while
|
||||
end sub
|
||||
@ -545,8 +578,8 @@ sub RemoveCurrentGroup()
|
||||
group = m.scene.getChild(n - 1)
|
||||
m.overhang.title = group.overhangTitle
|
||||
m.overhang.showOptions = group.optionsAvailable
|
||||
if group.optionsAvailable <> prevOptionsAvailable then
|
||||
if group.optionsAvailable = false then
|
||||
if group.optionsAvailable <> prevOptionsAvailable
|
||||
if group.optionsAvailable = false
|
||||
m.scene.unobserveField("optionsPressed")
|
||||
else
|
||||
m.scene.observeField("optionsPressed", m.port)
|
||||
@ -558,9 +591,9 @@ sub RemoveCurrentGroup()
|
||||
else
|
||||
group.setFocus(true)
|
||||
end if
|
||||
if group.subtype() = "Home" then
|
||||
if group.subtype() = "Home"
|
||||
currentTime = CreateObject("roDateTime").AsSeconds()
|
||||
if group.timeLastRefresh = invalid or (currentTime - group.timeLastRefresh) > 20 then
|
||||
if group.timeLastRefresh = invalid or (currentTime - group.timeLastRefresh) > 20
|
||||
group.timeLastRefresh = currentTime
|
||||
group.callFunc("refresh")
|
||||
end if
|
||||
@ -570,7 +603,7 @@ end sub
|
||||
|
||||
' Roku Performance monitoring
|
||||
sub SendPerformanceBeacon(signalName as string)
|
||||
if m.global.app_loaded = false then
|
||||
if m.global.app_loaded = false
|
||||
m.scene.signalBeacon(signalName)
|
||||
end if
|
||||
end sub
|
||||
|
@ -12,7 +12,7 @@ function CreateServerGroup()
|
||||
button.observeField("buttonSelected", port)
|
||||
screen.observeField("backPressed", port)
|
||||
|
||||
while(true)
|
||||
while true
|
||||
msg = wait(0, port)
|
||||
print type(msg), msg
|
||||
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed()
|
||||
@ -29,12 +29,33 @@ function CreateServerGroup()
|
||||
set_setting("password", "")
|
||||
end if
|
||||
set_setting("server", serverUrl)
|
||||
if ServerInfo() = invalid then
|
||||
' Show Connecting to Server spinner
|
||||
dialog = createObject("roSGNode", "ProgressDialog")
|
||||
dialog.title = tr("Connecting to Server")
|
||||
m.scene.dialog = dialog
|
||||
|
||||
serverInfoResult = ServerInfo()
|
||||
|
||||
dialog.close = true
|
||||
|
||||
if serverInfoResult = invalid
|
||||
' Maybe don't unset setting, but offer as a prompt
|
||||
' Server not found, is it online? New values / Retry
|
||||
print "Server not found, is it online? New values / Retry"
|
||||
screen.findNode("alert").text = tr("Server not found, is it online?")
|
||||
SignOut()
|
||||
else if serverInfoResult.Error <> invalid and serverInfoResult.Error
|
||||
' If server redirected received, update the URL
|
||||
if serverInfoResult.UpdatedUrl <> invalid
|
||||
server_hostname.value = serverInfoResult.UpdatedUrl
|
||||
end if
|
||||
' Display Error Message to user
|
||||
message = tr("Error: ")
|
||||
if serverInfoResult.ErrorCode <> invalid
|
||||
message = message + "[" + serverInfoResult.ErrorCode.toStr() + "] "
|
||||
end if
|
||||
screen.findNode("alert").text = message + tr(serverInfoResult.ErrorMessage)
|
||||
SignOut()
|
||||
else
|
||||
screen.visible = false
|
||||
return "true"
|
||||
@ -48,7 +69,7 @@ function CreateServerGroup()
|
||||
end function
|
||||
|
||||
function CreateUserSelectGroup(users = [])
|
||||
if users.count() = 0 then
|
||||
if users.count() = 0
|
||||
return ""
|
||||
end if
|
||||
group = CreateObject("roSGNode", "UserSelect")
|
||||
@ -59,7 +80,7 @@ function CreateUserSelectGroup(users = [])
|
||||
group.findNode("userRow").observeField("userSelected", port)
|
||||
group.findNode("alternateOptions").observeField("itemSelected", port)
|
||||
group.observeField("backPressed", port)
|
||||
while(true)
|
||||
while true
|
||||
msg = wait(0, port)
|
||||
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed()
|
||||
group.visible = false
|
||||
@ -69,7 +90,7 @@ function CreateUserSelectGroup(users = [])
|
||||
else if type(msg) = "roSGNodeEvent" and msg.getField() = "userSelected"
|
||||
return msg.GetData()
|
||||
else if type(msg) = "roSGNodeEvent" and msg.getField() = "itemSelected"
|
||||
if msg.getData() = 0 then
|
||||
if msg.getData() = 0
|
||||
return ""
|
||||
end if
|
||||
end if
|
||||
@ -77,6 +98,7 @@ function CreateUserSelectGroup(users = [])
|
||||
|
||||
' Just hide it when done, in case we need to come back
|
||||
group.visible = false
|
||||
return ""
|
||||
end function
|
||||
|
||||
function CreateSigninGroup(user = "")
|
||||
@ -117,7 +139,7 @@ function CreateSigninGroup(user = "")
|
||||
|
||||
group.observeField("backPressed", port)
|
||||
|
||||
while(true)
|
||||
while true
|
||||
msg = wait(0, port)
|
||||
if type(msg) = "roSGScreenEvent" and msg.isScreenClosed()
|
||||
group.visible = false
|
||||
@ -144,6 +166,7 @@ function CreateSigninGroup(user = "")
|
||||
|
||||
' Just hide it when done, in case we need to come back
|
||||
group.visible = false
|
||||
return ""
|
||||
end function
|
||||
|
||||
function CreateHomeGroup()
|
||||
@ -244,26 +267,16 @@ function CreateSearchPage()
|
||||
return group
|
||||
end function
|
||||
|
||||
function CreateSidePanel(buttons, options)
|
||||
sub CreateSidePanel(buttons, options)
|
||||
group = CreateObject("roSGNode", "OptionsSlider")
|
||||
group.buttons = buttons
|
||||
group.options = options
|
||||
|
||||
end function
|
||||
|
||||
function CreatePaginator()
|
||||
group = CreateObject("roSGNode", "Pager")
|
||||
group.id = "paginator"
|
||||
|
||||
group.observeField("pageSelected", m.port)
|
||||
|
||||
return group
|
||||
end function
|
||||
end sub
|
||||
|
||||
function CreateVideoPlayerGroup(video_id, audio_stream_idx = 1)
|
||||
' Video is Playing
|
||||
video = VideoPlayer(video_id, audio_stream_idx)
|
||||
if video = invalid return invalid
|
||||
if video = invalid then return invalid
|
||||
timer = video.findNode("playbackTimer")
|
||||
|
||||
video.observeField("backPressed", m.port)
|
||||
@ -274,46 +287,3 @@ function CreateVideoPlayerGroup(video_id, audio_stream_idx = 1)
|
||||
|
||||
return video
|
||||
end function
|
||||
|
||||
function SeriesLister(group, page_size)
|
||||
sort_order = get_user_setting("series_sort_order", "Ascending")
|
||||
sort_field = get_user_setting("series_sort_field", "SortName")
|
||||
|
||||
item_list = ItemList(group.id, { "limit": page_size,
|
||||
"StartIndex": page_size * (group.pageNumber - 1),
|
||||
"SortBy": sort_field,
|
||||
"SortOrder": sort_order,
|
||||
"IncludeItemTypes": "Series"
|
||||
})
|
||||
group.objects = item_list
|
||||
|
||||
p = group.findNode("paginator")
|
||||
p.maxPages = div_ceiling(group.objects.TotalRecordCount, page_size)
|
||||
end function
|
||||
|
||||
function CollectionLister(group, page_size)
|
||||
sort_order = get_user_setting("boxsets_sort_order", "Ascending")
|
||||
sort_field = get_user_setting("boxsets_sort_field", "SortName")
|
||||
|
||||
item_list = ItemList(group.id, { "limit": page_size,
|
||||
"StartIndex": page_size * (group.pageNumber - 1),
|
||||
"SortBy": sort_field,
|
||||
"SortOrder": sort_order,
|
||||
})
|
||||
group.objects = item_list
|
||||
|
||||
p = group.findNode("paginator")
|
||||
p.maxPages = div_ceiling(group.objects.TotalRecordCount, page_size)
|
||||
end function
|
||||
|
||||
function ChannelLister(group, page_size)
|
||||
sort_order = get_user_setting("channel_sort_order", "Ascending")
|
||||
sort_field = get_user_setting("channel_sort_field", "SortName")
|
||||
group.objects = Channels({ "limit": page_size,
|
||||
"StartIndex": page_size * (group.pageNumber - 1),
|
||||
"SortBy": sort_field,
|
||||
"SortOrder": sort_order,
|
||||
})
|
||||
p = group.findNode("paginator")
|
||||
p.maxPages = div_ceiling(group.objects.TotalRecordCount, page_size)
|
||||
end function
|
||||
|
@ -1,9 +1,11 @@
|
||||
function VideoPlayer(id, audio_stream_idx = 1)
|
||||
function VideoPlayer(id, audio_stream_idx = 1, subtitle_idx = -1)
|
||||
|
||||
' Get video controls and UI
|
||||
video = CreateObject("roSGNode", "JFVideo")
|
||||
video.id = id
|
||||
video = VideoContent(video, audio_stream_idx)
|
||||
if video = invalid
|
||||
AddVideoContent(video, audio_stream_idx, subtitle_idx)
|
||||
|
||||
if video.content = invalid
|
||||
return invalid
|
||||
end if
|
||||
jellyfin_blue = "#00a4dcFF"
|
||||
@ -14,226 +16,130 @@ function VideoPlayer(id, audio_stream_idx = 1)
|
||||
return video
|
||||
end function
|
||||
|
||||
function VideoContent(video, audio_stream_idx = 1) as object
|
||||
' Get video stream
|
||||
sub AddVideoContent(video, audio_stream_idx = 1, subtitle_idx = -1, playbackPosition = -1)
|
||||
|
||||
video.content = createObject("RoSGNode", "ContentNode")
|
||||
params = {}
|
||||
|
||||
meta = ItemMetaData(video.id)
|
||||
if meta = invalid
|
||||
video.content = invalid
|
||||
return
|
||||
end if
|
||||
|
||||
video.content.title = meta.title
|
||||
video.showID = meta.showID
|
||||
|
||||
' If there is a last playback positon, ask user if they want to resume
|
||||
position = meta.json.UserData.PlaybackPositionTicks
|
||||
if position > 0 then
|
||||
dialogResult = startPlayBackOver(position)
|
||||
'Dialog returns -1 when back pressed, 0 for resume, and 1 for start over
|
||||
if dialogResult = -1 then
|
||||
'User pressed back, return invalid and don't load video
|
||||
return invalid
|
||||
else if dialogResult = 1 then
|
||||
'Start Over selected, change position to 0
|
||||
position = 0
|
||||
else if dialogResult = 2 then
|
||||
'Mark this item as watched, refresh the page, and return invalid so we don't load the video
|
||||
MarkItemWatched(video.id)
|
||||
video.content.watched = not video.content.watched
|
||||
group = m.scene.focusedChild
|
||||
group.timeLastRefresh = CreateObject("roDateTime").AsSeconds()
|
||||
group.callFunc("refresh")
|
||||
return invalid
|
||||
end if
|
||||
end if
|
||||
video.content.PlayStart = int(position/10000000)
|
||||
|
||||
playbackInfo = ItemPostPlaybackInfo(video.id, position)
|
||||
|
||||
if playbackInfo = invalid then
|
||||
return invalid
|
||||
end if
|
||||
|
||||
video.PlaySessionId = playbackInfo.PlaySessionId
|
||||
|
||||
if meta.live then
|
||||
video.content.live = true
|
||||
video.content.StreamFormat = "hls"
|
||||
|
||||
'Original MediaSource seems to be a placeholder and real stream data is available
|
||||
'after POSTing to PlaybackInfo
|
||||
json = meta.json
|
||||
json.AddReplace("MediaSources", playbackInfo.MediaSources)
|
||||
json.AddReplace("MediaStreams", playbackInfo.MediaSources[0].MediaStreams)
|
||||
meta.json = json
|
||||
end if
|
||||
|
||||
container = getContainerType(meta)
|
||||
video.container = container
|
||||
|
||||
transcodeParams = getTranscodeParameters(meta, audio_stream_idx)
|
||||
transcodeParams.append({"PlaySessionId": video.PlaySessionId})
|
||||
|
||||
if meta.live then
|
||||
_livestream_params = {
|
||||
"MediaSourceId": playbackInfo.MediaSources[0].Id,
|
||||
"LiveStreamId": playbackInfo.MediaSources[0].LiveStreamId,
|
||||
"MinSegments": 2 'This is a guess about initial buffer size, segments are 3s each
|
||||
}
|
||||
params.append(_livestream_params)
|
||||
transcodeParams.append(_livestream_params)
|
||||
end if
|
||||
|
||||
subtitles = sortSubtitles(meta.id,meta.json.MediaStreams)
|
||||
video.Subtitles = subtitles["all"]
|
||||
video.content.SubtitleTracks = subtitles["text"]
|
||||
|
||||
'TODO: allow user selection of subtitle track before playback initiated, for now set to first track
|
||||
if video.Subtitles.count() then
|
||||
video.SelectedSubtitle = 0
|
||||
else
|
||||
video.SelectedSubtitle = -1
|
||||
end if
|
||||
|
||||
if video.SelectedSubtitle <> -1 and displaySubtitlesByUserConfig(video.Subtitles[video.SelectedSubtitle], meta.json.MediaStreams[audio_stream_idx]) then
|
||||
if video.Subtitles[0].IsTextSubtitleStream then
|
||||
video.subtitleTrack = video.availableSubtitleTracks[video.Subtitles[0].TextIndex].TrackName
|
||||
video.suppressCaptions = false
|
||||
else
|
||||
video.suppressCaptions = true
|
||||
'Watch to see if system overlay opened/closed to change transcoding if caption mode changed
|
||||
m.device.EnableAppFocusEvent(True)
|
||||
video.captionMode = video.globalCaptionMode
|
||||
if video.globalCaptionMode = "On" or (video.globalCaptionMode = "When mute" and m.mute = true) then
|
||||
'Only transcode if subtitles are turned on
|
||||
transcodeParams.append({"SubtitleStreamIndex" : video.Subtitles[0].index })
|
||||
if playbackPosition = -1
|
||||
playbackPosition = meta.json.UserData.PlaybackPositionTicks
|
||||
if playbackPosition > 0
|
||||
dialogResult = startPlayBackOver(playbackPosition)
|
||||
'Dialog returns -1 when back pressed, 0 for resume, and 1 for start over
|
||||
if dialogResult = -1
|
||||
'User pressed back, return invalid and don't load video
|
||||
video.content = invalid
|
||||
return
|
||||
else if dialogResult = 1
|
||||
'Start Over selected, change position to 0
|
||||
playbackPosition = 0
|
||||
else if dialogResult = 2
|
||||
'Mark this item as watched, refresh the page, and return invalid so we don't load the video
|
||||
MarkItemWatched(video.id)
|
||||
video.content.watched = not video.content.watched
|
||||
group = m.scene.focusedChild
|
||||
group.timeLastRefresh = CreateObject("roDateTime").AsSeconds()
|
||||
group.callFunc("refresh")
|
||||
video.content = invalid
|
||||
return
|
||||
end if
|
||||
end if
|
||||
else
|
||||
video.suppressCaptions = true
|
||||
video.SelectedSubtitle = -1
|
||||
end if
|
||||
video.content.PlayStart = int(playbackPosition / 10000000)
|
||||
|
||||
' Call PlayInfo from server
|
||||
mediaSourceId = video.id
|
||||
if meta.live then mediaSourceId = "" ' Don't send mediaSourceId for Live media
|
||||
playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
|
||||
|
||||
video.videoId = video.id
|
||||
video.mediaSourceId = video.id
|
||||
video.audioIndex = audio_stream_idx
|
||||
|
||||
if playbackInfo = invalid
|
||||
video.content = invalid
|
||||
return
|
||||
end if
|
||||
|
||||
video.directPlaySupported = directPlaySupported(meta)
|
||||
video.decodeAudioSupported = decodeAudioSupported(meta, audio_stream_idx)
|
||||
video.transcodeParams = transcodeParams
|
||||
params = {}
|
||||
video.PlaySessionId = playbackInfo.PlaySessionId
|
||||
|
||||
if video.directPlaySupported and video.decodeAudioSupported and transcodeParams.SubtitleStreamIndex = invalid then
|
||||
if meta.live
|
||||
video.content.live = true
|
||||
video.content.StreamFormat = "hls"
|
||||
end if
|
||||
|
||||
video.container = getContainerType(meta)
|
||||
|
||||
subtitles = sortSubtitles(meta.id, playbackInfo.MediaSources[0].MediaStreams)
|
||||
video.Subtitles = subtitles["all"]
|
||||
|
||||
if meta.live
|
||||
video.transcodeParams = {
|
||||
"MediaSourceId": playbackInfo.MediaSources[0].Id,
|
||||
"LiveStreamId": playbackInfo.MediaSources[0].LiveStreamId,
|
||||
"PlaySessionId": video.PlaySessionId
|
||||
}
|
||||
end if
|
||||
|
||||
video.content.SubtitleTracks = subtitles["text"]
|
||||
|
||||
' 'TODO: allow user selection of subtitle track before playback initiated, for now set to no subtitles
|
||||
video.SelectedSubtitle = -1
|
||||
|
||||
video.directPlaySupported = playbackInfo.MediaSources[0].SupportsDirectPlay
|
||||
|
||||
if video.directPlaySupported
|
||||
params.append({
|
||||
"Static": "true",
|
||||
"Container": container,
|
||||
"Container": video.container,
|
||||
"PlaySessionId": video.PlaySessionId,
|
||||
"AudioStreamIndex": audio_stream_idx
|
||||
})
|
||||
video.content.url = buildURL(Substitute("Videos/{0}/stream", video.id), params)
|
||||
video.content.streamformat = container
|
||||
video.content.switchingStrategy = ""
|
||||
video.isTranscode = False
|
||||
video.audioTrack = audio_stream_idx + 1 ' Tell Roku what Audio Track to play (convert from 0 based index for roku)
|
||||
video.isTranscoded = false
|
||||
else
|
||||
video.content.url = buildURL(Substitute("Videos/{0}/master.m3u8", video.id), transcodeParams)
|
||||
|
||||
' Get transcoding reason
|
||||
video.transcodeReasons = getTranscodeReasons(playbackInfo.MediaSources[0].TranscodingUrl)
|
||||
|
||||
video.content.url = buildURL(playbackInfo.MediaSources[0].TranscodingUrl)
|
||||
video.isTranscoded = true
|
||||
end if
|
||||
|
||||
video.content = authorize_request(video.content)
|
||||
|
||||
' todo - audioFormat is read only
|
||||
video.content.audioFormat = getAudioFormat(meta)
|
||||
video.content.setCertificatesFile("common:/certs/ca-bundle.crt")
|
||||
return video
|
||||
|
||||
end sub
|
||||
|
||||
'
|
||||
' Extract array of Transcode Reasons from the content URL
|
||||
' @returns Array of Strings
|
||||
function getTranscodeReasons(url as string) as object
|
||||
|
||||
regex = CreateObject("roRegex", "&TranscodeReasons=([^&]*)", "")
|
||||
match = regex.Match(url)
|
||||
|
||||
if match.count() > 1
|
||||
return match[1].Split(",")
|
||||
end if
|
||||
|
||||
return []
|
||||
end function
|
||||
|
||||
|
||||
function getTranscodeParameters(meta as object, audio_stream_idx = 1)
|
||||
|
||||
params = {"AudioStreamIndex": audio_stream_idx}
|
||||
if decodeAudioSupported(meta, audio_stream_idx) and meta.json.MediaStreams[audio_stream_idx] <> invalid and meta.json.MediaStreams[audio_stream_idx].Type = "Audio" then
|
||||
audioCodec = meta.json.MediaStreams[audio_stream_idx].codec
|
||||
audioChannels = meta.json.MediaStreams[audio_stream_idx].channels
|
||||
else
|
||||
params.Append({"AudioCodec": "aac"})
|
||||
|
||||
' If 5.1 Audio Output is connected then allow transcoding to 5.1
|
||||
di = CreateObject("roDeviceInfo")
|
||||
if di.GetAudioOutputChannel() = "5.1 surround" and di.CanDecodeAudio({ Codec: "aac", ChCnt: 6 }).result then
|
||||
params.Append({"MaxAudioChannels": "6"})
|
||||
else
|
||||
params.Append({"MaxAudioChannels": "2"})
|
||||
end if
|
||||
end if
|
||||
|
||||
streamInfo = {}
|
||||
|
||||
if meta.json.MediaStreams[0] <> invalid and meta.json.MediaStreams[0].codec <> invalid then
|
||||
streamInfo.Codec = meta.json.MediaStreams[0].codec
|
||||
end if
|
||||
|
||||
if meta.json.MediaStreams[0] <> invalid and meta.json.MediaStreams[0].Profile <> invalid and meta.json.MediaStreams[0].Profile.len() > 0 then
|
||||
streamInfo.Profile = LCase(meta.json.MediaStreams[0].Profile)
|
||||
end if
|
||||
if meta.json.MediaSources[0] <> invalid and meta.json.MediaSources[0].container <> invalid and meta.json.MediaSources[0].container.len() > 0 then
|
||||
streamInfo.Container = meta.json.MediaSources[0].container
|
||||
end if
|
||||
|
||||
devinfo = CreateObject("roDeviceInfo")
|
||||
res = devinfo.CanDecodeVideo(streamInfo)
|
||||
|
||||
if res.result = false then
|
||||
params.Append({"VideoCodec": "h264"})
|
||||
streamInfo.Profile = "h264"
|
||||
streamInfo.Container = "ts"
|
||||
end if
|
||||
|
||||
params.Append({"MediaSourceId": meta.id})
|
||||
params.Append({"DeviceId": devinfo.getChannelClientID()})
|
||||
|
||||
return params
|
||||
end function
|
||||
|
||||
'Checks available subtitle tracks and puts subtitles in forced, default, and non-default/forced but preferred language at the top
|
||||
function sortSubtitles(id as string, MediaStreams)
|
||||
tracks = { "forced": [], "default": [], "normal": [] }
|
||||
'Too many args for using substitute
|
||||
dashedid = id.left(8) + "-" + id.mid(8,4) + "-" + id.mid(12,4) + "-" + id.mid(16,4) + "-" + id.right(12)
|
||||
prefered_lang = m.user.Configuration.SubtitleLanguagePreference
|
||||
for each stream in MediaStreams
|
||||
if stream.type = "Subtitle" then
|
||||
'Documentation lists that srt, ttml, and dfxp can be sideloaded but only srt was working in my testing,
|
||||
'forcing srt for all text subtitles
|
||||
url = Substitute("{0}/Videos/{1}/{2}/Subtitles/{3}/0/", get_url(), dashedid, id, stream.index.tostr())
|
||||
url = url + Substitute("Stream.srt?api_key={0}", get_setting("active_user"))
|
||||
stream = {
|
||||
"Track": { "Language" : stream.language, "Description": stream.displaytitle , "TrackName": url },
|
||||
"IsTextSubtitleStream": stream.IsTextSubtitleStream,
|
||||
"Index": stream.index,
|
||||
"TextIndex": -1,
|
||||
"IsDefault": stream.IsDefault,
|
||||
"IsForced": stream.IsForced
|
||||
}
|
||||
if stream.isForced then
|
||||
trackType = "forced"
|
||||
else if stream.IsDefault then
|
||||
trackType = "default"
|
||||
else
|
||||
trackType = "normal"
|
||||
end if
|
||||
if prefered_lang <> "" and prefered_lang = stream.Track.Language then
|
||||
tracks[trackType].unshift(stream)
|
||||
else
|
||||
tracks[trackType].push(stream)
|
||||
end if
|
||||
end if
|
||||
end for
|
||||
tracks["default"].append(tracks["normal"])
|
||||
tracks["forced"].append(tracks["default"])
|
||||
textTracks = []
|
||||
for i = 0 to tracks["forced"].count() - 1
|
||||
if tracks["forced"][i].IsTextSubtitleStream then tracks["forced"][i].TextIndex = textTracks.count()
|
||||
textTracks.push(tracks["forced"][i].Track)
|
||||
end for
|
||||
return { "all" : tracks["forced"], "text": textTracks }
|
||||
end function
|
||||
|
||||
'Opens dialog asking user if they want to resume video or start playback over
|
||||
function startPlayBackOver(time as LongInteger) as integer
|
||||
if m.scene.focusedChild.overhangTitle = "Home" then
|
||||
if m.scene.focusedChild.overhangTitle = "Home"
|
||||
return option_dialog([ "Resume playing at " + ticksToHuman(time) + ".", "Start over from the beginning.", "Watched"])
|
||||
else
|
||||
return option_dialog([ "Resume playing at " + ticksToHuman(time) + ".", "Start over from the beginning."])
|
||||
@ -242,41 +148,30 @@ end function
|
||||
|
||||
function directPlaySupported(meta as object) as boolean
|
||||
devinfo = CreateObject("roDeviceInfo")
|
||||
if meta.json.MediaSources[0] <> invalid and meta.json.MediaSources[0].SupportsDirectPlay = false then
|
||||
if meta.json.MediaSources[0] <> invalid and meta.json.MediaSources[0].SupportsDirectPlay = false
|
||||
return false
|
||||
end if
|
||||
|
||||
if meta.json.MediaStreams[0] = invalid then
|
||||
if meta.json.MediaStreams[0] = invalid
|
||||
return false
|
||||
end if
|
||||
|
||||
streamInfo = { Codec: meta.json.MediaStreams[0].codec }
|
||||
if meta.json.MediaStreams[0].Profile <> invalid and meta.json.MediaStreams[0].Profile.len() > 0 then
|
||||
if meta.json.MediaStreams[0].Profile <> invalid and meta.json.MediaStreams[0].Profile.len() > 0
|
||||
streamInfo.Profile = LCase(meta.json.MediaStreams[0].Profile)
|
||||
end if
|
||||
if meta.json.MediaSources[0].container <> invalid and meta.json.MediaSources[0].container.len() > 0 then
|
||||
if meta.json.MediaSources[0].container <> invalid and meta.json.MediaSources[0].container.len() > 0
|
||||
'CanDecodeVideo() requires the .container to be format: “mp4”, “hls”, “mkv”, “ism”, “dash”, “ts” if its to direct stream
|
||||
if meta.json.MediaSources[0].container = "mov" then
|
||||
if meta.json.MediaSources[0].container = "mov"
|
||||
streamInfo.Container = "mp4"
|
||||
else
|
||||
streamInfo.Container = meta.json.MediaSources[0].container
|
||||
end if
|
||||
end if
|
||||
return devinfo.CanDecodeVideo(streamInfo).result
|
||||
end function
|
||||
|
||||
function decodeAudioSupported(meta as object, audio_stream_idx = 1) as boolean
|
||||
decodeResult = devinfo.CanDecodeVideo(streamInfo)
|
||||
return decodeResult <> invalid and decodeResult.result
|
||||
|
||||
'Check for missing audio and allow playing
|
||||
if meta.json.MediaStreams[audio_stream_idx] = invalid or meta.json.MediaStreams[audio_stream_idx].Type <> "Audio" then return true
|
||||
|
||||
devinfo = CreateObject("roDeviceInfo")
|
||||
codec = meta.json.MediaStreams[audio_stream_idx].codec
|
||||
streamInfo = { Codec: codec, ChCnt: meta.json.MediaStreams[audio_stream_idx].channels }
|
||||
|
||||
'Otherwise check Roku can decode stream and channels
|
||||
canDecode = devinfo.CanDecodeAudio(streamInfo)
|
||||
return canDecode.result
|
||||
end function
|
||||
|
||||
function getContainerType(meta as object) as string
|
||||
@ -313,52 +208,35 @@ function getAudioInfo(meta as object) as object
|
||||
return results
|
||||
end function
|
||||
|
||||
function ReportPlayback(video, state = "update" as string)
|
||||
sub ReportPlayback(video, state = "update" as string)
|
||||
|
||||
if video = invalid or video.position = invalid then return
|
||||
|
||||
params = {
|
||||
"PlaySessionId": video.PlaySessionId,
|
||||
"PositionTicks": int(video.position) * 10000000&, 'Ensure a LongInteger is used
|
||||
"IsPaused": (video.state = "paused"),
|
||||
}
|
||||
if video.content.live then
|
||||
if video.content.live
|
||||
params.append({
|
||||
"MediaSourceId": video.transcodeParams.MediaSourceId,
|
||||
"LiveStreamId": video.transcodeParams.LiveStreamId
|
||||
})
|
||||
end if
|
||||
PlaystateUpdate(video.id, state, params)
|
||||
end function
|
||||
end sub
|
||||
|
||||
function StopPlayback()
|
||||
sub StopPlayback()
|
||||
video = m.scene.focusedchild
|
||||
if video.state = "finished" then MarkItemWatched(video.id)
|
||||
video.control = "stop"
|
||||
m.device.EnableAppFocusEvent(False)
|
||||
video.findNode("playbackTimer").control = "stop"
|
||||
ReportPlayback(video, "stop")
|
||||
end function
|
||||
|
||||
function displaySubtitlesByUserConfig(subtitleTrack, audioTrack)
|
||||
subtitleMode = m.user.Configuration.SubtitleMode
|
||||
audioLanguagePreference = m.user.Configuration.AudioLanguagePreference
|
||||
subtitleLanguagePreference = m.user.Configuration.SubtitleLanguagePreference
|
||||
if subtitleMode = "Default"
|
||||
return (subtitleTrack.isForced or subtitleTrack.isDefault)
|
||||
else if subtitleMode = "Smart"
|
||||
return (audioLanguagePreference <> "" and audioTrack.Language <> invalid and subtitleLanguagePreference <> "" and subtitleTrack.Track.Language <> invalid and subtitleLanguagePreference = subtitleTrack.Track.Language and audioLanguagePreference <> audioTrack.Language)
|
||||
else if subtitleMode = "OnlyForced"
|
||||
return subtitleTrack.IsForced
|
||||
else if subtitleMode = "Always"
|
||||
return true
|
||||
else if subtitleMode = "None"
|
||||
return false
|
||||
else
|
||||
return false
|
||||
end if
|
||||
end function
|
||||
end sub
|
||||
|
||||
function autoPlayNextEpisode(videoID as string, showID as string)
|
||||
' use web client setting
|
||||
if m.user.Configuration.EnableNextEpisodeAutoPlay then
|
||||
if m.user.Configuration.EnableNextEpisodeAutoPlay
|
||||
' query API for next episode ID
|
||||
url = Substitute("Shows/{0}/Episodes", showID)
|
||||
urlParams = { "UserId": get_setting("active_user")}
|
||||
@ -367,7 +245,7 @@ function autoPlayNextEpisode(videoID as string, showID as string)
|
||||
resp = APIRequest(url, urlParams)
|
||||
data = getJson(resp)
|
||||
|
||||
if data <> invalid and data.Items.Count() = 2 then
|
||||
if data <> invalid and data.Items.Count() = 2
|
||||
' remove finished video node
|
||||
n = m.scene.getChildCount() - 1
|
||||
m.scene.removeChildIndex(n)
|
||||
|
@ -1,9 +0,0 @@
|
||||
function api_BrandingConfig()
|
||||
' Gets branding configuration
|
||||
' {
|
||||
' LoginDisclaimer: string
|
||||
' CustomCss: string
|
||||
' }
|
||||
resp = APIRequest("Branding/Configuration")
|
||||
return getJson(resp)
|
||||
end function
|
@ -37,15 +37,15 @@ end function
|
||||
|
||||
function ImageURL(id, version = "Primary", params = {})
|
||||
' set defaults
|
||||
if params.maxHeight = invalid then
|
||||
if params.maxHeight = invalid
|
||||
param = { "maxHeight" : "384" }
|
||||
params.append(param)
|
||||
end if
|
||||
if params.maxWidth = invalid then
|
||||
if params.maxWidth = invalid
|
||||
param = { "maxWidth" : "196" }
|
||||
params.append(param)
|
||||
end if
|
||||
if params.quality = invalid then
|
||||
if params.quality = invalid
|
||||
param = { "quality" : "90" }
|
||||
params.append(param)
|
||||
end if
|
||||
@ -56,13 +56,13 @@ end function
|
||||
|
||||
function UserImageURL(id, params = {})
|
||||
' set defaults
|
||||
if params.maxHeight = invalid then
|
||||
if params.maxHeight = invalid
|
||||
params.append({ "maxHeight" : "300" })
|
||||
end if
|
||||
if params.maxWidth = invalid then
|
||||
if params.maxWidth = invalid
|
||||
params.append({ "maxWidth" : "300" })
|
||||
end if
|
||||
if params.quality = invalid then
|
||||
if params.quality = invalid
|
||||
params.append({ "quality" : "90" })
|
||||
end if
|
||||
|
||||
|
@ -1,31 +1,7 @@
|
||||
function ItemsList(params = {} as object)
|
||||
' Gets items based on a query.
|
||||
resp = APIRequest("Items", params)
|
||||
data = getJson(resp)
|
||||
' TODO - parse items
|
||||
return data
|
||||
end function
|
||||
|
||||
function UserItems(params = {} as object)
|
||||
' Gets items based on a query
|
||||
resp = APIRequest(Substitute("Items/{0}/Items", get_setting("active_user")), params)
|
||||
data = getJson(resp)
|
||||
' TODO - parse items
|
||||
return data
|
||||
end function
|
||||
|
||||
function UserItemsResume(params = {} as object)
|
||||
' Gets items based on a query
|
||||
resp = APIRequest(Substitute("Items/{0}/Items/Resume", get_setting("active_user")), params)
|
||||
data = getJson(resp)
|
||||
' TODO - parse items
|
||||
return data
|
||||
end function
|
||||
|
||||
function ItemGetPlaybackInfo(id as string, StartTimeTicks = 0 as longinteger)
|
||||
function ItemGetPlaybackInfo(id as string, startTimeTicks = 0 as longinteger)
|
||||
params = {
|
||||
"UserId": get_setting("active_user"),
|
||||
"StartTimeTicks": StartTimeTicks,
|
||||
"StartTimeTicks": startTimeTicks,
|
||||
"IsPlayback": true,
|
||||
"AutoOpenLiveStream": true,
|
||||
"MaxStreamingBitrate": "140000000"
|
||||
@ -34,17 +10,24 @@ function ItemGetPlaybackInfo(id as string, StartTimeTicks = 0 as longinteger)
|
||||
return getJson(resp)
|
||||
end function
|
||||
|
||||
function ItemPostPlaybackInfo(id as string, StartTimeTicks = 0 as longinteger)
|
||||
function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string , audioTrackIndex = -1 as integer, subtitleTrackIndex = -1 as integer, startTimeTicks = 0 as longinteger)
|
||||
body = {
|
||||
"DeviceProfile": getDeviceProfile()
|
||||
}
|
||||
params = {
|
||||
"UserId": get_setting("active_user"),
|
||||
"StartTimeTicks": StartTimeTicks,
|
||||
"StartTimeTicks": startTimeTicks,
|
||||
"IsPlayback": true,
|
||||
"AutoOpenLiveStream": true,
|
||||
"MaxStreamingBitrate": "140000000"
|
||||
"MaxStreamingBitrate": "140000000",
|
||||
"MaxStaticBitrate": "140000000",
|
||||
"SubtitleStreamIndex": subtitleTrackIndex
|
||||
}
|
||||
|
||||
if mediaSourceId <> "" then params.MediaSourceId = mediaSourceId
|
||||
|
||||
if audioTrackIndex > -1 then params.AudioStreamIndex = audioTrackIndex
|
||||
|
||||
req = APIRequest(Substitute("Items/{0}/PlaybackInfo", id), params)
|
||||
req.SetRequest("POST")
|
||||
return postJson(req, FormatJson(body))
|
||||
@ -63,11 +46,12 @@ function SearchMedia(query as string)
|
||||
"IncludeGenres": false,
|
||||
"IncludeStudios": false,
|
||||
"IncludeArtists": false,
|
||||
' "IncludeItemTypes: "Movie",
|
||||
"IncludeItemTypes": "TvChannel,Movie,BoxSet,Series,Episode,Video"
|
||||
"EnableTotalRecordCount": false,
|
||||
"ImageTypeLimit": 1,
|
||||
"Recursive": true
|
||||
})
|
||||
|
||||
data = getJson(resp)
|
||||
results = []
|
||||
for each item in data.Items
|
||||
@ -80,94 +64,14 @@ function SearchMedia(query as string)
|
||||
return data
|
||||
end function
|
||||
|
||||
' List items from within a library
|
||||
function ItemList(library_id = invalid as string, params = {})
|
||||
if params["limit"] = invalid
|
||||
params["limit"] = 30
|
||||
end if
|
||||
if params["page"] = invalid
|
||||
params["page"] = 1
|
||||
end if
|
||||
params["parentid"] = library_id
|
||||
params["recursive"] = true
|
||||
|
||||
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
results = []
|
||||
for each item in data.Items
|
||||
imgParams = {}
|
||||
if item.ImageTags.Primary <> invalid then
|
||||
' If Primary image exists use it
|
||||
param = { "Tag" : item.ImageTags.Primary }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
param = { "AddPlayedIndicator": item.UserData.Played }
|
||||
imgParams.Append(param)
|
||||
if item.UserData.PlayedPercentage <> invalid then
|
||||
param = { "PercentPlayed": item.UserData.PlayedPercentage }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
if item.type = "Movie"
|
||||
tmp = CreateObject("roSGNode", "MovieData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
else if item.type = "Series"
|
||||
if item.UserData.UnplayedItemCount > 0 then
|
||||
param = { "UnplayedCount" : item.UserData.UnplayedItemCount }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
tmp = CreateObject("roSGNode", "SeriesData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
else if item.type = "BoxSet"
|
||||
if item.UserData.UnplayedItemCount > 0 then
|
||||
param = { "UnplayedCount" : item.UserData.UnplayedItemCount }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
tmp = CreateObject("roSGNode", "CollectionData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
else if item.type = "Episode"
|
||||
imgParams.Append({ "AddPlayedIndicator": item.UserData.Played })
|
||||
tmp = CreateObject("roSGNode", "TVEpisodeData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
else if item.type = "MusicAlbum"
|
||||
tmp = CreateObject("roSGNode", "AlbumData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
else if item.type = "Video"
|
||||
tmp = CreateObject("roSGNode", "VideoData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
else if item.type = "Folder"
|
||||
tmp = CreateObject("roSGNode", "FolderData")
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
else
|
||||
print "Items.brs::ItemList received unhandled type: " item.type
|
||||
' Otherwise we just stick with the JSON
|
||||
results.push(item)
|
||||
end if
|
||||
end for
|
||||
data.items = results
|
||||
return data
|
||||
end function
|
||||
|
||||
' MetaData about an item
|
||||
function ItemMetaData(id as string)
|
||||
url = Substitute("Users/{0}/Items/{1}", get_setting("active_user"), id)
|
||||
resp = APIRequest(url)
|
||||
data = getJson(resp)
|
||||
if data = invalid then return invalid
|
||||
imgParams = {}
|
||||
if data.UserData.PlayedPercentage <> invalid then
|
||||
if data.UserData.PlayedPercentage <> invalid
|
||||
param = { "PercentPlayed": data.UserData.PlayedPercentage }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
@ -191,7 +95,7 @@ function ItemMetaData(id as string)
|
||||
else if data.type = "BoxSet"
|
||||
tmp = CreateObject("roSGNode", "CollectionData")
|
||||
tmp.image = PosterImage(data.id, imgParams)
|
||||
tmp.json = item
|
||||
tmp.json = data
|
||||
return tmp
|
||||
else if data.type = "Season"
|
||||
tmp = CreateObject("roSGNode", "TVSeasonData")
|
||||
@ -213,7 +117,6 @@ function ItemMetaData(id as string)
|
||||
' Return json if we don't know what it is
|
||||
return data
|
||||
end if
|
||||
return data
|
||||
end function
|
||||
|
||||
' Seasons for a TV Show
|
||||
@ -225,7 +128,7 @@ function TVSeasons(id as string)
|
||||
results = []
|
||||
for each item in data.Items
|
||||
imgParams = { "AddPlayedIndicator": item.UserData.Played }
|
||||
if item.UserData.UnplayedItemCount > 0 then
|
||||
if item.UserData.UnplayedItemCount > 0
|
||||
param = { "UnplayedCount" : item.UserData.UnplayedItemCount }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
@ -246,7 +149,7 @@ function TVEpisodes(show_id as string, season_id as string)
|
||||
results = []
|
||||
for each item in data.Items
|
||||
imgParams = { "AddPlayedIndicator": item.UserData.Played, "maxWidth": 712, "maxheight": 400 }
|
||||
if item.UserData.PlayedPercentage <> invalid then
|
||||
if item.UserData.PlayedPercentage <> invalid
|
||||
param = { "PercentPlayed": item.UserData.PlayedPercentage }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
@ -262,42 +165,3 @@ function TVEpisodes(show_id as string, season_id as string)
|
||||
data.Items = results
|
||||
return data
|
||||
end function
|
||||
|
||||
' The next up episode for a TV show
|
||||
function TVNext(id as string)
|
||||
url = Substitute("Shows/NextUp", id)
|
||||
resp = APIRequest(url, { "UserId": get_setting("active_user"), "SeriesId": id })
|
||||
|
||||
data = getJson(resp)
|
||||
for each item in data.Items
|
||||
item.image = PosterImage(item.id)
|
||||
end for
|
||||
return data
|
||||
end function
|
||||
|
||||
function Channels(params = {})
|
||||
if params["limit"] = invalid
|
||||
params["limit"] = 30
|
||||
end if
|
||||
if params["page"] = invalid
|
||||
params["page"] = 1
|
||||
end if
|
||||
params["recursive"] = true
|
||||
|
||||
resp = APIRequest("LiveTv/Channels", params)
|
||||
|
||||
data = getJson(resp)
|
||||
results = []
|
||||
for each item in data.Items
|
||||
imgParams = { "maxWidth": 712, "maxheight": 400 }
|
||||
tmp = CreateObject("roSGNode", "ChannelData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
if tmp.image <> invalid
|
||||
tmp.image.posterDisplayMode = "scaleToFit"
|
||||
end if
|
||||
tmp.json = item
|
||||
results.push(tmp)
|
||||
end for
|
||||
data.Items = results
|
||||
return data
|
||||
end function
|
||||
|
@ -1,19 +0,0 @@
|
||||
function ItemCounts()
|
||||
' Gets counts of a library
|
||||
' Query:
|
||||
' UserId: Get counts from specific user's library
|
||||
' IsFavorite: Get counts of favorite items
|
||||
resp = APIRequest("Items/Counts", {})
|
||||
data = getJson(resp)
|
||||
return data
|
||||
end function
|
||||
|
||||
function LibraryMediaFolders()
|
||||
' Gets all user media folders
|
||||
' Query:
|
||||
' IsHidden: Filter by folders that are marked hidden, or not
|
||||
resp = APIRequest("Library/MediaFolders")
|
||||
data = getJson(resp)
|
||||
return data
|
||||
|
||||
end function
|
@ -1,10 +1,13 @@
|
||||
function PlaystateUpdate(id, state as string, params = {})
|
||||
if state = "start" then
|
||||
if state = "start"
|
||||
url = "Sessions/Playing"
|
||||
else if state = "stop" then
|
||||
else if state = "stop"
|
||||
url = "Sessions/Playing/Stopped"
|
||||
else if state = "update"
|
||||
url = "Sessions/Playing/Progress"
|
||||
else
|
||||
' Unknow State
|
||||
return {}
|
||||
end if
|
||||
params = PlaystateDefaults(id, params)
|
||||
resp = APIRequest(url)
|
||||
@ -40,10 +43,9 @@ function PlaystateDefaults(id="" as string, params={} as object)
|
||||
return FormatJson(new_params)
|
||||
end function
|
||||
|
||||
function deleteTranscode(id)
|
||||
sub deleteTranscode(id)
|
||||
devinfo = CreateObject("roDeviceInfo")
|
||||
device_id = devinfo.getChannelClientID()
|
||||
req = APIRequest("/Videos/ActiveEncodings", { "deviceID" : devinfo.getChannelClientID(), "PlaySessionId": id })
|
||||
req.setRequest("DELETE")
|
||||
postVoid(req)
|
||||
end function
|
||||
end sub
|
||||
|
@ -11,9 +11,8 @@ function UnmarkItemFavorite(id as String)
|
||||
return getJson(resp)
|
||||
end function
|
||||
|
||||
function MarkItemWatched(id as String)
|
||||
sub MarkItemWatched(id as String)
|
||||
date = CreateObject("roDateTime")
|
||||
date.toLocalTime()
|
||||
dateStr = stri(date.getYear()).trim()
|
||||
dateStr += leftPad(stri(date.getMonth()).trim(), "0", 2)
|
||||
dateStr += leftPad(stri(date.getDayOfMonth()).trim(), "0", 2)
|
||||
@ -22,9 +21,8 @@ function MarkItemWatched(id as String)
|
||||
dateStr += leftPad(stri(date.getSeconds()).trim(), "0", 2)
|
||||
|
||||
url = Substitute("Users/{0}/PlayedItems/{1}", get_setting("active_user"), id)
|
||||
resp = APIRequest(url, {"DatePlayed": dateStr})
|
||||
data = postJson(resp)
|
||||
end function
|
||||
APIRequest(url, {"DatePlayed": dateStr})
|
||||
end sub
|
||||
|
||||
function UnmarkItemWatched(id as String)
|
||||
url = Substitute("Users/{0}/PlayedItems/{1}", get_setting("active_user"), id)
|
||||
|
@ -5,6 +5,7 @@ function buildParams(params={} as Object) as string
|
||||
|
||||
param_array = []
|
||||
for each field in params.items()
|
||||
item = ""
|
||||
if type(field.value) = "String" or type(field.value) = "roString"
|
||||
item = field.key + "=" + req.escape(field.value.trim())
|
||||
'item = field.key + "=" + field.value.trim()
|
||||
@ -30,15 +31,22 @@ function buildParams(params={} as Object) as string
|
||||
item = field.key + "=" + req.escape(field.value)
|
||||
'item = field.key + "=" + field.value
|
||||
end if
|
||||
param_array.push(item)
|
||||
|
||||
if item <> "" then param_array.push(item)
|
||||
end for
|
||||
|
||||
return param_array.join("&")
|
||||
end function
|
||||
|
||||
function buildURL(path as String, params={} as Object) as string
|
||||
|
||||
full_url = get_url() + "/" + path
|
||||
|
||||
' Add intial '/' if path does not start with one
|
||||
if path.Left(1) = "/"
|
||||
full_url = get_url() + path
|
||||
else
|
||||
full_url = get_url() + "/" + path
|
||||
end if
|
||||
|
||||
if params.count() > 0
|
||||
full_url = full_url + "?" + buildParams(params)
|
||||
end if
|
||||
@ -136,7 +144,7 @@ function authorize_request(request)
|
||||
auth = auth + ", Version=" + Chr(34) + version + Chr(34)
|
||||
|
||||
user = get_setting("active_user")
|
||||
if user <> invalid and user <> "" then
|
||||
if user <> invalid and user <> ""
|
||||
auth = auth + ", UserId=" + Chr(34) + user + Chr(34)
|
||||
end if
|
||||
|
||||
|
@ -2,10 +2,8 @@ function get_token(user as String, password as String)
|
||||
url = "Users/AuthenticateByName?format=json"
|
||||
req = APIRequest(url)
|
||||
|
||||
encPass = CreateObject("roUrlTransfer")
|
||||
json = postJson(req, FormatJson({ "Username": user, "Pw": password }))
|
||||
|
||||
|
||||
if json = invalid then return invalid
|
||||
|
||||
userdata = CreateObject("roSGNode", "UserData")
|
||||
@ -23,43 +21,91 @@ function AboutMe()
|
||||
return getJson(resp)
|
||||
end function
|
||||
|
||||
function SignOut()
|
||||
sub SignOut()
|
||||
if get_setting("active_user") <> invalid
|
||||
unset_user_setting("token")
|
||||
unset_setting("username")
|
||||
unset_setting("password")
|
||||
end if
|
||||
unset_setting("active_user")
|
||||
m.overhang.currentUser = ""
|
||||
m.overhang.showOptions = false
|
||||
m.scene.unobserveField("optionsPressed")
|
||||
end function
|
||||
end sub
|
||||
|
||||
function AvailableUsers()
|
||||
users = parseJson(get_setting("available_users", "[]"))
|
||||
return users
|
||||
end function
|
||||
|
||||
function PickUser(id as string)
|
||||
sub PickUser(id as string)
|
||||
this_user = invalid
|
||||
for each user in AvailableUsers()
|
||||
if user.id = id then this_user = user
|
||||
end for
|
||||
if this_user = invalid then return invalid
|
||||
if this_user = invalid then return
|
||||
set_setting("active_user", this_user.id)
|
||||
set_setting("server", this_user.server)
|
||||
end function
|
||||
end sub
|
||||
|
||||
function RemoveUser(id as string)
|
||||
sub RemoveUser(id as string)
|
||||
user = CreateObject("roSGNode", "UserData")
|
||||
user.id = id
|
||||
user.callFunc("removeFromRegistry")
|
||||
|
||||
if get_setting("active_user") = id then SignOut()
|
||||
end function
|
||||
end sub
|
||||
|
||||
function ServerInfo()
|
||||
url = "System/Info/Public"
|
||||
resp = APIRequest(url)
|
||||
return getJson(resp)
|
||||
req = APIRequest(url)
|
||||
|
||||
req.setMessagePort(CreateObject("roMessagePort"))
|
||||
req.AsyncGetToString()
|
||||
|
||||
' wait 15 seconds for a server response
|
||||
resp = wait(35000, req.GetMessagePort())
|
||||
|
||||
' handle unknown errors
|
||||
if type(resp) <> "roUrlEvent"
|
||||
return { "Error": true, "ErrorMessage": "Unknown" }
|
||||
end if
|
||||
|
||||
' check for a location redirect header in the response
|
||||
headers = resp.GetResponseHeaders()
|
||||
if headers <> invalid and headers.location <> invalid
|
||||
|
||||
' only follow redirect if it the API Endpoint path is the same (/System/Info/Public)
|
||||
' set the server to new location and try again
|
||||
if right(headers.location, 19) = "/System/Info/Public"
|
||||
set_setting("server", left(headers.location, len(headers.location) - 19))
|
||||
info = ServerInfo()
|
||||
if info.Error
|
||||
info.UpdatedUrl = left(headers.location, len(headers.location) - 19)
|
||||
info.ErrorMessage = info.ErrorMessage + " (Note: Server redirected us to " + info.UpdatedUrl + ")"
|
||||
end if
|
||||
return info
|
||||
end if
|
||||
end if
|
||||
|
||||
' handle any non 200 responses, returning the error code and message
|
||||
if resp.GetResponseCode() <> 200
|
||||
return { "Error": true, "ErrorCode": resp.GetResponseCode(), "ErrorMessage": resp.GetFailureReason() }
|
||||
end if
|
||||
|
||||
' return the parsed response string
|
||||
responseString = resp.GetString()
|
||||
if responseString <> invalid and responseString <> ""
|
||||
result = ParseJson(responseString)
|
||||
if result <> invalid
|
||||
result.Error = false
|
||||
return result
|
||||
end if
|
||||
end if
|
||||
|
||||
' otherwise return error message
|
||||
return { "Error": true, "ErrorMessage": "Does not appear to be a Jellyfin Server" }
|
||||
|
||||
end function
|
||||
|
||||
function GetPublicUsers()
|
||||
@ -77,7 +123,7 @@ sub LoadUserPreferences()
|
||||
resp = APIRequest(url)
|
||||
jsonResponse = getJson(resp)
|
||||
|
||||
if jsonResponse <> invalid and jsonResponse.CustomPrefs <> invalid and jsonResponse.CustomPrefs["landing-livetv"] <> invalid then
|
||||
if jsonResponse <> invalid and jsonResponse.CustomPrefs <> invalid and jsonResponse.CustomPrefs["landing-livetv"] <> invalid
|
||||
set_user_setting("display.livetv.landing", jsonResponse.CustomPrefs["landing-livetv"])
|
||||
else
|
||||
unset_user_setting("display.livetv.landing")
|
||||
|
@ -1,3 +1,142 @@
|
||||
|
||||
function selectSubtitleTrack(tracks, current = -1) as integer
|
||||
video = m.scene.focusedChild
|
||||
trackSelected = selectSubtitleTrackDialog(video.Subtitles, video.SelectedSubtitle)
|
||||
if trackSelected = invalid or trackSelected = -1 ' back pressed in Dialog - no selection made
|
||||
return -2
|
||||
else
|
||||
return trackSelected - 1
|
||||
end if
|
||||
end function
|
||||
|
||||
' Present Dialog to user to select subtitle track
|
||||
function selectSubtitleTrackDialog(tracks, currentTrack = -1)
|
||||
iso6392 = getSubtitleLanguages()
|
||||
options = ["None"]
|
||||
for each item in tracks
|
||||
forced = ""
|
||||
default = ""
|
||||
if item.IsForced then forced = " [Forced]"
|
||||
if item.IsDefault then default = " - Default"
|
||||
if item.Track.Language <> invalid
|
||||
language = iso6392.lookup(item.Track.Language)
|
||||
if language = invalid then language = item.Track.Language
|
||||
else
|
||||
language = "Undefined"
|
||||
end if
|
||||
options.push(language + forced + default)
|
||||
end for
|
||||
return option_dialog(options, "Select a subtitle track", currentTrack + 1)
|
||||
end function
|
||||
|
||||
sub changeSubtitleDuringPlayback(newid)
|
||||
|
||||
' If no subtitles set
|
||||
if newid = invalid or newid = -1
|
||||
turnoffSubtitles()
|
||||
return
|
||||
end if
|
||||
|
||||
video = m.scene.focusedChild
|
||||
|
||||
' If no change of subtitle track, return
|
||||
if newid = video.SelectedSubtitle then return
|
||||
|
||||
currentSubtitles = video.Subtitles[video.SelectedSubtitle]
|
||||
newSubtitles = video.Subtitles[newid]
|
||||
|
||||
if newSubtitles.IsEncoded
|
||||
|
||||
' Switching to Encoded Subtitle stream
|
||||
video.control = "stop"
|
||||
AddVideoContent(video, video.audioIndex, newSubtitles.Index, video.position * 10000000)
|
||||
video.control = "play"
|
||||
video.globalCaptionMode = "Off" ' Using encoded subtitles - so turn off text subtitles
|
||||
|
||||
else if currentSubtitles <> invalid AND currentSubtitles.IsEncoded
|
||||
|
||||
' Switching from an Encoded stream to a text stream
|
||||
video.control = "stop"
|
||||
AddVideoContent(video, video.audioIndex, -1, video.position * 10000000)
|
||||
video.control = "play"
|
||||
video.globalCaptionMode = "On"
|
||||
video.subtitleTrack = video.availableSubtitleTracks[newSubtitles.TextIndex].TrackName
|
||||
|
||||
else
|
||||
|
||||
' Switch to Text Subtitle Track
|
||||
video.globalCaptionMode = "On"
|
||||
video.subtitleTrack = video.availableSubtitleTracks[newSubtitles.TextIndex].TrackName
|
||||
end if
|
||||
|
||||
video.SelectedSubtitle = newid
|
||||
|
||||
end sub
|
||||
|
||||
sub turnoffSubtitles()
|
||||
video = m.scene.focusedChild
|
||||
current = video.SelectedSubtitle
|
||||
video.SelectedSubtitle = -1
|
||||
video.globalCaptionMode = "Off"
|
||||
m.device.EnableAppFocusEvent(false)
|
||||
' Check if Enoded subtitles are being displayed, and turn off
|
||||
if current > -1 and video.Subtitles[current].IsEncoded
|
||||
video.control = "stop"
|
||||
AddVideoContent(video, video.audioIndex, -1, video.position * 10000000)
|
||||
video.control = "play"
|
||||
end if
|
||||
end sub
|
||||
|
||||
'Checks available subtitle tracks and puts subtitles in forced, default, and non-default/forced but preferred language at the top
|
||||
function sortSubtitles(id as string, MediaStreams)
|
||||
tracks = { "forced": [], "default": [], "normal": [] }
|
||||
'Too many args for using substitute
|
||||
prefered_lang = m.user.Configuration.SubtitleLanguagePreference
|
||||
for each stream in MediaStreams
|
||||
if stream.type = "Subtitle"
|
||||
|
||||
url = ""
|
||||
if stream.DeliveryUrl <> invalid
|
||||
url = buildURL(stream.DeliveryUrl)
|
||||
end if
|
||||
|
||||
stream = {
|
||||
"Track": { "Language" : stream.language, "Description": stream.displaytitle , "TrackName": url },
|
||||
"IsTextSubtitleStream": stream.IsTextSubtitleStream,
|
||||
"Index": stream.index,
|
||||
"IsDefault": stream.IsDefault,
|
||||
"IsForced": stream.IsForced,
|
||||
"IsExternal": stream.IsExternal
|
||||
"IsEncoded": stream.DeliveryMethod = "Encode"
|
||||
}
|
||||
if stream.isForced
|
||||
trackType = "forced"
|
||||
else if stream.IsDefault
|
||||
trackType = "default"
|
||||
else
|
||||
trackType = "normal"
|
||||
end if
|
||||
if prefered_lang <> "" and prefered_lang = stream.Track.Language
|
||||
tracks[trackType].unshift(stream)
|
||||
else
|
||||
tracks[trackType].push(stream)
|
||||
end if
|
||||
end if
|
||||
end for
|
||||
|
||||
tracks["default"].append(tracks["normal"])
|
||||
tracks["forced"].append(tracks["default"])
|
||||
|
||||
textTracks = []
|
||||
for i = 0 to tracks["forced"].count() - 1
|
||||
if tracks["forced"][i].IsTextSubtitleStream
|
||||
tracks["forced"][i].TextIndex = textTracks.count()
|
||||
textTracks.push(tracks["forced"][i].Track)
|
||||
end if
|
||||
end for
|
||||
return { "all" : tracks["forced"], "text": textTracks }
|
||||
end function
|
||||
|
||||
function getSubtitleLanguages()
|
||||
return {
|
||||
"aar": "Afar",
|
||||
|
@ -1,164 +0,0 @@
|
||||
function selectSubtitleTrack(tracks, current = -1)
|
||||
video = m.scene.focusedChild
|
||||
trackSelected = selectSubtitleTrackDialog(video.Subtitles, video.SelectedSubtitle)
|
||||
if trackSelected = -1 then
|
||||
return invalid
|
||||
else
|
||||
return trackSelected - 1
|
||||
end if
|
||||
end function
|
||||
|
||||
function selectSubtitleTrackDialog(tracks, currentTrack = -1)
|
||||
iso6392 = getSubtitleLanguages()
|
||||
options = ["None"]
|
||||
for each item in tracks
|
||||
forced = ""
|
||||
default = ""
|
||||
if item.IsForced then forced = " [Forced]"
|
||||
if item.IsDefault then default = " - Default"
|
||||
if item.Track.Language <> invalid then
|
||||
language = iso6392.lookup(item.Track.Language)
|
||||
if language = invalid then language = item.Track.Language
|
||||
else
|
||||
language = "Undefined"
|
||||
end if
|
||||
options.push(language + forced + default)
|
||||
end for
|
||||
return option_dialog(options, "Select a subtitle track", currentTrack + 1)
|
||||
end function
|
||||
|
||||
sub changeSubtitleDuringPlayback(newid)
|
||||
if newid = invalid then return
|
||||
if newid = -1 then
|
||||
turnoffSubtitles()
|
||||
return
|
||||
end if
|
||||
|
||||
video = m.scene.focusedChild
|
||||
oldTrack = video.Subtitles[video.SelectedSubtitle]
|
||||
newTrack = video.Subtitles[newid]
|
||||
|
||||
video.captionMode = video.globalCaptionMode
|
||||
m.device.EnableAppFocusEvent(not newTrack.IsTextSubtitleStream)
|
||||
video.SelectedSubtitle = newid
|
||||
|
||||
if newTrack.IsTextSubtitleStream then
|
||||
if video.content.PlayStart > video.position
|
||||
'User has rewinded to before playback was initiated. The Roku never loaded this portion of the text subtitle
|
||||
'Changing the track will cause plaback to jump to initial bookmark position.
|
||||
video.suppressCaptions = true
|
||||
rebuildURL(false)
|
||||
end if
|
||||
video.subtitleTrack = video.availableSubtitleTracks[newTrack.TextIndex].TrackName
|
||||
video.suppressCaptions = false
|
||||
else
|
||||
video.suppressCaptions = true
|
||||
end if
|
||||
|
||||
'Rebuild URL if subtitle track is video or if changed from video subtitle to text subtitle.
|
||||
if not newTrack.IsTextSubtitleStream then
|
||||
rebuildURL(true)
|
||||
else if oldTrack <> invalid and not oldTrack.IsTextSubtitleStream then
|
||||
rebuildURL(false)
|
||||
if newTrack.TextIndex > 0 then video.subtitleTrack = video.availableSubtitleTracks[newTrack.TextIndex].TrackName
|
||||
end if
|
||||
end sub
|
||||
|
||||
function turnoffSubtitles()
|
||||
video = m.scene.focusedChild
|
||||
current = video.SelectedSubtitle
|
||||
video.SelectedSubtitle = -1
|
||||
video.suppressCaptions = true
|
||||
m.device.EnableAppFocusEvent(false)
|
||||
if current > -1 and not video.Subtitles[current].IsTextSubtitleStream then
|
||||
rebuildURL(false)
|
||||
end if
|
||||
end function
|
||||
|
||||
function systemOverlayClosed()
|
||||
video = m.scene.focusedChild
|
||||
if video.globalCaptionMode <> video.captionMode then
|
||||
video.captionMode = video.globalCaptionMode
|
||||
reviewSubtitleDisplay()
|
||||
end if
|
||||
end function
|
||||
|
||||
function reviewSubtitleDisplay()
|
||||
'TODO handle changing subtitles tracks during playback
|
||||
displayed = areSubtitlesDisplayed()
|
||||
needed = areSubtitlesNeeded()
|
||||
print "displayed: " displayed " needed: " needed
|
||||
if areSubtitlesNeeded() and (not areSubtitlesDisplayed()) then
|
||||
rebuildURL(true)
|
||||
else if areSubtitlesDisplayed() and (not areSubtitlesNeeded()) then
|
||||
rebuildURL(false)
|
||||
end if
|
||||
end function
|
||||
|
||||
function areSubtitlesDisplayed()
|
||||
index = m.scene.focusedChild.transcodeParams.lookup("SubtitleStreamIndex")
|
||||
if index <> invalid and index <> -1 then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end if
|
||||
end function
|
||||
|
||||
function areSubtitlesNeeded()
|
||||
captions = m.scene.focusedChild.globalCaptionMode
|
||||
if captions = "On"
|
||||
return true
|
||||
else if captions = "Off"
|
||||
return false
|
||||
else if captions = "When mute"
|
||||
return m.mute
|
||||
else if captions = "Instant replay"
|
||||
'Unsupported. Do we want to do this? Is it worth transcoding for rewinded content and then untranscoding?
|
||||
return false
|
||||
end if
|
||||
end function
|
||||
|
||||
sub rebuildURL(captions as boolean)
|
||||
playBackBuffer = -5
|
||||
|
||||
video = m.scene.focusedChild
|
||||
video.control = "pause"
|
||||
|
||||
tmpParams = video.transcodeParams
|
||||
if captions = false then
|
||||
tmpParams.delete("SubtitleStreamIndex")
|
||||
else
|
||||
if video.Subtitles[video.SelectedSubtitle] <> invalid then
|
||||
tmpParams.addreplace("SubtitleStreamIndex", int(video.Subtitles[video.SelectedSubtitle].Index))
|
||||
end if
|
||||
end if
|
||||
|
||||
if video.isTranscoded then
|
||||
deleteTranscode(video.PlaySessionId)
|
||||
end if
|
||||
video.PlaySessionId = ItemGetPlaybackInfo(video.id, int(video.position) + playBackBuffer).PlaySessionId
|
||||
tmpParams.PlaySessionId = video.PlaySessionId
|
||||
video.transcodeParams = tmpParams
|
||||
|
||||
if video.directPlaySupported and video.decodeAudioSupported and not captions then
|
||||
'Captions are off and we do not need to transcode video or audo
|
||||
base = Substitute("Videos/{0}/stream", video.id)
|
||||
params = {
|
||||
"Static": "true",
|
||||
"Container": video.container
|
||||
"PlaySessionId": video.PlaySessionId
|
||||
}
|
||||
video.isTranscoded = false
|
||||
video.content.streamformat = video.container
|
||||
else
|
||||
'Captions are on or we need to transcode for any other reason
|
||||
video.content.streamformat = "hls"
|
||||
base = Substitute("Videos/{0}/master.m3u8", video.id)
|
||||
video.isTranscoded = true
|
||||
params = video.transcodeParams
|
||||
end if
|
||||
|
||||
video.content.url = buildURL(base, params)
|
||||
video.content.PlayStart = int(video.position + playBackBuffer)
|
||||
video.control = "play"
|
||||
end sub
|
@ -9,51 +9,51 @@ function registry_read(key, section=invalid)
|
||||
return invalid
|
||||
end function
|
||||
|
||||
function registry_write(key, value, section=invalid)
|
||||
if section = invalid then return invalid
|
||||
sub registry_write(key, value, section=invalid)
|
||||
if section = invalid then return
|
||||
reg = CreateObject("roRegistrySection", section)
|
||||
reg.write(key, value)
|
||||
reg.flush()
|
||||
end function
|
||||
end sub
|
||||
|
||||
function registry_delete(key, section=invalid)
|
||||
if section = invalid then return invalid
|
||||
sub registry_delete(key, section=invalid)
|
||||
if section = invalid then return
|
||||
reg = CreateObject("roRegistrySection", section)
|
||||
reg.delete(key)
|
||||
reg.flush()
|
||||
end function
|
||||
end sub
|
||||
|
||||
|
||||
' "Jellyfin" registry accessors for the default global settings
|
||||
function get_setting(key, default=invalid)
|
||||
value = registry_read(key, "Jellyfin")
|
||||
if value = invalid return default
|
||||
if value = invalid then return default
|
||||
return value
|
||||
end function
|
||||
|
||||
function set_setting(key, value)
|
||||
sub set_setting(key, value)
|
||||
registry_write(key, value, "Jellyfin")
|
||||
end function
|
||||
end sub
|
||||
|
||||
function unset_setting(key)
|
||||
sub unset_setting(key)
|
||||
registry_delete(key, "Jellyfin")
|
||||
end function
|
||||
end sub
|
||||
|
||||
|
||||
' User registry accessors for the currently active user
|
||||
function get_user_setting(key, default=invalid)
|
||||
if get_setting("active_user") = invalid then return default
|
||||
value = registry_read(key, get_setting("active_user"))
|
||||
if value = invalid return default
|
||||
if value = invalid then return default
|
||||
return value
|
||||
end function
|
||||
|
||||
function set_user_setting(key, value)
|
||||
if get_setting("active_user") = invalid then return invalid
|
||||
sub set_user_setting(key, value)
|
||||
if get_setting("active_user") = invalid then return
|
||||
registry_write(key, value, get_setting("active_user"))
|
||||
end function
|
||||
end sub
|
||||
|
||||
function unset_user_setting(key)
|
||||
if get_setting("active_user") = invalid then return invalid
|
||||
sub unset_user_setting(key)
|
||||
if get_setting("active_user") = invalid then return
|
||||
registry_delete(key, get_setting("active_user"))
|
||||
end function
|
||||
end sub
|
||||
|
@ -21,7 +21,7 @@ function getDeviceProfile() as object
|
||||
'Check if 5.1 Audio Output connected
|
||||
maxAudioChannels = 2
|
||||
di = CreateObject("roDeviceInfo")
|
||||
if di.GetAudioOutputChannel() = "5.1 surround" then
|
||||
if di.GetAudioOutputChannel() = "5.1 surround"
|
||||
maxAudioChannels = 6
|
||||
end if
|
||||
|
||||
@ -155,51 +155,51 @@ function GetDirectPlayProfiles() as object
|
||||
end if
|
||||
|
||||
' Check for supported Audio
|
||||
if di.CanDecodeAudio({ Codec: "ac3"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "ac3"}).result
|
||||
mkvAudio = mkvAudio + ",ac3"
|
||||
mp4Audio = mp4Audio + ",ac3"
|
||||
audio = audio + ",ac3"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "wma"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "wma"}).result
|
||||
audio = audio + ",wma"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "flac"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "flac"}).result
|
||||
mkvAudio = mkvAudio + ",flac"
|
||||
audio = audio + ",flac"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "alac"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "alac"}).result
|
||||
mkvAudio = mkvAudio + ",alac"
|
||||
mp4Audio = mp4Audio + ",alac"
|
||||
audio = audio + ",alac"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "aac"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "aac"}).result
|
||||
mkvAudio = mkvAudio + ",aac"
|
||||
mp4Audio = mp4Audio + ",aac"
|
||||
audio = audio + ",aac"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "opus"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "opus"}).result
|
||||
mkvAudio = mkvAudio + ",opus"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "dts"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "dts"}).result
|
||||
mkvAudio = mkvAudio + ",dts"
|
||||
audio = audio + ",dts"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "wmapro"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "wmapro"}).result
|
||||
audio = audio + ",wmapro"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "vorbis"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "vorbis"}).result
|
||||
mkvAudio = mkvAudio + ",vorbis"
|
||||
end if
|
||||
|
||||
if di.CanDecodeAudio({ Codec: "eac3"}).result then
|
||||
if di.CanDecodeAudio({ Codec: "eac3"}).result
|
||||
mkvAudio = mkvAudio + ",eac3"
|
||||
end if
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
function initGlobal()
|
||||
sub initGlobal()
|
||||
if m.globals = invalid
|
||||
m.globals = CreateObject("roAssociativeArray")
|
||||
end if
|
||||
end function
|
||||
end sub
|
||||
|
||||
function getGlobal(key="" as String) as Dynamic
|
||||
initGlobal()
|
||||
return m.globals[key]
|
||||
end function
|
||||
|
||||
function setGlobal(key="" as String, value=invalid as Dynamic)
|
||||
sub setGlobal(key="" as String, value=invalid as Dynamic)
|
||||
initGlobal()
|
||||
m.globals[key] = value
|
||||
end function
|
||||
end sub
|
||||
|
@ -16,8 +16,8 @@ end function
|
||||
|
||||
function getButton(msg, subnode = "buttons" as string) as object
|
||||
buttons = msg.getRoSGNode().findNode(subnode)
|
||||
if buttons = invalid then return invalid
|
||||
active_button = buttons.focusedChild
|
||||
|
||||
return active_button
|
||||
end function
|
||||
|
||||
@ -46,7 +46,7 @@ function formatTime(time) as string
|
||||
hours = time.getHours()
|
||||
minHourDigits = 1
|
||||
di = CreateObject("roDeviceInfo")
|
||||
if di.GetClockFormat() = "12h" then
|
||||
if di.GetClockFormat() = "12h"
|
||||
meridian = "AM"
|
||||
if hours = 0
|
||||
hours = 12
|
||||
@ -70,7 +70,7 @@ end function
|
||||
|
||||
function div_ceiling(a as integer, b as integer) as integer
|
||||
if a < b then return 1
|
||||
if int(a/b) = a/b then
|
||||
if int(a/b) = a/b
|
||||
return a/b
|
||||
end if
|
||||
return a/b + 1
|
||||
@ -80,9 +80,9 @@ end function
|
||||
function get_dialog_result(dialog, port)
|
||||
while dialog <> invalid
|
||||
msg = wait(0, port)
|
||||
if isNodeEvent(msg, "backPressed") then
|
||||
if isNodeEvent(msg, "backPressed")
|
||||
return -1
|
||||
elseif isNodeEvent(msg, "itemSelected")
|
||||
else if isNodeEvent(msg, "itemSelected")
|
||||
return dialog.findNode("optionList").itemSelected
|
||||
end if
|
||||
end while
|
||||
@ -93,7 +93,7 @@ end function
|
||||
function lastFocusedChild(obj as object) as object
|
||||
child = obj
|
||||
for i = 0 to obj.getChildCount()
|
||||
if obj.focusedChild <> invalid then
|
||||
if obj.focusedChild <> invalid
|
||||
child = child.focusedChild
|
||||
end if
|
||||
end for
|
||||
@ -101,14 +101,13 @@ function lastFocusedChild(obj as object) as object
|
||||
end function
|
||||
|
||||
function show_dialog(message as string, options = [], defaultSelection = 0) as integer
|
||||
group = m.scene.focusedChild
|
||||
lastFocus = lastFocusedChild(m.scene)
|
||||
'We want to handle backPressed instead of the main loop
|
||||
m.scene.unobserveField("backPressed")
|
||||
|
||||
dialog = createObject("roSGNode", "JFMessageDialog")
|
||||
if options.count() then dialog.options = options
|
||||
if message.len() > 0 then
|
||||
if message.len() > 0
|
||||
reg = CreateObject("roFontRegistry")
|
||||
font = reg.GetDefaultFont()
|
||||
dialog.fontHeight = font.GetOneLineHeight()
|
||||
@ -116,7 +115,7 @@ function show_dialog(message as string, options = [], defaultSelection = 0) as i
|
||||
dialog.message = message
|
||||
end if
|
||||
|
||||
if defaultSelection > 0 then
|
||||
if defaultSelection > 0
|
||||
dialog.findNode("optionList").jumpToItem = defaultSelection
|
||||
end if
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user