Merge branch 'unstable' into hevc-directplay-profile-version-override

This commit is contained in:
Neil Burrows 2022-09-25 10:20:31 +01:00 committed by GitHub
commit 58e91aeb76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 2576 additions and 922 deletions

View File

@ -0,0 +1,20 @@
name: "Close stale Pull Requests"
on:
schedule:
- cron: "30 1 * * *"
jobs:
stale:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/stale@3de2653986ebd134983c79fe2be5d45cc3d9f4e1 # tag=v6
with:
days-before-issue-stale: -1
days-before-issue-close: -1
stale-pr-message: "This pull request has been inactive for 21 days and will be automatically closed in 7 days if there is no further activity."
close-pr-message: "This pull request has been closed because it has been inactive for 28 days. You may submit a new pull request if desired."
days-before-pr-stale: 21
days-before-pr-close: 7
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -8,8 +8,8 @@ jobs:
run:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
with:
node-version: "14.12.0"
- run: npm ci

View File

@ -19,6 +19,13 @@ Open up the new folder:
cd jellyfin-roku
```
Install Dependencies:
```bash
npm install
```
## Method 1: Visual Studio Code
We recommend using Visual Studio Code when working on this project. The [BrightScript Language extension](https://marketplace.visualstudio.com/items?itemName=RokuCommunity.brightscript) provides a rich debugging experience, including in-editor syntax checking, debugging/breakpoint support, variable inspection at runtime, auto-formatting, an integrated remote control mode, and [much more](https://rokucommunity.github.io/vscode-brightscript-language/features.html).

View File

@ -25,7 +25,17 @@ sub init()
m.itemGrid.observeField("itemFocused", "onItemFocused")
m.itemGrid.observeField("itemSelected", "onItemSelected")
m.itemGrid.observeField("AlphaSelected", "onItemAlphaSelected")
m.itemGrid.observeField("alphaSelected", "onItemalphaSelected")
'Voice filter setup
m.voiceBox = m.top.findNode("voiceBox")
m.voiceBox.voiceEnabled = true
m.voiceBox.active = true
m.voiceBox.observeField("text", "onvoiceFilter")
'set voice help text
m.voiceBox.hintText = tr("Use voice remote to search")
'backdrop
m.newBackdrop.observeField("loadStatus", "newBGLoaded")
'Background Image Queued for loading
@ -45,19 +55,35 @@ sub init()
m.spinner = m.top.findNode("spinner")
m.spinner.visible = true
m.Alpha = m.top.findNode("AlphaMenu")
m.AlphaSelected = m.top.findNode("AlphaSelected")
'Get reset folder setting
m.resetGrid = get_user_setting("itemgrid.reset") = "true"
'Check if device has voice remote
devinfo = CreateObject("roDeviceInfo")
m.deviFeature = devinfo.HasFeature("voice_remote")
m.micButton = m.top.findNode("micButton")
m.micButtonText = m.top.findNode("micButtonText")
'Hide voice search if device does not have voice remote
if m.deviFeature = false
m.micButton.visible = false
m.micButtonText.visible = false
end if
end sub
'
'Load initial set of Data
sub loadInitialItems()
m.loadItemsTask.control = "stop"
m.spinner.visible = true
if m.top.parentItem.json.Type = "CollectionFolder" 'or m.top.parentItem.json.Type = "Folder"
m.top.HomeLibraryItem = m.top.parentItem.Id
end if
if m.top.parentItem.backdropUrl <> invalid
SetBackground(m.top.parentItem.backdropUrl)
end if
@ -66,6 +92,14 @@ sub loadInitialItems()
if m.top.parentItem.collectionType = "livetv"
' Translate between app and server nomenclature
viewSetting = get_user_setting("display.livetv.landing")
'Move mic to be visiable on TV Guide screen
if m.deviFeature = true
m.micButton.translation = "[1540, 92]"
m.micButtonText.visible = true
m.micButtonText.translation = "[1600,130]"
m.micButtonText.font.size = 22
m.micButtonText.text = tr("Search")
end if
if viewSetting = "guide"
m.view = "tvGuide"
else
@ -112,24 +146,31 @@ sub loadInitialItems()
end if
updateTitle()
m.loadItemsTask.nameStartsWith = m.top.AlphaSelected
m.loadItemsTask.nameStartsWith = m.top.alphaSelected
m.loadItemsTask.searchTerm = m.voiceBox.text
m.emptyText.visible = false
m.loadItemsTask.sortField = m.sortField
m.loadItemsTask.sortAscending = m.sortAscending
m.loadItemsTask.filter = m.filter
m.loadItemsTask.startIndex = 0
' Load Item Types
if m.top.parentItem.collectionType = "movies"
if getCollectionType() = "movies"
m.loadItemsTask.itemType = "Movie"
m.loadItemsTask.itemId = m.top.parentItem.Id
else if m.top.parentItem.collectionType = "tvshows"
else if getCollectionType() = "tvshows"
m.loadItemsTask.itemType = "Series"
m.loadItemsTask.itemId = m.top.parentItem.Id
else if m.top.parentItem.collectionType = "music"
else if getCollectionType() = "music"
' Default Settings
m.loadItemsTask.recursive = false
m.itemGrid.itemSize = "[290, 290]"
m.itemGrid.itemSpacing = "[ 0, 20]"
if m.voiceBox.text <> ""
m.loadItemsTask.recursive = true
else
m.loadItemsTask.recursive = false
m.itemGrid.itemSize = "[290, 290]"
end if
m.loadItemsTask.itemType = "MusicArtist,MusicAlbum"
m.loadItemsTask.itemId = m.top.parentItem.Id
@ -143,19 +184,21 @@ sub loadInitialItems()
m.loadItemsTask.recursive = true
end if
else if m.top.parentItem.collectionType = "livetv"
m.loadItemsTask.itemType = "LiveTV"
m.loadItemsTask.itemType = "TvChannel"
m.loadItemsTask.itemId = " "
' For LiveTV, we want to "Fit" the item images, not zoom
m.top.imageDisplayMode = "scaleToFit"
if get_user_setting("display.livetv.landing") = "guide" and m.options.view <> "livetv"
showTvGuide()
end if
else if m.top.parentItem.collectionType = "CollectionFolder" or m.top.parentItem.type = "CollectionFolder" or m.top.parentItem.collectionType = "boxsets" or m.top.parentItem.Type = "Boxset" 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.Type = "Channel"
m.top.imageDisplayMode = "scaleToFit"
else if m.top.parentItem.collectionType = "CollectionFolder" or m.top.parentItem.type = "CollectionFolder" or m.top.parentItem.collectionType = "boxsets" or m.top.parentItem.Type = "Boxset" or m.top.parentItem.Type = "Boxsets" or m.top.parentItem.Type = "Folder" or m.top.parentItem.Type = "Channel"
if m.voiceBox.text <> ""
m.loadItemsTask.recursive = true
else
' non recursive for collections (folders, boxsets, photo albums, etc)
m.loadItemsTask.recursive = false
end if
else if m.top.parentItem.json.type = "Studio"
m.loadItemsTask.itemId = m.top.parentItem.parentFolder
m.loadItemsTask.itemType = "Series,Movie"
@ -491,13 +534,33 @@ sub onItemSelected()
m.top.selectedItem = m.itemGrid.content.getChild(m.itemGrid.itemSelected)
end sub
sub onItemAlphaSelected()
m.loadedRows = 0
m.loadedItems = 0
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
m.spinner.visible = true
loadInitialItems()
sub onItemalphaSelected()
if m.top.alphaSelected <> ""
m.loadedRows = 0
m.loadedItems = 0
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
m.loadItemsTask.searchTerm = ""
m.VoiceBox.text = ""
m.loadItemsTask.nameStartsWith = m.alpha.itemAlphaSelected
m.spinner.visible = true
loadInitialItems()
end if
end sub
sub onvoiceFilter()
if m.VoiceBox.text <> ""
m.loadedRows = 0
m.loadedItems = 0
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
m.top.alphaSelected = ""
m.loadItemsTask.NameStartsWith = " "
m.loadItemsTask.searchTerm = m.voiceBox.text
m.loadItemsTask.recursive = true
m.spinner.visible = true
loadInitialItems()
end if
end sub
@ -598,6 +661,7 @@ sub optionsClosed()
if m.tvGuide <> invalid
m.tvGuide.lastFocus.setFocus(true)
end if
end sub
sub showTVGuide()
@ -608,6 +672,7 @@ sub showTVGuide()
m.tvGuide.observeField("focusedChannel", "onChannelFocused")
end if
m.tvGuide.filter = m.filter
m.tvGuide.searchTerm = m.voiceBox.text
m.top.appendChild(m.tvGuide)
m.tvGuide.lastFocus.setFocus(true)
end sub
@ -629,6 +694,12 @@ end sub
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
topGrp = m.top.findNode("itemGrid")
searchGrp = m.top.findNode("voiceBox")
if key = "left" and searchGrp.isinFocusChain()
topGrp.setFocus(true)
searchGrp.setFocus(false)
end if
if key = "options"
if m.options.visible = true
m.options.visible = false
@ -655,9 +726,13 @@ function onKeyEvent(key as string, press as boolean) as boolean
m.options.visible = false
optionsClosed()
return true
else
m.global.sceneManager.callfunc("popScene")
m.loadItemsTask.control = "stop"
return true
end if
else if key = "play" or key = "OK"
markupGrid = m.top.getChild(2)
markupGrid = m.top.findNode("itemGrid")
itemToPlay = markupGrid.content.getChild(markupGrid.itemFocused)
if itemToPlay <> invalid and (itemToPlay.type = "Movie" or itemToPlay.type = "Episode")
@ -673,9 +748,10 @@ function onKeyEvent(key as string, press as boolean) as boolean
else if key = "left" and topGrp.isinFocusChain()
m.top.alphaActive = true
topGrp.setFocus(false)
alpha = m.Alpha.getChild(0).findNode("Alphamenu")
alpha = m.alpha.getChild(0).findNode("Alphamenu")
alpha.setFocus(true)
return true
else if key = "right" and m.Alpha.isinFocusChain()
m.top.alphaActive = false
m.Alpha.setFocus(false)
@ -690,6 +766,20 @@ function onKeyEvent(key as string, press as boolean) as boolean
end if
end if
if key = "replay"
m.spinner.visible = true
m.loadItemsTask.searchTerm = ""
m.loadItemsTask.nameStartsWith = ""
m.voiceBox.text = ""
m.top.alphaSelected = ""
m.loadItemsTask.filter = "All"
m.filter = "All"
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
loadInitialItems()
return true
end if
return false
end function
@ -699,9 +789,11 @@ sub updateTitle()
else if m.filter = "Favorites"
m.top.overhangTitle = m.top.parentItem.title + " " + tr("(Favorites)")
end if
if m.top.AlphaSelected <> ""
m.top.overhangTitle = m.top.parentItem.title + " " + tr("(Filtered)")
if m.voiceBox.text <> ""
m.top.overhangTitle = m.top.parentItem.title + tr(" (Filtered by ") + m.loadItemsTask.searchTerm + ")"
end if
if m.top.alphaSelected <> ""
m.top.overhangTitle = m.top.parentItem.title + tr(" (Filtered by ") + m.loadItemsTask.nameStartsWith + ")"
end if
if m.options.view = "Networks" or m.view = "Networks"

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="ItemGrid" extends="JFGroup">
<children>
<VoiceTextEditBox id="VoiceBox" visible="true" width = "40" translation = "[52, 120]" />
<Rectangle id="VoiceBoxCover" height="240" width="100" color="0x262626ff" translation = "[25, 75]" />
<poster id="backdrop"
loadDisplayMode="scaleToFill"
width="1920"
@ -23,10 +24,12 @@
vertFocusAnimationStyle = "fixed"
itemSize = "[ 290, 425 ]"
itemSpacing = "[ 0, 45 ]"
drawFocusFeedback = "false" />
<Label translation="[0,540]" id="emptyText" font="font:LargeSystemFont" width="1920" horizAlign="center" vertAlign="center" height="64" visible="false" />
drawFocusFeedback = "false" />
<Label id="micButtonText" font="font:SmallSystemFont" visible="false" />
<Button id = "micButton" maxWidth = "20" translation = "[20, 120]" iconUri = "pkg:/images/icons/mic_icon.png"/>
<Label translation="[0,540]" id="emptyText" font="font:LargeSystemFont" width="1910" horizAlign="center" vertAlign="center" height="64" visible="false" />
<ItemGridOptions id="options" visible="false" />
<Spinner id="spinner" translation="[920, 540]" />
<Spinner id="spinner" translation="[900, 450]" />
<Animation id="backroundSwapAnimation" duration="1" repeat="false" easeFunction="linear" >
<FloatFieldInterpolator id = "fadeinLoading" key="[0.0, 1.0]" keyValue="[ 0.00, 0.25 ]" fieldToInterp="backdropTransition.opacity" />
<FloatFieldInterpolator id = "fadeoutLoaded" key="[0.0, 1.0]" keyValue="[ 0.25, 0.00 ]" fieldToInterp="backdrop.opacity" />

View File

@ -1,7 +1,7 @@
sub init()
m.buttons = m.top.findNode("buttons")
m.buttons.buttons = [tr("TAB_VIEW"), tr("TAB_SORT"), tr("TAB_FILTER")]
m.buttons.buttons = [tr("View"), tr("Sort"), tr("Filter")]
m.buttons.selectedIndex = 1
m.buttons.setFocus(true)

View File

@ -1,9 +1,15 @@
sub init()
m.top.functionName = "loadItems"
m.top.limit = 60
usersettingLimit = get_user_setting("itemgrid.Limit")
if usersettingLimit <> invalid
m.top.limit = usersettingLimit
end if
end sub
sub loadItems()
results = []
sort_field = m.top.sortField
@ -26,15 +32,30 @@ sub loadItems()
StudioIds: m.top.studioIds,
genreIds: m.top.genreIds
}
' Handle special case when getting names starting with numeral
if m.top.NameStartsWith <> ""
if m.top.NameStartsWith = "#"
params.NameLessThan = "A"
if m.top.ItemType = "LiveTV" or m.top.ItemType = "TvChannel"
params.searchterm = "A"
params.append({ parentid: " " })
else
params.NameLessThan = "A"
end if
else
params.NameStartsWith = m.top.nameStartsWith
if m.top.ItemType = "LiveTV" or m.top.ItemType = "TvChannel"
params.searchterm = m.top.nameStartsWith
params.append({ parentid: " " })
else
params.NameStartsWith = m.top.nameStartsWith
end if
end if
end if
if m.top.searchTerm <> ""
params.searchTerm = m.top.searchTerm
end if
filter = m.top.filter
if filter = "All" or filter = "all"
' do nothing
@ -67,11 +88,11 @@ sub loadItems()
for each item in data.Items
tmp = invalid
if item.Type = "Movie"
if item.Type = "Movie" or item.Type = "MusicVideo"
tmp = CreateObject("roSGNode", "MovieData")
else if item.Type = "Series"
tmp = CreateObject("roSGNode", "SeriesData")
else if item.Type = "BoxSet"
else if item.Type = "BoxSet" or item.Type = "ManualPlaylistsFolder"
tmp = CreateObject("roSGNode", "CollectionData")
else if item.Type = "TvChannel"
tmp = CreateObject("roSGNode", "ChannelData")

View File

@ -12,6 +12,7 @@
<field id="nameStartsWith" type="string" value="" />
<field id="recursive" type="boolean" value="true" />
<field id="filter" type="string" value="All" />
<field id="searchTerm" type="string" value="" />
<field id="studioIds" type="string" value="" />
<field id="genreIds" type="string" value="" />
<field id="view" type="string" value="" />

View File

@ -2,130 +2,21 @@ sub init()
m.playbackTimer = m.top.findNode("playbackTimer")
m.bufferCheckTimer = m.top.findNode("bufferCheckTimer")
m.top.observeField("state", "onState")
m.top.observeField("position", "onPositionChanged")
m.top.trickPlayBar.observeField("visible", "onTrickPlayBarVisibilityChange")
m.playbackTimer.observeField("fire", "ReportPlayback")
m.bufferPercentage = 0 ' Track whether content is being loaded
m.playReported = false
m.top.transcodeReasons = []
m.bufferCheckTimer.duration = 10
m.bufferCheckTimer.duration = 30
if get_user_setting("ui.design.hideclock") = "true"
clockNode = findNodeBySubtype(m.top, "clock")
if clockNode[0] <> invalid then clockNode[0].parent.removeChild(clockNode[0].node)
end if
' Skip Intro Button
m.skipIntroButton = m.top.findNode("skipIntro")
m.skipIntroButton.text = tr("Skip Intro")
m.introCompleted = false
m.showskipIntroButtonAnimation = m.top.findNode("showskipIntroButton")
m.hideskipIntroButtonAnimation = m.top.findNode("hideskipIntroButton")
m.moveUpskipIntroButtonAnimation = m.top.findNode("moveUpskipIntroButton")
m.moveDownskipIntroButtonAnimation = m.top.findNode("moveDownskipIntroButton")
end sub
'
' Checks if we have valid skip intro param data
function haveSkipIntroParams() as boolean
' Intro data is invalid, skip
if not isValid(m.top.skipIntroParams?.Valid)
return false
end if
' Returned intro data is not valid, return
if not m.top.skipIntroParams.Valid
return false
end if
return true
end function
'
' Handles showing / hiding the skip intro button
sub handleSkipIntro()
' We've already shown the intro, return
if m.introCompleted then return
' We don't have valid data, return
if not haveSkipIntroParams() then return
' Check if it's time to hide the skip prompt
if m.top.position >= m.top.skipIntroParams.HideSkipPromptAt
if skipIntroButtonVisible()
hideSkipIntro()
end if
return
end if
' Check if it's time to show the skip prompt
if m.top.position >= m.top.skipIntroParams.ShowSkipPromptAt
if not skipIntroButtonVisible()
showSkipIntro()
end if
return
end if
end sub
'
' When Trick Playbar Visibility changes
sub onTrickPlayBarVisibilityChange()
' Skip Intro button isn't visible, return
if not skipIntroButtonVisible() then return
' Trick Playbar is visible, move the skip intro button up and fade it out
if m.top.trickPlayBar.visible
m.moveUpskipIntroButtonAnimation.control = "start"
m.skipIntroButton.setFocus(false)
m.top.setFocus(true)
return
end if
' Trick Playbar is not visible, move the skip intro button down and fade it in
m.moveDownskipIntroButtonAnimation.control = "start"
m.skipIntroButton.setFocus(true)
end sub
'
' When Video Player state changes
sub onPositionChanged()
' Check if content is episode
if m.top.content.contenttype = 4
handleSkipIntro()
end if
end sub
'
' Returns if skip intro button is currently visible
function skipIntroButtonVisible() as boolean
return m.skipIntroButton.opacity > 0
end function
'
' Runs skip intro button animation and sets focus to button
sub showSkipIntro()
m.showskipIntroButtonAnimation.control = "start"
m.skipIntroButton.setFocus(true)
end sub
'
' Runs hide intro button animation and sets focus back to video
sub hideSkipIntro()
m.top.trickPlayBar.unobserveField("visible")
m.hideskipIntroButtonAnimation.control = "start"
m.introCompleted = true
m.skipIntroButton.setFocus(false)
m.top.setFocus(true)
end sub
'
' When Video Player state changes
sub onState(msg)
' When buffering, start timer to monitor buffering process
if m.top.state = "buffering" and m.bufferCheckTimer <> invalid
@ -238,21 +129,14 @@ end sub
function onKeyEvent(key as string, press as boolean) as boolean
if key = "OK"
if not m.top.trickPlayBar.visible
if m.skipIntroButton.hasFocus()
m.top.seek = m.top.skipIntroParams.IntroEnd
hideSkipIntro()
return true
end if
end if
end if
if not press then return false
if m.top.Subtitles.count() and key = "down"
m.top.selectSubtitlePressed = true
return true
else if key = "up"
m.top.selectPlaybackInfoPressed = true
return true
end if
return false

View File

@ -3,6 +3,7 @@
<interface>
<field id="backPressed" type="boolean" alwaysNotify="true" />
<field id="selectSubtitlePressed" type="boolean" alwaysNotify="true" />
<field id="selectPlaybackInfoPressed" type="boolean" alwaysNotify="true" />
<field id="PlaySessionId" type="string" />
<field id="Subtitles" type="array" />
<field id="SelectedSubtitle" type="integer" />
@ -17,35 +18,16 @@
<field id="retryWithTranscoding" type="boolean" value="false" />
<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" />
<field id="skipIntroParams" type="assocarray" />
</interface>
<script type="text/brightscript" uri="JFVideo.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
<children>
<JFButton id="skipIntro" opacity="0" textColor="#f0f0f0" focusedTextColor="#202020" focusFootprintBitmapUri="pkg:/images/option-menu-bg.9.png" focusBitmapUri="pkg:/images/white.9.png" translation="[1575, 900]" />
<timer id="playbackTimer" repeat="true" duration="30" />
<timer id="bufferCheckTimer" repeat="true" />
<Animation id="moveUpskipIntroButton" duration=".1" repeat="false" easeFunction="linear">
<Vector2DFieldInterpolator key="[0.0,1.0]" keyValue="[[1575, 900], [1575, 825]]" fieldToInterp="skipIntro.translation"/>
<FloatFieldInterpolator key="[0.0, 1.0]" keyValue="[.9, .1]" fieldToInterp="skipIntro.opacity" />
</Animation>
<Animation id="moveDownskipIntroButton" duration=".1" repeat="false" easeFunction="linear">
<Vector2DFieldInterpolator key="[0.0,1.0]" keyValue="[[1575, 825], [1575, 900]]" fieldToInterp="skipIntro.translation"/>
<FloatFieldInterpolator key="[0.0, 1.0]" keyValue="[.1, .9]" fieldToInterp="skipIntro.opacity" />
</Animation>
<Animation id="showskipIntroButton" duration="1.0" repeat="false" easeFunction="inQuad">
<FloatFieldInterpolator key="[0.0, 1.0]" keyValue="[0.0, .9]" fieldToInterp="skipIntro.opacity" />
</Animation>
<Animation id="hideskipIntroButton" duration=".2" repeat="false" easeFunction="inQuad">
<FloatFieldInterpolator key="[0.0, 1.0]" keyValue="[.9, 0]" fieldToInterp="skipIntro.opacity" />
</Animation>
</children>
</component>

View File

@ -1,6 +1,7 @@
sub init()
m.title = m.top.findNode("title")
m.staticTitle = m.top.findNode("staticTitle")
m.series = m.top.findNode("Series")
m.poster = m.top.findNode("poster")
m.backdrop = m.top.findNode("backdrop")
@ -39,6 +40,8 @@ sub updateSize()
m.staticTitle.height = m.title.height
m.staticTitle.translation = m.title.translation
m.series.maxWidth = maxSize[0]
m.poster.width = int(maxSize[0]) - 4
m.poster.height = int(maxSize[1]) - m.title.height 'Set poster height to available space
@ -54,9 +57,14 @@ sub itemContentChanged() as void
if itemData.json.lookup("Type") = "Episode" and itemData.json.IndexNumber <> invalid
m.title.text = StrI(itemData.json.IndexNumber) + ". " + m.title.text
m.series.text = itemData.json.Series
m.series.visible = true
else if itemData.json.lookup("Type") = "MusicAlbum"
m.title.font = "font:SmallestSystemFont"
m.staticTitle.font = "font:SmallestSystemFont"
end if
m.staticTitle.text = m.title.text
@ -64,7 +72,7 @@ sub itemContentChanged() as void
if get_user_setting("ui.tvshows.blurunwatched") = "true"
if itemData.json.lookup("Type") = "Episode"
if itemData.json.lookup("Type") = "Episode" and itemData.json.userdata <> invalid
if not itemData.json.userdata.played
imageUrl = imageUrl + "&blur=15"
end if
@ -82,6 +90,7 @@ sub focusChanged()
if m.top.itemHasFocus = true
m.title.repeatCount = -1
m.series.repeatCount = -1
m.staticTitle.visible = false
m.title.visible = true
@ -94,6 +103,7 @@ sub focusChanged()
else
m.title.repeatCount = 0
m.series.repeatCount = 0
m.staticTitle.visible = true
m.title.visible = false
end if

View File

@ -2,6 +2,12 @@
<component name="ListPoster" extends="Group">
<children>
<Rectangle id="backdrop" />
<ScrollingLabel id="Series"
horizAlign="center"
font="font:SmallSystemFont"
repeatCount="0"
visible="false"
/>
<Poster id="poster" translation="[2,0]" loadDisplayMode="scaleToFit" />
<ScrollingLabel id="title"
horizAlign="center"

View File

@ -154,7 +154,7 @@ sub createDialogPallete()
DialogSecondaryTextColor: "0xf8f8f8ff",
DialogSecondaryItemColor: "0xcc7ecc4D",
DialogInputFieldColor: "0x80FF8080",
DialogKeyboardColor: "0x80FF804D",
KeyboardDialogColor: "0x80FF804D",
DialogFootprintColor: "0x80FF804D"
}
end sub

View File

@ -3,48 +3,25 @@ sub init()
m.top.horizAlignment = "center"
m.top.vertAlignment = "top"
m.top.visible = false
m.searchText = m.top.findNode("search_Key")
m.searchText.textEditBox.hintText = tr("Search")
m.searchText.keyGrid.keyDefinitionUri = "pkg:/components/data/CustomAddressKDF.json"
m.searchText.textEditBox.voiceEnabled = true
m.searchText.textEditBox.active = true
m.searchText.ObserveField("text", "searchMedias")
m.searchSelect = m.top.findNode("searchSelect")
'set lable text
m.label = m.top.findNode("text")
m.label.text = tr("Search now")
show_dialog()
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
if key = "OK"
' Make a Keyboard Dialog here
show_dialog()
return true
sub searchMedias()
m.top.search_values = m.searchText.text
if m.top.search_values.len() > 1
m.searchText.textEditBox.leadingEllipsis = true
else
m.searchText.textEditBox.leadingEllipsis = false
end if
return false
end function
function onDialogButton()
d = m.top.getScene().dialog
button_text = d.buttons[d.buttonSelected]
if button_text = tr("Search")
m.top.search_value = d.text
dismiss_dialog()
return true
else if button_text = tr("Cancel")
dismiss_dialog()
return true
end if
return false
end function
sub show_dialog()
dialog = CreateObject("roSGNode", "KeyboardDialog")
dialog.title = tr("Search")
dialog.buttons = [tr("Search"), tr("Cancel")]
m.top.getScene().dialog = dialog
dialog.observeField("buttonselected", "onDialogButton")
end sub
sub dismiss_dialog()
m.top.getScene().dialog.close = true
end sub

View File

@ -1,11 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="SearchBox" extends="LayoutGroup">
<children>
<Label text="Search" />
<TextEditBox id="search-input" width="800" maxTextLength="75" />
<Label id = "text" text="" visible="false" />
<DynamicMiniKeyboard id="search_Key" />
</children>
<interface>
<field id="search_value" type="string" alwaysNotify="true" />
<field id="search_values" type="string" alwaysNotify="true" />
</interface>
<script type="text/brightscript" uri="SearchBox.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Items.brs" />
<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" />
<script type="text/brightscript" uri="pkg:/source/utils/deviceCapabilities.brs" />
</component>

View File

@ -2,5 +2,5 @@ sub init()
m.top.poster.uri = "pkg:/images/spinner.png"
m.top.control = "start"
m.top.clockwise = true
m.top.spinInterval = 2
m.top.spinInterval = 3
end sub

View File

@ -8,7 +8,7 @@ sub init()
m.name.vertAlign = "center"
m.name.horizAlign = "center"
m.value.hintText = tr("Enter a value...")
m.value.hintText = tr("Enter a username")
m.value.maxTextLength = 120
end sub
@ -17,6 +17,7 @@ sub itemContentChanged()
m.name.text = data.label
if data.type = "password"
m.value.hintText = tr("Enter a password")
m.value.secureMode = true
end if

View File

@ -45,13 +45,18 @@ end function
sub show_dialog(configField)
dialog = createObject("roSGNode", "KeyboardDialog")
dialog = createObject("roSGNode", "StandardKeyboardDialog")
m.configField = configField
dialog.title = "Enter the " + configField.label
dialog.title = configField.label
dialog.buttons = [tr("OK"), tr("Cancel")]
m.greenPalette = createObject("roSGNode", "RSGPalette")
m.greenPalette.colors = {
DialogBackgroundColor: "#2A2B2A"
}
dialog.palette = m.greenPalette
if configField.type = "password"
dialog.keyboard.textEditBox.secureMode = true
dialog.textEditBox.secureMode = true
end if
if configField.value <> ""

View File

@ -23,12 +23,18 @@ function onKeyEvent(key as string, press as boolean) as boolean
else if key = "down" and submit.focusedChild = invalid
submit.setFocus(true)
return true
else if key = "up" and submit.focusedChild <> invalid or quickConnect.focusedChild <> invalid
else if key = "up" and submit.focusedChild <> invalid
checkbox.setFocus(true)
return true
else if key = "up" and quickConnect.focusedChild <> invalid
checkbox.setFocus(true)
return true
else if key = "up" and checkbox.focusedChild <> invalid
list.setFocus(true)
return true
else if key = "right" and checkbox.focusedChild <> invalid
quickConnect.setFocus(true)
return true
else if key = "right" and submit.focusedChild <> invalid
quickConnect.setFocus(true)
return true

View File

@ -29,9 +29,21 @@ function onKeyEvent(key as string, press as boolean) as boolean
'user navigating up to the server picker from the input box (it's only focusable if it has items)
else if key = "up" and m.serverUrlContainer.hasFocus() and m.servers.Count() > 0
m.serverPicker.setFocus(true)
else if key = "up" and m.serverUrlContainer.hasFocus() and m.servers.Count() = 0
ScanForServers()
else if key = "back" and m.serverUrlContainer.hasFocus() and m.servers.Count() > 0
m.serverPicker.setFocus(true)
else if key = "OK" and m.serverUrlContainer.hasFocus()
ShowKeyboard()
'focus the serverUrl input from submit button
else if key = "back" and m.submit.hasFocus() and m.servers.Count() > 0
m.serverPicker.setFocus(true)
else if key = "back" and m.submit.hasFocus() and m.servers.Count() = 0
m.serverUrlContainer.setFocus(true)
else if key = "back" and m.serverUrlContainer.hasFocus() and m.servers.Count() = 0
ScanForServers()
else if key = "back" and m.serverPicker.hasFocus() and m.servers.Count() > 0
ScanForServers()
' On "back" with or without available local servers, will rescan for servers
else if key = "up" and m.submit.hasFocus()
m.serverUrlContainer.setFocus(true)
'focus the submit button from serverUrl
@ -60,6 +72,7 @@ sub ScanForServers()
'run the task
m.ssdpScanner.observeField("content", "ScanForServersComplete")
m.ssdpScanner.control = "RUN"
m.spinner.visible = true
end sub
sub ScanForServersComplete(event)
@ -107,10 +120,13 @@ sub ScanForServersComplete(event)
end sub
sub ShowKeyboard()
dialog = createObject("roSGNode", "KeyboardDialog")
dialog = createObject("roSGNode", "StandardKeyboardDialog")
dialog.title = tr("Enter the server name or ip address")
dialog.buttons = [tr("OK"), tr("Cancel")]
dialog.text = m.serverUrlTextbox.text
greenPalette = createObject("roSGNode", "RSGPalette")
greenPalette.colors = { DialogBackgroundColor: "#2A2B2A" }
dialog.palette = greenPalette
m.top.getscene().dialog = dialog
m.dialog = dialog

View File

@ -0,0 +1,96 @@
{
"keyboardWidthFHD": 374,
"keyboardHeightFHD": 409,
"keyboardWidthHD": 250,
"keyboardHeightHD": 273,
"sections": [
{
"grids": [
{
"rows": [
{
"keys": [
{ "label": "a" },
{ "label": "b" },
{ "label": "c" },
{ "label": "d" },
{ "label": "e" },
{ "label": "f" }
]
},
{
"keys": [
{ "label": "g" },
{ "label": "h" },
{ "label": "i" },
{ "label": "j" },
{ "label": "k" },
{ "label": "l" }
]
},
{
"keys": [
{ "label": "m" },
{ "label": "n" },
{ "label": "o" },
{ "label": "p" },
{ "label": "q" },
{ "label": "r" }
]
},
{
"keys": [
{ "label": "s" },
{ "label": "t" },
{ "label": "u" },
{ "label": "v" },
{ "label": "w" },
{ "label": "x" }
]
},
{
"keys": [
{ "label": "y" },
{ "label": "z" },
{ "label": "1" },
{ "label": "2" },
{ "label": "3" },
{ "label": "4" }
]
},
{
"keys": [
{ "label": "5" },
{ "label": "6" },
{ "label": "7" },
{ "label": "8" },
{ "label": "9" },
{ "label": "0" }
]
},
{
"keys": [
{
"icon": "theme:DKB_ClearKeyBitmap",
"focusIcon": "theme:DKB_ClearKeyFocusBitmap",
"strOut": "clear"
},
{
"icon": "theme:DKB_SpaceKeyBitmap",
"focusIcon": "theme:DKB_SpaceKeyFocusBitmap",
"strOut": "space"
},
{
"icon": "theme:DKB_DeleteKeyBitmap",
"focusIcon": "theme:DKB_DeleteKeyFocusBitmap",
"autoRepeat": 1,
"strOut": "backspace"
}
]
}
]
}
]
}
]
}

View File

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

View File

@ -54,6 +54,22 @@ sub loadItems()
params["ImageTypeLimit"] = 1
params["UserId"] = get_setting("active_user")
maxDaysInNextUp = get_user_setting("ui.details.maxdaysnextup", "365")
if isValid(maxDaysInNextUp)
maxDaysInNextUp = Val(maxDaysInNextUp)
if maxDaysInNextUp > 0
dateToday = CreateObject("roDateTime")
dateCutoff = CreateObject("roDateTime")
dateCutoff.FromSeconds(dateToday.AsSeconds() - (maxDaysInNextUp * 86400))
params["NextUpDateCutoff"] = dateCutoff.ToISOString()
params["EnableRewatching"] = false
params["DisableFirstEpisode"] = false
params["limit"] = 24
end if
end if
resp = APIRequest(url, params)
data = getJson(resp)
for each item in data.Items

View File

@ -12,6 +12,7 @@
<script type="text/brightscript" uri="pkg:/source/api/Items.brs" />
<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/utils/misc.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/deviceCapabilities.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
</component>

View File

@ -6,15 +6,41 @@ sub loadChannels()
results = []
sort_field = m.top.sortField
if m.top.sortAscending = true
sort_order = "Ascending"
else
sort_order = "Descending"
end if
params = {
includeItemTypes: "LiveTvChannel",
SortBy: sort_field,
SortOrder: sort_order,
recursive: m.top.recursive,
UserId: get_setting("active_user")
}
' Handle special case when getting names starting with numeral
if m.top.NameStartsWith <> ""
if m.top.NameStartsWith = "#"
params.searchterm = "A"
else
params.searchterm = m.top.nameStartsWith
end if
end if
'Append voice search when there is text
if m.top.searchTerm <> ""
params.searchTerm = m.top.searchTerm
end if
if m.top.filter = "Favorites"
params.append({ isFavorite: true })
end if
url = "LiveTv/Channels"
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
resp = APIRequest(url, params)
data = getJson(resp)
@ -39,7 +65,5 @@ sub loadChannels()
results.push(channel)
end if
end for
m.top.channels = results
end sub

View File

@ -5,7 +5,11 @@
<field id="limit" type="integer" value="" />
<field id="startIndex" type="integer" value="0" />
<field id="filter" type="string" value="All" />
<field id="searchTerm" type="string" value="" />
<field id="sortField" type="string" value="SortName" />
<field id="sortAscending" type="boolean" value="true" />
<field id="nameStartsWith" type="string" value="" />
<field id="recursive" type="boolean" value="true" />
<!-- Total records available from server-->
<field id="channels" type="array" />
</interface>

View File

@ -1,5 +1,4 @@
sub init()
m.EPGLaunchCompleteSignaled = false
m.scheduleGrid = m.top.findNode("scheduleGrid")
m.detailsPane = m.top.findNode("detailsPane")
@ -7,7 +6,6 @@ sub init()
m.detailsPane.observeField("watchSelectedChannel", "onWatchChannelSelected")
m.detailsPane.observeField("recordSelectedChannel", "onRecordChannelSelected")
m.detailsPane.observeField("recordSeriesSelectedChannel", "onRecordSeriesChannelSelected")
m.gridStartDate = CreateObject("roDateTime")
m.scheduleGrid.contentStartTime = m.gridStartDate.AsSeconds() - 1800
m.gridEndDate = createObject("roDateTime")
@ -28,10 +26,11 @@ sub init()
m.top.lastFocus = m.scheduleGrid
m.channelIndex = {}
m.spinner = m.top.findNode("spinner")
end sub
sub channelFilterSet()
print "Channel Filter set"
m.scheduleGrid.jumpToChannel = 0
if m.top.filter <> invalid and m.LoadChannelsTask.filter <> m.top.filter
if m.LoadChannelsTask.state = "run" then m.LoadChannelsTask.control = "stop"
@ -42,6 +41,19 @@ sub channelFilterSet()
end sub
'Voice Search set
sub channelsearchTermSet()
m.scheduleGrid.jumpToChannel = 0
if m.top.searchTerm <> invalid and m.LoadChannelsTask.searchTerm <> m.top.searchTerm
if m.LoadChannelsTask.state = "run" then m.LoadChannelsTask.control = "stop"
m.LoadChannelsTask.searchTerm = m.top.searchTerm
m.spinner.visible = true
m.LoadChannelsTask.control = "RUN"
end if
end sub
' Initial list of channels loaded
sub onChannelsLoaded()
gridData = createObject("roSGNode", "ContentNode")
@ -49,32 +61,36 @@ sub onChannelsLoaded()
counter = 0
channelIdList = ""
for each item in m.LoadChannelsTask.channels
gridData.appendChild(item)
m.channelIndex[item.Id] = counter
counter = counter + 1
channelIdList = channelIdList + item.Id + ","
end for
'if search returns channels
if m.LoadChannelsTask.channels.count() > 0
for each item in m.LoadChannelsTask.channels
gridData.appendChild(item)
m.channelIndex[item.Id] = counter
counter = counter + 1
channelIdList = channelIdList + item.Id + ","
end for
m.scheduleGrid.content = gridData
m.scheduleGrid.content = gridData
m.LoadScheduleTask = createObject("roSGNode", "LoadScheduleTask")
m.LoadScheduleTask.observeField("schedule", "onScheduleLoaded")
m.LoadScheduleTask = createObject("roSGNode", "LoadScheduleTask")
m.LoadScheduleTask.observeField("schedule", "onScheduleLoaded")
m.LoadScheduleTask.startTime = m.gridStartDate.ToISOString()
m.LoadScheduleTask.endTime = m.gridEndDate.ToISOString()
m.LoadScheduleTask.channelIds = channelIdList
m.LoadScheduleTask.control = "RUN"
m.LoadScheduleTask.startTime = m.gridStartDate.ToISOString()
m.LoadScheduleTask.endTime = m.gridEndDate.ToISOString()
m.LoadScheduleTask.channelIds = channelIdList
m.LoadScheduleTask.control = "RUN"
m.LoadProgramDetailsTask = createObject("roSGNode", "LoadProgramDetailsTask")
m.LoadProgramDetailsTask.observeField("programDetails", "onProgramDetailsLoaded")
m.LoadProgramDetailsTask = createObject("roSGNode", "LoadProgramDetailsTask")
m.LoadProgramDetailsTask.observeField("programDetails", "onProgramDetailsLoaded")
m.scheduleGrid.setFocus(true)
if m.EPGLaunchCompleteSignaled = false
m.top.signalBeacon("EPGLaunchComplete") ' Required Roku Performance monitoring
m.EPGLaunchCompleteSignaled = true
end if
m.LoadChannelsTask.channels = []
m.scheduleGrid.setFocus(true)
if m.EPGLaunchCompleteSignaled = false
m.top.signalBeacon("EPGLaunchComplete") ' Required Roku Performance monitoring
m.EPGLaunchCompleteSignaled = true
end if
m.LoadChannelsTask.channels = []
end sub
' When LoadScheduleTask completes (initial or more data) and we have a schedule to display
@ -102,6 +118,7 @@ sub onScheduleLoaded()
m.scheduleGrid.showLoadingDataFeedback = false
m.scheduleGrid.setFocus(true)
m.LoadScheduleTask.schedule = []
m.spinner.visible = false
end sub
sub onProgramFocused()
@ -118,7 +135,9 @@ sub onProgramFocused()
m.top.focusedChannel = channel
' Exit if Channels not yet loaded
if channel = invalid or channel.getChildCount() = 0
m.detailsPane.programDetails = invalid
return
end if
@ -195,6 +214,22 @@ sub onWatchChannelSelected()
m.top.watchChannel = m.detailsPane.channel
end sub
' As user scrolls grid, check if more data requries to be loaded
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()
' Ensure the task is not already (still) running,
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()
m.LoadScheduleTask.control = "RUN"
end if
end if
end sub
' Handle user selecting "Record Channel" from Program Details
sub onRecordChannelSelected()
if m.detailsPane.recordSelectedChannel = false then return
@ -242,27 +277,18 @@ sub onRecordOperationDone()
end if
end sub
' As user scrolls grid, check if more data requries to be loaded
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()
' Ensure the task is not already (still) running,
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()
m.LoadScheduleTask.control = "RUN"
end if
end if
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
detailsGrp = m.top.findNode("detailsPane")
gridGrp = m.top.findNode("scheduleGrid")
if key = "back" and m.detailsPane.isInFocusChain()
if key = "back" and detailsGrp.isInFocusChain()
focusProgramDetails(false)
detailsGrp.setFocus(false)
gridGrp.setFocus(true)
return true
else if key = "back"
m.global.sceneManager.callFunc("popScene")
return true
end if

View File

@ -12,6 +12,7 @@
programTitleFocusedColor="#ffffff" iconFocusedColor="#ff0000"
showPastTimeScreen="true" pastTimeScreenBlendColor="#555555"
/>
<Spinner id="spinner" translation="[900, 450]" />
<Animation id="gridMoveAnimation" duration="1" repeat="false" easeFunction="outQuad" >
<Vector2DFieldInterpolator id="gridMoveAnimationPosition" key="[0.0, 0.5]" fieldToInterp="scheduleGrid.translation" />
</Animation>
@ -20,6 +21,7 @@
<field id="watchChannel" type="node" alwaysNotify="false" />
<field id="focusedChannel" type="node" alwaysNotify="false" />
<field id="filter" type="string" value="All" onChange="channelFilterSet" />
<field id="searchTerm" type="string" value="" onChange="channelsearchTermSet" />
</interface>
<script type="text/brightscript" uri="schedule.brs" />
</component>

View File

@ -46,8 +46,9 @@ sub itemContentChanged()
m.top.findNode("moviePoster").uri = m.top.itemContent.posterURL
' Set default video source
m.top.selectedVideoStreamId = itemData.MediaSources[0].id
if itemData.MediaSources <> invalid
m.top.selectedVideoStreamId = itemData.MediaSources[0].id
end if
' Find first Audio Stream and set that as default
SetDefaultAudioTrack(itemData)

View File

@ -408,7 +408,7 @@ sub onMetaDataLoaded()
if data <> invalid and data.count() > 0
' Use metadata to load backdrop image
if isvalid(data?.json?.ArtistItems?[0].id)
if isvalid(data?.json?.ArtistItems?[0]?.id)
m.LoadBackdropImageTask.itemId = data.json.ArtistItems[0].id
m.LoadBackdropImageTask.observeField("content", "onBackdropImageLoaded")
m.LoadBackdropImageTask.control = "RUN"

View File

@ -1,3 +1,60 @@
sub init()
m.top.optionsAvailable = false
m.searchSpinner = m.top.findnode("searchSpinner")
m.searchSelect = m.top.findnode("searchSelect")
m.searchTask = CreateObject("roSGNode", "SearchTask")
'set label text
m.searchHelpText = m.top.findNode("SearchHelpText")
m.searchHelpText.text = tr("You can search for Titles, People, Live TV Channels and more")
end sub
sub searchMedias()
query = m.top.searchAlpha
'if user deletes the search string hide the spinner
if query.len() = 0
m.searchSpinner.visible = false
end if
'if search task is running and user selectes another letter stop the search and load the next letter
m.searchTask.control = "stop"
if query <> invalid and query <> ""
m.searchSpinner.visible = true
end if
m.searchTask.observeField("results", "loadResults")
m.searchTask.query = query
m.top.overhangTitle = tr("Search") + ": " + query
m.searchTask.control = "RUN"
end sub
sub loadResults()
m.searchTask.unobserveField("results")
m.searchSpinner.visible = false
m.searchSelect.itemdata = m.searchTask.results
m.searchSelect.query = m.top.SearchAlpha
m.searchHelpText.visible = false
m.searchAlphabox = m.top.findnode("searchResults")
m.searchAlphabox.translation = "[470, 85]"
end sub
function onKeyEvent(key as string, press as boolean) as boolean
m.searchAlphabox = m.top.findNode("search_Key")
if m.searchAlphabox.textEditBox.hasFocus()
m.searchAlphabox.textEditBox.translation = "[0, -150]"
else
m.searchAlphabox.textEditBox.translation = "[0, 0]"
end if
if key = "left" and m.searchSelect.isinFocusChain() and (m.searchSelect.currFocusColumn = -1 or m.searchSelect.currFocusColumn = 0)
m.searchAlphabox.setFocus(true)
return true
else if key = "right"
m.searchSelect.setFocus(true)
return true
end if
return false
end function

View File

@ -1,13 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="SearchResults" extends="JFGroup">
<component name="searchResults" extends="JFGroup">
<children>
<SearchBox id="SearchBox" visible="true" translation="[960, 145]" />
<SearchRow id="SearchSelect" visible="false" />
<Rectangle width="1920" height="1080" color="#000000" opacity="0.75" />
<LayoutGroup layoutDirection="horiz" id="SearchAlphabox" translation="[70, 120]">
<SearchBox id="SearchBox" visible="true" focusable="true"/>
</LayoutGroup>
<LayoutGroup layoutDirection="vert" id="searchResults" translation="[470, 150]" >
<Label id = "SearchHelpText" text=""/>
<SearchRow id="searchSelect" visible="true" focusable="true"/>
</LayoutGroup>
<OptionsSlider id="options" />
<Spinner id = "searchSpinner" visible="false" translation="[1050, 500]"/>
</children>
<interface>
<field id="query" type="string" alwaysNotify="true" />
<field id="selectedItem" type="node" alwaysNotify="true" />
<field id="quickPlayNode" type="node" alwaysNotify="true" />
<field id="imageDisplayMode" type="string" value="scaleToZoom" />
<field id="searchAlpha" type="string" alias="SearchBox.search_values" alwaysNotify="true" onChange="searchMedias" />
</interface>
<script type="text/brightscript" uri="SearchResults.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Items.brs" />
<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" />
<script type="text/brightscript" uri="pkg:/source/utils/deviceCapabilities.brs" />
</component>

View File

@ -11,27 +11,28 @@ sub init()
' TODO - Define a failed to load image background
' m.top.failedBitmapURI
m.top.setFocus(true)
end sub
sub updateSize()
' In search results, rowSize only dictates how many are on screen at once
m.top.rowSize = 5
m.top.rowSize = 3
dimensions = m.top.getScene().currentDesignResolution
border = 75
border = 50
m.top.translation = [border, border + 115]
textHeight = 80
itemWidth = (dimensions["width"] - border * 2) / m.top.rowSize
itemHeight = itemWidth * 1.5 + textHeight
itemWidth = (dimensions["width"] - border) / 6
itemHeight = itemWidth + (textHeight / 2.3)
m.top.itemSize = [dimensions["width"] - border * 2, itemHeight]
m.top.itemSpacing = [0, 50]
m.top.itemSize = [1350, itemHeight] ' this is used for setting the row size
m.top.itemSpacing = [0, 105]
m.top.rowItemSize = [itemWidth, itemHeight]
m.top.rowItemSpacing = [0, 0]
m.top.numRows = 2
m.top.translation = "[12,18]"
end sub
function getData()
@ -45,16 +46,17 @@ function getData()
' 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", "TvChannel", "Episode", "AlbumArtist", "Album", "Audio", "Person"]
type_array = ["Movie", "Series", "TvChannel", "Episode", "MusicArtist", "MusicAlbum", "Audio", "Person", "PlaylistsFolder"]
content_types = {
"TvChannel": { "label": "Channels", "count": 0 },
"Movie": { "label": "Movies", "count": 0 },
"Series": { "label": "Shows", "count": 0 },
"Episode": { "label": "Episodes", "count": 0 },
"AlbumArtist": { "label": "Artists", "count": 0 },
"Album": { "label": "Albums", "count": 0 },
"MusicArtist": { "label": "Artists", "count": 0 },
"MusicAlbum": { "label": "Albums", "count": 0 },
"Audio": { "label": "Songs", "count": 0 },
"Person": { "label": "People", "count": 0 }
"Person": { "label": "People", "count": 0 },
"PlaylistsFolder": { "label": "Playlist", "count": 0 }
}
for each item in itemData.searchHints
@ -84,3 +86,4 @@ sub addRow(data, title, type_filter)
end if
end for
end sub

View File

@ -4,7 +4,12 @@
<field id="rowSize" type="int" />
<field id="itemData" type="assocarray" onChange="getData" />
<field id="query" type="string" />
<field id="itemSelected" type="int" />
<field id="itemSelected" type="node" alwaysNotify="true" />
</interface>
<script type="text/brightscript" uri="SearchRow.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Items.brs" />
<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" />
<script type="text/brightscript" uri="pkg:/source/utils/deviceCapabilities.brs" />
</component>

View File

@ -0,0 +1,9 @@
sub init()
m.top.functionName = "search"
end sub
sub search()
if m.top.query <> invalid and m.top.query <> ""
m.top.results = searchMedia(m.top.query)
end if
end sub

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="SearchTask" extends="Task">
<interface>
<field id="query" type="string" />
<field id="results" type="assocarray" />
</interface>
<script type="text/brightscript" uri="SearchTask.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Items.brs" />
<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" />
<script type="text/brightscript" uri="pkg:/source/utils/deviceCapabilities.brs" />
</component>

View File

@ -12,6 +12,9 @@ sub init()
m.path = m.top.findNode("path")
m.boolSetting = m.top.findNode("boolSetting")
m.integerSetting = m.top.findNode("integerSetting")
m.integerSetting.observeField("submit", "onKeyGridSubmit")
m.integerSetting.observeField("escape", "onKeyGridEscape")
m.settingsMenu.setFocus(true)
m.settingsMenu.observeField("itemFocused", "settingFocused")
@ -24,6 +27,17 @@ sub init()
LoadMenu({ children: m.configTree })
end sub
sub onKeyGridSubmit()
selectedSetting = m.userLocation.peek().children[m.settingsMenu.itemFocused]
set_user_setting(selectedSetting.settingName, m.integerSetting.text)
m.settingsMenu.setFocus(true)
end sub
sub onKeyGridEscape()
if m.integerSetting.escape = "left" or m.integerSetting.escape = "back"
m.settingsMenu.setFocus(true)
end if
end sub
sub LoadMenu(configSection)
@ -58,8 +72,6 @@ sub LoadMenu(configSection)
end for
end sub
sub settingFocused()
selectedSetting = m.userLocation.peek().children[m.settingsMenu.itemFocused]
@ -68,6 +80,7 @@ sub settingFocused()
' Hide Settings
m.boolSetting.visible = false
m.integerSetting.visible = false
if selectedSetting.type = invalid
return
@ -80,6 +93,12 @@ sub settingFocused()
else
m.boolSetting.checkedItem = 0
end if
else if selectedSetting.type = "integer"
integerValue = get_user_setting(selectedSetting.settingName, selectedSetting.default)
if isValid(integerValue)
m.integerSetting.text = integerValue
end if
m.integerSetting.visible = true
else
print "Unknown setting type " + selectedSetting.type
end if
@ -91,11 +110,13 @@ sub settingSelected()
selectedItem = m.userLocation.peek().children[m.settingsMenu.itemFocused]
if selectedItem.type <> invalid ' Show setting
if selectedItem.type = "bool"
m.boolSetting.setFocus(true)
end if
if selectedItem.type = "integer"
m.integerSetting.setFocus(true)
end if
else if selectedItem.children <> invalid and selectedItem.children.Count() > 0 ' Show sub menu
LoadMenu(selectedItem)
m.settingsMenu.setFocus(true)

View File

@ -2,50 +2,39 @@
<component name="Settings" extends="JFGroup">
<children>
<Label id="path" translation = "[95,175]" font="font:SmallestBoldSystemFont" />
<Label id="path" translation = "[95,175]" font="font:SmallestBoldSystemFont" />
<LabelList
<LabelList
translation = "[120,250]"
id = "settingsMenu"
itemSize = "[440,48]"
vertFocusAnimationStyle = "floatingFocus"
focusBitmapBlendColor = "#006fab"
focusedColor = "#ffffff"
itemSpacing = "[0,5]"
/>
itemSpacing = "[0,5]" />
<Poster
translation = "[710,250]"
id="testRectangle"
width="880"
height="700"
uri="pkg:/images/white.9.png"
blendColor = "#3f3f3f"
/>
<Poster
translation = "[710,250]" id="testRectangle" width="880" height="700" uri="pkg:/images/white.9.png"
blendColor = "#3f3f3f" />
<LayoutGroup
translation = "[1150,275]"
id="settingDetail"
vertAlignment="top"
horizAlignment="center"
itemSpacings="[50]"
>
<LayoutGroup translation = "[1150,275]" id="settingDetail" vertAlignment="top" horizAlignment="center" itemSpacings="[50]">
<ScrollingLabel font="font:LargeSystemFont" id="settingTitle" maxWidth="750" />
<ScrollingLabel font="font:LargeSystemFont" id="settingTitle" maxWidth="750" />
<Label id="settingDesc" width="750" wrap = "true" />
<Label id="settingDesc" width="750" wrap = "true" />
<RadioButtonList id="boolSetting" vertFocusAnimationStyle="floatingFocus">
<ContentNode role="content">
<ContentNode title="Disabled" />
<ContentNode title="Enabled" />
</ContentNode>
</RadioButtonList>
</LayoutGroup>
<RadioButtonList id="boolSetting" vertFocusAnimationStyle="floatingFocus">
<ContentNode role="content">
<ContentNode title="Disabled" />
<ContentNode title="Enabled" />
</ContentNode>
</RadioButtonList>
</LayoutGroup>
<intkeyboard_integerKeyboard translation="[900, 520]" id="integerSetting" maxLength="3" domain="numeric" visible="false" />
</children>
<script type="text/brightscript" uri="settings.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
</component>

BIN
images/icons/mic_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -112,9 +112,17 @@
<source>Home</source>
<translation>Основен</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Въведете потребителско име</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Въведете паролата</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Въведете стойност</translation>
<translation>Въведете стойност...</translation>
</message>
<message>
<source>Sort Field</source>

View File

@ -112,9 +112,17 @@
<source>Home</source>
<translation>Domů</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Zadejte jméno</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Zadejte heslo</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Zadejte hodnotu</translation>
<translation>Zadejte hodnotu...</translation>
</message>
<message>
<source>Sort Field</source>
@ -3302,7 +3310,7 @@
<translation>Speciální funkce</translation>
<message>
<source>Press &apos;OK&apos; to Close</source>
<translation>Press &apos;OK&apos; to Close</translation>
<translation>Pro zavřestiskni &apos;OK&apos;</translation>
</message>
</message>
<message>
@ -3402,5 +3410,104 @@
<source>Save Credentials?</source>
<translation>Uložit přihlašovací údaje?</translation>
</message>
<message>
<comment>Title of Tab for options to filter library content</comment>
<source>TAB_FILTER</source>
<translation>Filtr</translation>
</message>
<message>
<comment>Title of Tab for options to sort library content</comment>
<source>TAB_SORT</source>
<translation>Třídit</translation>
</message>
<message>
<comment>Title of Tab for switching &quot;views&quot; when looking at a library</comment>
<source>TAB_VIEW</source>
<translation>Pohled</translation>
</message>
<message>
<source>RUNTIME</source>
<translation>Délka</translation>
</message>
<message>
<source>RELEASE_DATE</source>
<translation>Datum vydání</translation>
</message>
<message>
<source>PLAY_COUNT</source>
<translation>Počet přehrání</translation>
</message>
<message>
<source>OFFICIAL_RATING</source>
<translation>Rodičovské hodnocení</translation>
</message>
<message>
<source>DATE_PLAYED</source>
<translation>Datum přehrání</translation>
</message>
<message>
<source>DATE_ADDED</source>
<translation>Datum přidání</translation>
</message>
<message>
<source>CRITIC_RATING</source>
<translation>Honocení kritiků</translation>
</message>
<message>
<source>IMDB_RATING</source>
<translation>Hodnocení IMDb</translation>
</message>
<message>
<comment>Name or Title field of media item</comment>
<source>TITLE</source>
<translation>Název</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>Tato %1 neobsahuje žádné položky</translation>
</message>
<message>
<source>On Now</source>
<translation>Nyní</translation>
</message>
<message>
<source>Delete Saved</source>
<translation>Smazat uložené</translation>
</message>
<message>
<source>Save Credentials?</source>
<translation>Uložit přihlašovací údaje?</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>Tato %1 neobsahuje žádné položky</translation>
</message>
<message>
<source>CRITIC_RATING</source>
<translation>Honocení kritiků</translation>
</message>
<message>
<source>IMDB_RATING</source>
<translation>Hodnocení IMDb</translation>
</message>
<message>
<comment>Name or Title field of media item</comment>
<source>TITLE</source>
<translation>Název</translation>
</message>
<message>
<source>On Now</source>
<translation>Nyní</translation>
</message>
<message>
<source>Delete Saved</source>
<translation>Smazat uložené</translation>
</message>
<message>
<source>Save Credentials?</source>
<translation>Uložit přihlašovací údaje?</translation>
</message>
</context>
</TS>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Home</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Benutzernamen eingeben</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Passwort eingeben</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Wert eingeben</translation>
@ -5915,7 +5923,7 @@
<translation>Extras</translation>
<message>
<source>Press &apos;OK&apos; to Close</source>
<translation>Press &apos;OK&apos; to Close</translation>
<translation>Drücke &apos;OK&apos; zum Schliessen</translation>
</message>
</message>
<message>
@ -6136,5 +6144,839 @@
<source>Change Server</source>
<translation>Server wechseln</translation>
</message>
<message>
<source>Record Series</source>
<translation>Serie aufnehmen</translation>
</message>
<message>
<source>Record</source>
<translation>Aufnehmen</translation>
</message>
<message>
<source>View Channel</source>
<translation>Kanal anzeigen</translation>
</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>Endet am</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>Endet 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 am</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>Begann am</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>Begonnen um</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>
<source>TV Shows</source>
<translation>Serien</translation>
</message>
<message>
<source>Movies</source>
<translation>Filme</translation>
</message>
<message>
<source>Special Features</source>
<translation>Extras</translation>
<message>
<source>Press &apos;OK&apos; to Close</source>
<translation>Drücke &quot;OK&quot; um zuschließen</translation>
</message>
</message>
<message>
<source>More Like This</source>
<translation>Mehr wie dieses</translation>
</message>
<message>
<source>Cast &amp; Crew</source>
<translation>Besetzung &amp; Mitwirkende</translation>
</message>
<message>
<source>Age</source>
<translation>Alter</translation>
</message>
<message>
<source>Died</source>
<translation>gestorben</translation>
</message>
<message>
<source>Born</source>
<translation>geboren</translation>
</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>Sortieren</translation>
</message>
<message>
<comment>Title of Tab for switching &quot;views&quot; 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>Wiedergabeanzahl</translation>
</message>
<message>
<source>OFFICIAL_RATING</source>
<translation>Altersfreigabe</translation>
</message>
<message>
<source>DATE_PLAYED</source>
<translation>Abgespielt am</translation>
</message>
<message>
<source>DATE_ADDED</source>
<translation>Hinzugefügt am</translation>
</message>
<message>
<source>CRITIC_RATING</source>
<translation>Kritikerbewertung</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>%1 enthält keine Elemente</translation>
</message>
<message>
<source>Unable to load Channel Data from the server</source>
<translation>Kanaldaten können nicht vom Server geladen werden</translation>
</message>
<message>
<source>Error loading Channel Data</source>
<translation>Fehler beim Laden von 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>Beim Abspielen 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 Abrufen der Daten für dieses Element vom Server ist ein Fehler aufgetreten.</translation>
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
</message>
<message>
<source>Error During Playback</source>
<translation>Fehler während der Wiedergabe</translation>
<extracomment>Dialog title when error occurs during playback</extracomment>
</message>
<message>
<source>Error Retrieving Content</source>
<translation>Fehler beim Abrufen des Inhalts</translation>
<extracomment>Dialog title when unable to load Content from Server</extracomment>
</message>
<message>
<source>On Now</source>
<translation>Läuft gerade</translation>
</message>
<message>
<source>Delete Saved</source>
<translation>Gespeichertes verwerfen</translation>
</message>
<message>
<source>Save Credentials?</source>
<translation>Anmeldedaten speichern?</translation>
</message>
<message>
<source>Sign Out</source>
<translation>Abmelden</translation>
</message>
<message>
<source>Change Server</source>
<translation>Server wechseln</translation>
</message>
<message>
<source>Pick a Jellyfin server from the local network</source>
<translation>Wähle einen Jellyfin-Server aus dem lokalen Netzwerk aus:</translation>
<extracomment>Instructions on initial app launch when the user is asked to pick a server from a list</extracomment>
</message>
<message>
<source>The requested content does not exist on the server</source>
<translation>Der gefragte Inhalt existiert nicht</translation>
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
</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 Besitzt keine Elemente</translation>
</message>
<message>
<source>Unable to load Channel Data from the server</source>
<translation>Die Kanaldaten könnten nicht abgerufen werden</translation>
</message>
<message>
<source>Error During Playback</source>
<translation>Fehler während der Wiedergabe</translation>
<extracomment>Dialog title when error occurs during playback</extracomment>
</message>
<message>
<source>Error Retrieving Content</source>
<translation>Fehler beim Abrufen des Inhaltes</translation>
<extracomment>Dialog title when unable to load Content from Server</extracomment>
</message>
<message>
<source>Use generated splashscreen image as Jellyfin&apos;s home background. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Nutze Jellyfin&apos;s Generierten Splashscreen als Bildschirmschoner Hintergrund. Jellyfin muss geschlossen und wieder geöffnet werden damit die Änderung in Kraft treten.</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Use the replay button to slowly animate to the first item in the folder. (If disabled, the folder will reset to the first item immediately).</source>
<translation>Nutze die Wiederholungstaste um langsam das erste Element in dem Ordner zu animieren. (Falls deaktiviert, Wird der Ordner sofort zum ersten Element zurückgesetzt).</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Always show the titles below the poster images. (If disabled, the title will be shown under the highlighted item only).</source>
<translation>Zeige immer die Title der Poster an. (Falls deaktiviert, der Title wird nur dem ausgewählten Element angezeigt).</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Delete Saved</source>
<translation>Gespeichertes verwerfen</translation>
</message>
<message>
<source>Set the maximum amount of days a show should stay in the &apos;Next Up&apos; list without watching it.</source>
<translation>Legen Sie die maximale Anzahl an Tagen fest, an denen eine Sendung in der Nächste Sendung-Liste bleiben soll, ohne sie anzusehen.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Options for Home Page.</source>
<translation>Einstellungen für die Startseite.</translation>
<extracomment>Description for Home Page user settings.</extracomment>
</message>
<message>
<source>Home Page</source>
<translation>Startseite</translation>
</message>
<message>
<source>Settings relating to how the application looks.</source>
<translation>Einstellungen zum Aussehen der Anwendung.</translation>
</message>
<message>
<source>Settings relating to playback and supported codec and media types.</source>
<translation>Einstellungen in Bezug auf Wiedergabe und unterstützte Codecs und Medientypen.</translation>
</message>
<message>
<source>Play Trailer</source>
<translation>Trailer abspielen</translation>
</message>
<message>
<source>Skip Intro</source>
<translation>Überspringe Intro</translation>
</message>
<message>
<source>Hides all clocks in Jellyfin. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Blendet alle Uhren in Jellyfin aus. Jellyfin muss geschlossen und wieder geöffnet werden, damit die Änderung wirksam wird.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Hide Clock</source>
<translation>Uhr ausblenden</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Cinema Mode brings the theater experience straight to your living room with the ability to play custom intros before the main feature.</source>
<translation>Kino Modus bringt dir ein Kinoerlebnis direkt zu dir in dein Wohnzimmer, mit der Fähigkeit eigene Intros abzuspielen.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Cinema Mode</source>
<translation>Kino Modus</translation>
<extracomment>Settings Menu - Title for option</extracomment>
</message>
<message>
<source>Use Splashscreen as Home Background</source>
<translation>Splashscreen als Hintergrund für die Startseite verwenden</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options that alter the design of Jellyfin.</source>
<translation>Optionen, die das Design von Jellyfin verändern.</translation>
<extracomment>Description for Design Elements user settings.</extracomment>
</message>
<message>
<source>Design Elements</source>
<translation>Designelemente</translation>
</message>
<message>
<source>Use generated splashscreen image as Jellyfin&apos;s screensaver background. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Nutze Jellyfin&apos;s Generierten Splashscreen als Bildschirmschoner Hintergrund. Jellyfin muss geschlossen und wieder geöffnet werden damit die Änderung in Kraft treten.</translation>
</message>
<message>
<source>Use Splashscreen as Screensaver Background</source>
<translation>Splashscreen als Bildschirmschoner-Hintergrund verwenden</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options for Jellyfin&apos;s screensaver.</source>
<translation>Optionen für Jellyfin&apos;s Bildschirmschoner.</translation>
<extracomment>Description for Screensaver user settings.</extracomment>
</message>
<message>
<source>Screensaver</source>
<translation>Bildschirmschoner</translation>
</message>
<message>
<source>If enabled, images of unwatched episodes will be blurred.</source>
<translation>Falls aktiviert, werden Bilder von nicht geschauten Folgen unkenntlich gemacht.</translation>
</message>
<message>
<source>Blur Unwatched Episodes</source>
<translation>Nicht geschaute Folgen unkenntlich machen</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options for TV Shows.</source>
<translation>Optionen für Serien.</translation>
<extracomment>Description for TV Shows user settings.</extracomment>
</message>
<message>
<source>Hides tagline text on details pages.</source>
<translation>Blendet Slogan-Text auf Detailseiten aus.</translation>
</message>
<message>
<source>Hide Taglines</source>
<translation>Slogan ausblenden</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options for Details pages.</source>
<translation>Optionen für Detailseiten.</translation>
<extracomment>Description for Details page user settings.</extracomment>
</message>
<message>
<source>Details Page</source>
<translation>Detailseite</translation>
</message>
<message>
<source>Return to Top</source>
<translation>Zurück nach oben</translation>
<extracomment>UI -&gt; Media Grid -&gt; Item Title in user setting screen.</extracomment>
</message>
<message>
<source>Shows</source>
<translation>Serien</translation>
</message>
<message>
<source>Studios</source>
<translation>Studios</translation>
</message>
<message>
<source>Networks</source>
<translation>Netzwerke</translation>
</message>
<message>
<source>There was an error authenticating via Quick Connect.</source>
<translation>Fehler beim Authentifizierung über Quick Connect.</translation>
</message>
<message>
<source>(Dialog will close automatically)</source>
<translation>(Der Dialog wird automatisch geschlossen)</translation>
</message>
<message>
<source>Here is your Quick Connect code:</source>
<translation>Hier ist dein Quick Connect Code:</translation>
</message>
<message>
<source>Quick Connect</source>
<translation>Quick Connect</translation>
</message>
<message>
<source>%1 of %2</source>
<translation>%1 von %2</translation>
<extracomment>Item position and count. %1 = current item. %2 = total number of items</extracomment>
</message>
<message>
<source>You can search for Titles, People, Live TV Channels and more</source>
<translation>Du kannst nach Titeln, Leuten, Live Fernsehen Känale und mehr suchen</translation>
<extracomment>Help text in search results</extracomment>
</message>
<message>
<source>Search now</source>
<translation>Suche jetzt</translation>
<extracomment>Help text in search Box</extracomment>
</message>
<message>
<source>Use voice remote to search</source>
<translation>Verwenden Sie die Sprachfernbedienung, um zu suchen</translation>
<extracomment>Help text in search voice text box</extracomment>
</message>
<message>
<source>Go to episode</source>
<translation>Zur Episode gehen</translation>
<extracomment>Continue Watching Popup Menu - Navigate to the Episode Detail Page</extracomment>
</message>
<message>
<source>Go to season</source>
<translation>Zur Staffel gehen</translation>
<extracomment>Continue Watching Popup Menu - Navigate to the Season Page</extracomment>
</message>
<message>
<source>Go to series</source>
<translation>Zur Serie gehen</translation>
<extracomment>Continue Watching Popup Menu - Navigate to the Series Detail Page</extracomment>
</message>
<message>
<source>Set Watched</source>
<translation>Als gesehen markieren</translation>
<extracomment>Button Text - When pressed, marks item as Warched</extracomment>
</message>
<message>
<source>Set Favorite</source>
<translation>Als Favorit markieren</translation>
<extracomment>Button Text - When pressed, sets item as Favorite</extracomment>
</message>
<message>
<source>Show item count in the library and index of selected item.</source>
<translation>Zeigt die Anzahl der Elemente in der Bibliothek und den Index des ausgewählten Elements an.</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Item Count</source>
<translation>Anzahl</translation>
<extracomment>UI -&gt; Media Grid -&gt; Item Count in user setting screen.</extracomment>
</message>
<message>
<source>Item Titles</source>
<translation>Artikel Namen</translation>
<extracomment>UI -&gt; Media Grid -&gt; Item Title in user setting screen.</extracomment>
</message>
<message>
<source>Media Grid options.</source>
<translation>Medienraster Einstellungen.</translation>
</message>
<message>
<source>Media Grid</source>
<translation>Medienraster</translation>
<extracomment>UI -&gt; Media Grid section in user setting screen.</extracomment>
</message>
<message>
<source>User Interface</source>
<translation>Benutzeroberfläche</translation>
<extracomment>Title for User Interface section in user setting screen.</extracomment>
</message>
<message>
<source>Disabled</source>
<translation>Deaktiviert</translation>
</message>
<message>
<source>Enabled</source>
<translation>Aktiviert</translation>
</message>
<message>
<source>Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.</source>
<translation type="unfinished">Unterstützung von Direkt Abspielen von MPEG-2 Inhalte (z.B., Live Fernsehen). Dadurch wird die Transcodierung von MPEG-2-Inhalten verhindert, aber es nutzt bedeutend mehr Bandbreite.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>MPEG-2 Support</source>
<translation>MPEG-2 Unterstützung</translation>
<extracomment>Settings Menu - Title for option</extracomment>
</message>
<message>
<source>Playback</source>
<translation>Wiedergabe</translation>
<extracomment>Title for Playback section in user setting screen.</extracomment>
</message>
<message>
<source>Version</source>
<translation>Version</translation>
</message>
<message>
<source>An error was encountered while playing this item. Server did not provide required transcoding data.</source>
<translation>Beim Abspielen des Titels ist ein Fehler aufgetreten. Der Server hat zur Transkodierung erforderliche Daten nicht zur Verfügung gestellt.</translation>
<extracomment>Content of message box when trying to play an item which requires transcoding, and the server did not provide transcode url</extracomment>
</message>
<message>
<source>Error Getting Playback Information</source>
<translation>Fehler beim Abrufen von Wiedergabe-Informationen</translation>
<extracomment>Dialog Title: Received error from server when trying to get information about the selected item for playback</extracomment>
</message>
<message>
<source>...or enter server URL manually:</source>
<translation>Falls kein Server angezeigt wird, versuche die URL per Hand einzugeben:</translation>
<extracomment>Instructions on initial app launch when the user is asked to manually enter a server URL</extracomment>
</message>
<message>
<source>Enter the server name or ip address</source>
<translation>Gebe den Servernamen oder die IP-Adresse ein</translation>
<extracomment>Title of KeyboardDialog when manually entering a server URL</extracomment>
</message>
<message>
<source>Unknown</source>
<translation>Unbekannt</translation>
<extracomment>Title for a cast member for which we have no information for</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>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>Close</source>
<translation>Schließen</translation>
</message>
<message>
<source>Cancel Series Recording</source>
<translation>Serienaufnahme abbrechen</translation>
</message>
<message>
<source>Cancel Recording</source>
<translation>Aufnahme abbrechen</translation>
</message>
<message>
<source>Record Series</source>
<translation>Serie aufnehmen</translation>
</message>
<message>
<source>Record</source>
<translation>Aufnehmen</translation>
</message>
<message>
<source>View Channel</source>
<translation>Kanal anzeigen</translation>
</message>
<message>
<source>TV Guide</source>
<translation>Fernsehprogramm</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>Endet am</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>Endet 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 am</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>Gestartet</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>Begonnen um</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>
<source>TV Shows</source>
<translation>Serien</translation>
</message>
<message>
<source>Movies</source>
<translation>Filme</translation>
</message>
<message>
<source>Special Features</source>
<translation>Extras Features</translation>
<message>
<source>Press &apos;OK&apos; to Close</source>
<translation>Press &apos;OK&apos; to Close</translation>
</message>
</message>
<message>
<source>More Like This</source>
<translation>Mehr wie dieses</translation>
</message>
<message>
<source>Cast &amp; Crew</source>
<translation>Besetzung &amp; Mitwirkende</translation>
</message>
<message>
<source>Age</source>
<translation>Alter</translation>
</message>
<message>
<source>Died</source>
<translation>gestorben</translation>
</message>
<message>
<source>Born</source>
<translation>geboren</translation>
</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>Sortieren</translation>
</message>
<message>
<comment>Title of Tab for switching &quot;views&quot; 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>Wiedergabeanzahl</translation>
</message>
<message>
<source>OFFICIAL_RATING</source>
<translation>Altersfreigabe</translation>
</message>
<message>
<source>DATE_PLAYED</source>
<translation>Abgespielt am</translation>
</message>
<message>
<source>DATE_ADDED</source>
<translation>Hinzugefügt am</translation>
</message>
<message>
<source>CRITIC_RATING</source>
<translation>Kritikerbewertung</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>
<source>Error loading Channel Data</source>
<translation>Fehler beim Laden von 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>Ein Fehler ist aufgetreten beim Wiedergeben des Elementes.</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 Abrufen der Daten ist ein Fehler aufgetreten.</translation>
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
</message>
<message>
<source>On Now</source>
<translation>Läuft gerade</translation>
</message>
<message>
<source>Save Credentials?</source>
<translation>Anmeldedaten speichern?</translation>
</message>
<message>
<source>Sign Out</source>
<translation>Abmelden</translation>
</message>
<message>
<source>Change Server</source>
<translation>Server wechseln</translation>
</message>
</context>
</TS>

View File

@ -113,8 +113,12 @@
<translation>Home</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Enter a value</translation>
<source>Enter a username</source>
<translation>Enter a username</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Enter a password</translation>
</message>
<message>
<source>Name</source>
@ -374,7 +378,7 @@
</message>
<message>
<source>Pick a Jellyfin server from the local network</source>
<translation>Pick a Jellyfin server from the local network</translation>
<translation>Select an available Jellyfin server from your local network:</translation>
<extracomment>Instructions on initial app launch when the user is asked to pick a server from a list</extracomment>
</message>
<message>
@ -384,7 +388,7 @@
</message>
<message>
<source>...or enter server URL manually:</source>
<translation>or enter server URL manually:</translation>
<translation>If no server is listed above, you may also enter the server URL manually:</translation>
<extracomment>Instructions on initial app launch when the user is asked to manually enter a server URL</extracomment>
</message>
<message>
@ -844,5 +848,379 @@
<source>Save Credentials?</source>
<translation>Save Credentials?</translation>
</message>
<message>
<source>Set the maximum amount of days a show should stay in the &apos;Next Up&apos; list without watching it.</source>
<translation>Set the maximum amount of days a show should stay in the &apos;Next Up&apos; list without watching it.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Max Days Next Up</source>
<translation>Max Days Next Up</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options for Home Page.</source>
<translation>Options for Home Page.</translation>
<extracomment>Description for Home Page user settings.</extracomment>
</message>
<message>
<source>Home Page</source>
<translation>Home Page</translation>
</message>
<message>
<source>Settings relating to how the application looks.</source>
<translation>Settings relating to how the application looks.</translation>
</message>
<message>
<source>Settings relating to playback and supported codec and media types.</source>
<translation>Settings relating to playback and supported codec and media types.</translation>
</message>
<message>
<source>Play Trailer</source>
<translation>Play Trailer</translation>
</message>
<message>
<source>Skip Intro</source>
<translation>Skip Intro</translation>
</message>
<message>
<source>Hides all clocks in Jellyfin. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Hides all clocks in Jellyfin. Jellyfin will need to be closed and reopened for change to take effect.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Hide Clock</source>
<translation>Hide Clock</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Cinema Mode brings the theater experience straight to your living room with the ability to play custom intros before the main feature.</source>
<translation>Cinema Mode brings the theatre experience straight to your living room with the ability to play custom intros before the main feature.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Cinema Mode</source>
<translation>Cinema Mode</translation>
<extracomment>Settings Menu - Title for option</extracomment>
</message>
<message>
<source>Use generated splashscreen image as Jellyfin&apos;s home background. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Use generated splashscreen image as Jellyfin&apos;s home background. Jellyfin will need to be closed and reopened for change to take effect.</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Use Splashscreen as Home Background</source>
<translation>Use Splashscreen as Home Background</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options that alter the design of Jellyfin.</source>
<translation>Options that alter the design of Jellyfin.</translation>
<extracomment>Description for Design Elements user settings.</extracomment>
</message>
<message>
<source>Design Elements</source>
<translation>Design Elements</translation>
</message>
<message>
<source>Use generated splashscreen image as Jellyfin&apos;s screensaver background. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Use generated splashscreen image as Jellyfin&apos;s screensaver background. Jellyfin will need to be closed and reopened for change to take effect.</translation>
</message>
<message>
<source>Use Splashscreen as Screensaver Background</source>
<translation>Use Splashscreen as Screensaver Background</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options for Jellyfin&apos;s screensaver.</source>
<translation>Options for Jellyfin&apos;s screensaver.</translation>
<extracomment>Description for Screensaver user settings.</extracomment>
</message>
<message>
<source>Screensaver</source>
<translation>Screensaver</translation>
</message>
<message>
<source>If enabled, images of unwatched episodes will be blurred.</source>
<translation>If enabled, images of unwatched episodes will be blurred.</translation>
</message>
<message>
<source>Blur Unwatched Episodes</source>
<translation>Blur Unwatched Episodes</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options for TV Shows.</source>
<translation>Options for TV Shows.</translation>
<extracomment>Description for TV Shows user settings.</extracomment>
</message>
<message>
<source>Hides tagline text on details pages.</source>
<translation>Hides tagline text on details pages.</translation>
</message>
<message>
<source>Hide Taglines</source>
<translation>Hide Taglines</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Options for Details pages.</source>
<translation>Options for Details pages.</translation>
<extracomment>Description for Details page user settings.</extracomment>
</message>
<message>
<source>Details Page</source>
<translation>Details Page</translation>
</message>
<message>
<source>Use the replay button to slowly animate to the first item in the folder. (If disabled, the folder will reset to the first item immediately).</source>
<translation>Use the replay button to slowly animate to the first item in the folder. (If disabled, the folder will reset to the first item immediately).</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Return to Top</source>
<translation>Return to Top</translation>
<extracomment>UI -&gt; Media Grid -&gt; Item Title in user setting screen.</extracomment>
</message>
<message>
<source>Shows</source>
<translation>Shows</translation>
</message>
<message>
<source>Studios</source>
<translation>Studios</translation>
</message>
<message>
<source>Networks</source>
<translation>Networks</translation>
</message>
<message>
<source>There was an error authenticating via Quick Connect.</source>
<translation>There was an error authenticating via Quick Connect.</translation>
</message>
<message>
<source>(Dialog will close automatically)</source>
<translation>(Dialog will close automatically)</translation>
</message>
<message>
<source>Here is your Quick Connect code:</source>
<translation>Here is your Quick Connect code:</translation>
</message>
<message>
<source>Quick Connect</source>
<translation>Quick Connect</translation>
</message>
<message>
<source>%1 of %2</source>
<translation>%1 of %2</translation>
<extracomment>Item position and count. %1 = current item. %2 = total number of items</extracomment>
</message>
<message>
<source>You can search for Titles, People, Live TV Channels and more</source>
<translation>You can search for Titles, People, Live TV Channels and more</translation>
<extracomment>Help text in search results</extracomment>
</message>
<message>
<source>Search now</source>
<translation>Search now</translation>
<extracomment>Help text in search Box</extracomment>
</message>
<message>
<source>Use voice remote to search</source>
<translation>Use voice remote to search</translation>
<extracomment>Help text in search voice text box</extracomment>
</message>
<message>
<source>Go to episode</source>
<translation>Go to episode</translation>
<extracomment>Continue Watching Popup Menu - Navigate to the Episode Detail Page</extracomment>
</message>
<message>
<source>Go to season</source>
<translation>Go to season</translation>
<extracomment>Continue Watching Popup Menu - Navigate to the Season Page</extracomment>
</message>
<message>
<source>Go to series</source>
<translation>Go to series</translation>
<extracomment>Continue Watching Popup Menu - Navigate to the Series Detail Page</extracomment>
</message>
<message>
<source>Set Watched</source>
<translation>Set Watched</translation>
<extracomment>Button Text - When pressed, marks item as Warched</extracomment>
</message>
<message>
<source>Set Favorite</source>
<translation>Set Favourite</translation>
<extracomment>Button Text - When pressed, sets item as Favorite</extracomment>
</message>
<message>
<source>Show item count in the library and index of selected item.</source>
<translation>Show item count in the library and index of selected item.</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Item Count</source>
<translation>Item Count</translation>
<extracomment>UI -&gt; Media Grid -&gt; Item Count in user setting screen.</extracomment>
</message>
<message>
<source>Always show the titles below the poster images. (If disabled, the title will be shown under the highlighted item only).</source>
<translation>Always show the titles below the poster images. (If disabled, the title will be shown under the highlighted item only).</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Item Titles</source>
<translation>Item Titles</translation>
<extracomment>UI -&gt; Media Grid -&gt; Item Title in user setting screen.</extracomment>
</message>
<message>
<source>Media Grid options.</source>
<translation>Media Grid options.</translation>
</message>
<message>
<source>Media Grid</source>
<translation>Media Grid</translation>
<extracomment>UI -&gt; Media Grid section in user setting screen.</extracomment>
</message>
<message>
<source>User Interface</source>
<translation>User Interface</translation>
<extracomment>Title for User Interface section in user setting screen.</extracomment>
</message>
<message>
<source>Disabled</source>
<translation>Disabled</translation>
</message>
<message>
<source>Enabled</source>
<translation>Enabled</translation>
</message>
<message>
<source>Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.</source>
<translation>Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>MPEG-2 Support</source>
<translation>MPEG-2 Support</translation>
<extracomment>Settings Menu - Title for option</extracomment>
</message>
<message>
<source>Playback</source>
<translation>Playback</translation>
<extracomment>Title for Playback section in user setting screen.</extracomment>
</message>
<message>
<source>Version</source>
<translation>Version</translation>
</message>
<message>
<source>An error was encountered while playing this item. Server did not provide required transcoding data.</source>
<translation>An error was encountered while playing this item. Server did not provide required transcoding data.</translation>
<extracomment>Content of message box when trying to play an item which requires transcoding, and the server did not provide transcode url</extracomment>
</message>
<message>
<source>Error Getting Playback Information</source>
<translation>Error Getting Playback Information</translation>
<extracomment>Dialog Title: Received error from server when trying to get information about the selected item for playback</extracomment>
</message>
<message>
<source>...or enter server URL manually:</source>
<translation>If no server is listed above, you may also enter the server URL manually:</translation>
<extracomment>Instructions on initial app launch when the user is asked to manually enter a server URL</extracomment>
</message>
<message>
<source>Pick a Jellyfin server from the local network</source>
<translation>Select an available Jellyfin server from your local network:</translation>
<extracomment>Instructions on initial app launch when the user is asked to pick a server from a list</extracomment>
</message>
<message>
<source>Enter the server name or ip address</source>
<translation>Enter the server name or ip address</translation>
<extracomment>Title of KeyboardDialog when manually entering a server URL</extracomment>
</message>
<message>
<source>Unknown</source>
<translation>Unknown</translation>
<extracomment>Title for a cast member for which we have no information for</extracomment>
</message>
<message>
<source>Close</source>
<translation>Close</translation>
</message>
<message>
<source>Cancel Series Recording</source>
<translation>Cancel Series Recording</translation>
</message>
<message>
<source>Cancel Recording</source>
<translation>Cancel Recording</translation>
</message>
<message>
<source>Record Series</source>
<translation>Record Series</translation>
</message>
<message>
<source>Record</source>
<translation>Record</translation>
</message>
<message>
<source>View Channel</source>
<translation>View Channel</translation>
</message>
<message>
<source>TV Shows</source>
<translation>TV Shows</translation>
</message>
<message>
<source>Movies</source>
<translation>Movies</translation>
</message>
<message>
<source>Special Features</source>
<translation>Special Features</translation>
<message>
<source>Press &apos;OK&apos; to Close</source>
<translation>Press &apos;OK&apos; to Close</translation>
</message>
</message>
<message>
<source>More Like This</source>
<translation>More Like This</translation>
</message>
<message>
<source>Cast &amp; Crew</source>
<translation>Cast &amp; Crew</translation>
</message>
<message>
<source>Age</source>
<translation>Age</translation>
</message>
<message>
<source>Died</source>
<translation>Died</translation>
</message>
<message>
<source>Born</source>
<translation>Born</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Enter a value...</translation>
</message>
<message>
<source>On Now</source>
<translation>On Now</translation>
</message>
<message>
<source>Delete Saved</source>
<translation>Delete Saved</translation>
</message>
<message>
<source>Save Credentials?</source>
<translation>Save Credentials?</translation>
</message>
</context>
</TS>

View File

@ -125,8 +125,12 @@
<translation>Home</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Enter a value</translation>
<source>Enter a username</source>
<translation>Enter a username</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Enter a password</translation>
</message>
<message>
<source>Name</source>
@ -448,12 +452,12 @@
</message>
<message>
<source>Pick a Jellyfin server from the local network</source>
<translation>Pick a Jellyfin server from the local network</translation>
<translation>Select an available Jellyfin server from your local network:</translation>
<extracomment>Instructions on initial app launch when the user is asked to pick a server from a list</extracomment>
</message>
<message>
<source>...or enter server URL manually:</source>
<translation>or enter server URL manually:</translation>
<translation>If no server is listed above, you may also enter the server URL manually:</translation>
<extracomment>Instructions on initial app launch when the user is asked to manually enter a server URL</extracomment>
</message>
<message>
@ -476,13 +480,13 @@
<extracomment>Title for Playback section in user setting screen.</extracomment>
</message>
<message>
<source>MPEG 2 Support</source>
<translation>MPEG 2 Support</translation>
<source>MPEG-2 Support</source>
<translation>MPEG-2 Support</translation>
<extracomment>Settings Menu - Title for option</extracomment>
</message>
<message>
<source>Support direct play of MPEG 2 content (e.g. Live TV). This will prevent transcoding of MPEG 2 content, but uses significantly more bandwidth</source>
<translation>Support direct play of MPEG 2 content (e.g. Live TV). This will prevent transcoding of MPEG 2 content, but uses significantly more bandwidth</translation>
<source>Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.</source>
<translation>Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
@ -504,8 +508,8 @@
<extracomment>UI -&gt; Media Grid section in user setting screen.</extracomment>
</message>
<message>
<source>Media Grid Options</source>
<translation>Media Grid Options</translation>
<source>Media Grid options.</source>
<translation>Media Grid options.</translation>
</message>
<message>
<source>Item Titles</source>
@ -513,8 +517,8 @@
<extracomment>UI -&gt; Media Grid -&gt; Item Title in user setting screen.</extracomment>
</message>
<message>
<source>Always show the titles below the poster images. (If disabled, title will be shown under hilighted item only)</source>
<translation>Always show the titles below the poster images. (If disabled, title will be shown under hilighted item only)</translation>
<source>Always show the titles below the poster images. (If disabled, the title will be shown under the highlighted item only).</source>
<translation>Always show the titles below the poster images. (If disabled, the title will be shown under the highlighted item only).</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
@ -523,8 +527,8 @@
<extracomment>UI -&gt; Media Grid -&gt; Item Count in user setting screen.</extracomment>
</message>
<message>
<source>Show item count in the library, and index of selected item.</source>
<translation>Show item count in the library, and index of selected item.</translation>
<source>Show item count in the library and index of selected item.</source>
<translation>Show item count in the library and index of selected item.</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
@ -552,6 +556,21 @@
<translation>Go to episode</translation>
<extracomment>Continue Watching Popup Menu - Navigate to the Episode Detail Page</extracomment>
</message>
<message>
<source>Use voice remote to search</source>
<translation>Use voice remote to search</translation>
<extracomment>Help text in search voice text box</extracomment>
</message>
<message>
<source>Search now</source>
<translation>Search now</translation>
<extracomment>Help text in search Box</extracomment>
</message>
<message>
<source>You can search for Titles, People, Live TV Channels and more</source>
<translation>You can search for Titles, People, Live TV Channels and more</translation>
<extracomment>Help text in search results</extracomment>
</message>
<message>
<source>%1 of %2</source>
<translation>%1 of %2</translation>
@ -591,18 +610,18 @@
<extracomment>UI -&gt; Media Grid -&gt; Item Title in user setting screen.</extracomment>
</message>
<message>
<source>Use the replay button to slowly animate to the first item in the folder. (If disabled, The folder will reset to the first item immediately)</source>
<translation>Use the replay button to slowly animate to the first item in the folder. (If disabled, The folder will reset to the first item immediately)</translation>
<source>Use the replay button to slowly animate to the first item in the folder. (If disabled, the folder will reset to the first item immediately).</source>
<translation>Use the replay button to slowly animate to the first item in the folder. (If disabled, the folder will reset to the first item immediately).</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Detail Page</source>
<translation>Detail Page</translation>
<source>Details Page</source>
<translation>Details Page</translation>
</message>
<message>
<source>Options for details pages.</source>
<translation>Options for details pages.</translation>
<extracomment>Description for Detail Page user settings.</extracomment>
<source>Options for Details pages.</source>
<translation>Options for Details pages.</translation>
<extracomment>Description for Details page user settings.</extracomment>
</message>
<message>
<source>Hide Taglines</source>
@ -624,8 +643,8 @@
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>If enabled, images for unwatched episodes will be blurred.</source>
<translation>If enabled, images for unwatched episodes will be blurred.</translation>
<source>If enabled, images of unwatched episodes will be blurred.</source>
<translation>If enabled, images of unwatched episodes will be blurred.</translation>
</message>
<message>
<source>Screensaver</source>
@ -642,8 +661,8 @@
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Use generated splashscreen image as Jellyfin&apos;s screensaver background.</source>
<translation>Use generated splashscreen image as Jellyfin&apos;s screensaver background.</translation>
<source>Use generated splashscreen image as Jellyfin&apos;s screensaver background. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Use generated splashscreen image as Jellyfin&apos;s screensaver background. Jellyfin will need to be closed and reopened for change to take effect.</translation>
</message>
<message>
<source>Design Elements</source>
@ -660,18 +679,18 @@
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Use generated splashscreen image as Jellyfin home background. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Use generated splashscreen image as Jellyfin home background. Jellyfin will need to be closed and reopened for change to take effect.</translation>
<source>Use generated splashscreen image as Jellyfin&apos;s home background. Jellyfin will need to be closed and reopened for change to take effect.</source>
<translation>Use generated splashscreen image as Jellyfin&apos;s home background. Jellyfin will need to be closed and reopened for change to take effect.</translation>
<extracomment>Description for option in Setting Screen</extracomment>
</message>
<message>
<source>Cinema mode</source>
<translation>Cinema mode</translation>
<source>Cinema Mode</source>
<translation>Cinema Mode</translation>
<extracomment>Settings Menu - Title for option</extracomment>
</message>
<message>
<source>Cinema mode brings the theater experience straight to your living room with the ability to play custom intros before the main feature.</source>
<translation>Cinema mode brings the theater experience straight to your living room with the ability to play custom intros before the main feature.</translation>
<source>Cinema Mode brings the theater experience straight to your living room with the ability to play custom intros before the main feature.</source>
<translation>Cinema Mode brings the theater experience straight to your living room with the ability to play custom intros before the main feature.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
@ -684,10 +703,6 @@
<translation>Hides all clocks in Jellyfin. Jellyfin will need to be closed and reopened for change to take effect.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Skip Intro</source>
<translation>Skip Intro</translation>
</message>
<message>
<source>Play Trailer</source>
<translation>Play Trailer</translation>
@ -712,5 +727,109 @@
<translation>Attempt Direct Play for HEVC media with unsupported profile levels (> 5.1) before falling back to trancoding if it fails.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<source>Settings relating to playback and supported codec and media types.</source>
<translation>Settings relating to playback and supported codec and media types.</translation>
</message>
<message>
<source>Settings relating to how the application looks.</source>
<translation>Settings relating to how the application looks.</translation>
</message>
<message>
<source>Home Page</source>
<translation>Home Page</translation>
</message>
<message>
<source>Options for Home Page.</source>
<translation>Options for Home Page.</translation>
<extracomment>Description for Home Page user settings.</extracomment>
</message>
<message>
<source>Max Days Next Up</source>
<translation>Max Days Next Up</translation>
<extracomment>Option Title in user setting screen</extracomment>
</message>
<message>
<source>Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.</source>
<translation>Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.</translation>
<extracomment>Settings Menu - Description for option</extracomment>
</message>
<message>
<source>Playback Information</source>
<translation>Playback Information</translation>
</message>
<message>
<source>Transcoding Information</source>
<translation>Transcoding Information</translation>
</message>
<message>
<source>Reason</source>
<translation>Reason</translation>
</message>
<message>
<source>Video Codec</source>
<translation>Video Codec</translation>
</message>
<message>
<source>Audio Codec</source>
<translation>Audio Codec</translation>
</message>
<message>
<source>direct</source>
<translation>direct</translation>
</message>
<message>
<source>Total Bitrate</source>
<translation>Total Bitrate</translation>
</message>
<message>
<source>Audio Channels</source>
<translation>Audio Channels</translation>
</message>
<message>
<source>Stream Information</source>
<translation>Stream Information</translation>
</message>
<message>
<source>Codec</source>
<translation>Codec</translation>
</message>
<message>
<source>Codec Tag</source>
<translation>Codec Tag</translation>
</message>
<message>
<source>Level</source>
<translation>Level</translation>
<extracomment>Video profile level</extracomment>
</message>
<message>
<source>Bit Rate</source>
<translation>Bit Rate</translation>
<extracomment>Video streaming bit rate</extracomment>
</message>
<message>
<source>Container</source>
<translation>Container</translation>
<extracomment>Video streaming container</extracomment>
</message>
<message>
<source>Size</source>
<translation>Size</translation>
<extracomment>Video size</extracomment>
</message>
<message>
<source>Video range type</source>
<translation>Video range type</translation>
</message>
<message>
<source>Pixel format</source>
<translation>Pixel format</translation>
<extracomment>Video pixel format</extracomment>
</message>
<message>
<source>WxH</source>
<translation>WxH</translation>
<extracomment>Video width x height</extracomment>
</message>
</context>
</TS>

View File

@ -112,9 +112,17 @@
<source>Home</source>
<translation>Inicio</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Ingresar nombre de usuario</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Ingresar la contraseña</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Ingresar un valor</translation>
<translation>Ingresar un valor...</translation>
</message>
<message>
<source>Sort Field</source>

View File

@ -112,9 +112,17 @@
<source>Home</source>
<translation>Inicio</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Ingres nombre de usuario</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Ingres la contraseña</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Ingrese un valor</translation>
<translation>Ingres un valor...</translation>
</message>
<message>
<source>Sort Field</source>

View File

@ -113,8 +113,12 @@
<translation>Inicio</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Enter a value...</translation>
<source>Enter a username</source>
<translation>Ingres nombre de usuario</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Ingres la contraseña</translation>
</message>
<message>
<source>Sort Field</source>
@ -168,9 +172,17 @@
<source>Audio</source>
<translation>Audio</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Ingresar nombre de usuario</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Ingresar la contraseña</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Introduce un valor</translation>
<translation>Ingresar un valor...</translation>
</message>
<message>
<source>Sort Field</source>
@ -1867,5 +1879,10 @@
<source>Change Server</source>
<translation>Cambiar de Servidor</translation>
</message>
<message>
<comment>Name or Title field of media item</comment>
<source>TITLE</source>
<translation>Nombre</translation>
</message>
</context>
</TS>

View File

@ -111,6 +111,14 @@
<message>
<source>Home</source>
<translation>Accueil</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Entrez votre nom d'utilisateur</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Entrer le mot de passe</translation>
</message>
<message>
<source>Enter a value...</source>
@ -2700,5 +2708,22 @@
<source>Save Credentials?</source>
<translation>Enregistrer les identifiants&#xa0;?</translation>
</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>On Now</source>
<translation>Maintenant</translation>
</message>
<message>
<source>Delete Saved</source>
<translation>Supprimer les informations enregistrées</translation>
</message>
<message>
<source>Save Credentials?</source>
<translation>Enregistrer les identifiants&#xa0;?</translation>
</message>
</context>
</TS>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Accueil</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Entrez votre nom d'utilisateur</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Entrer le mot de passe</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Entrez une valeur</translation>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Kezdőlap</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Adjon meg egy felhasználónevet</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Írja be a jelszót</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Adj meg egy értéket</translation>
@ -6325,5 +6333,13 @@ elemeket</translation>
<source>Save Credentials?</source>
<translation>Mented a hitelesítő adatokat?</translation>
</message>
<message>
<source>Delete Saved</source>
<translation>Mentettek Törlése</translation>
</message>
<message>
<source>Save Credentials?</source>
<translation>Mented a hitelesítő adatokat?</translation>
</message>
</context>
</TS>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Home</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Inserisci il tuo cognome</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Inserisci la password</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Inserire un valore</translation>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Mājas</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Ievadiet lietotājvārdu</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Ievadiet paroli</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Ievadi vērtību</translation>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Início</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Insira nome de usuário</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Insira um senha</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Insira um valor</translation>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Acasă</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Introduceți un nume de utilizator</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Introduceți o parolă</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Introduceți o valoare</translation>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Domov</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Zadajte používateľské meno</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Zadajte heslo</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Zadajte hodnotu</translation>

View File

@ -112,6 +112,14 @@
<source>Home</source>
<translation>Domov</translation>
</message>
<message>
<source>Enter a username</source>
<translation>Vnesite svoje uporabniško ime</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Vnesite geslo</translation>
</message>
<message>
<source>Enter a value...</source>
<translation>Vnesite vrednost</translation>

View File

@ -112,9 +112,17 @@
<source>Home</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter a username</source>
<translation>Faka igama lomsebenzisi</translation>
</message>
<message>
<source>Enter a password</source>
<translation>Faka iphasiwedi</translation>
</message>
<message>
<source>Enter a value...</source>
<translation type="unfinished"></translation>
<translation>Faka inani...</translation>
</message>
<message>
<source>Sort Field</source>

511
package-lock.json generated
View File

@ -7,18 +7,20 @@
"": {
"name": "jellyfin-roku",
"version": "1.4.12",
"hasInstallScript": true,
"license": "GPL-2.0",
"dependencies": {
"api": "npm:jellyfin-api-bs-client@^1.0.5",
"bgv": "npm:button-group-vert@^1.0.1",
"brighterscript-formatter": "^1.6.8",
"intKeyboard": "npm:integer-keyboard@^1.0.12",
"sob": "npm:slide-out-button@^1.0.1"
},
"devDependencies": {
"@rokucommunity/bslint": "0.7.5",
"brighterscript": "0.57.0",
"brighterscript": "0.57.2",
"rooibos-cli": "1.4.0",
"ropm": "0.10.9"
"ropm": "0.10.10"
}
},
"node_modules/@nodelib/fs.scandir": {
@ -827,9 +829,9 @@
}
},
"node_modules/brighterscript": {
"version": "0.57.0",
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.57.0.tgz",
"integrity": "sha512-lv7/qIBLrF62fnukTQUR7OZlzKugMSDkSpdtMTZKElTCY/CqU3Kueprg+fKC4zuQeFdZlKnVrD5fpSYZiOhe2A==",
"version": "0.57.2",
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.57.2.tgz",
"integrity": "sha512-idvz7lVLSN1mM/VoDt4/uJPFqdydSgCro2eIwT9vqV8z/1iNLpUtvXCWfeAbWxsbJkXWtmQq4GPkklCxc4OjrQ==",
"dev": true,
"dependencies": {
"@rokucommunity/bslib": "^0.1.1",
@ -3767,6 +3769,12 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/intKeyboard": {
"name": "integer-keyboard",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/integer-keyboard/-/integer-keyboard-1.0.12.tgz",
"integrity": "sha512-DSLyd/PvtEBfc4grICTxSLu94Yo/Vm6rNerRZRbbzRrP0HQ9pYaquoY2RD9x6gAmica43gsFimScNpuRYVe54w=="
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@ -5228,14 +5236,14 @@
}
},
"node_modules/ropm": {
"version": "0.10.9",
"resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.9.tgz",
"integrity": "sha512-HuYCFi90rCsiBYe8+0I6ym2QGeWbmfZkSv3ubL/eAmZQoJl0ebGXcjg6P44IeOIR5tZRyJ6TDiiST+6m2GyUNg==",
"version": "0.10.10",
"resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.10.tgz",
"integrity": "sha512-tkPuDwP/Mva9IXIuTf4pnH1DC27WLeLfu8QJ70WwaX9tepNMZeDi4eEQdWQ7kalXxxlwXGnG4jUaaA1B4v8zWw==",
"dev": true,
"dependencies": {
"@xml-tools/ast": "^5.0.5",
"@xml-tools/parser": "1.0.10",
"brighterscript": "^0.57.0",
"brighterscript": "^0.57.2",
"del": "6.0.0",
"fs-extra": "9.1.0",
"glob-all": "3.2.1",
@ -5261,121 +5269,6 @@
"chevrotain": "7.1.1"
}
},
"node_modules/ropm/node_modules/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,
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/ropm/node_modules/anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/ropm/node_modules/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,
"engines": {
"node": ">=8"
}
},
"node_modules/ropm/node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ropm/node_modules/brighterscript": {
"version": "0.57.0",
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.57.0.tgz",
"integrity": "sha512-lv7/qIBLrF62fnukTQUR7OZlzKugMSDkSpdtMTZKElTCY/CqU3Kueprg+fKC4zuQeFdZlKnVrD5fpSYZiOhe2A==",
"dev": true,
"dependencies": {
"@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",
"fast-glob": "^3.2.11",
"file-url": "^3.0.0",
"fs-extra": "^8.0.0",
"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",
"require-relative": "^0.8.7",
"roku-deploy": "^3.8.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"
},
"bin": {
"bsc": "dist/cli.js"
}
},
"node_modules/ropm/node_modules/brighterscript/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/ropm/node_modules/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,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/ropm/node_modules/chevrotain": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-7.1.1.tgz",
@ -5385,45 +5278,6 @@
"regexp-to-ast": "0.5.0"
}
},
"node_modules/ropm/node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/ropm/node_modules/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,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ropm/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
@ -5439,7 +5293,7 @@
"node": ">=10"
}
},
"node_modules/ropm/node_modules/fs-extra/node_modules/jsonfile": {
"node_modules/ropm/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
@ -5451,7 +5305,7 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/ropm/node_modules/fs-extra/node_modules/universalify": {
"node_modules/ropm/node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
@ -5460,102 +5314,6 @@
"node": ">= 10.0.0"
}
},
"node_modules/ropm/node_modules/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,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/ropm/node_modules/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,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ropm/node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ropm/node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ropm/node_modules/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,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/ropm/node_modules/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,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ropm/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/ropm/node_modules/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,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/ropm/node_modules/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,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/ropm/node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
@ -7208,9 +6966,9 @@
}
},
"brighterscript": {
"version": "0.57.0",
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.57.0.tgz",
"integrity": "sha512-lv7/qIBLrF62fnukTQUR7OZlzKugMSDkSpdtMTZKElTCY/CqU3Kueprg+fKC4zuQeFdZlKnVrD5fpSYZiOhe2A==",
"version": "0.57.2",
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.57.2.tgz",
"integrity": "sha512-idvz7lVLSN1mM/VoDt4/uJPFqdydSgCro2eIwT9vqV8z/1iNLpUtvXCWfeAbWxsbJkXWtmQq4GPkklCxc4OjrQ==",
"dev": true,
"requires": {
"@rokucommunity/bslib": "^0.1.1",
@ -9535,6 +9293,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"intKeyboard": {
"version": "npm:integer-keyboard@1.0.12",
"resolved": "https://registry.npmjs.org/integer-keyboard/-/integer-keyboard-1.0.12.tgz",
"integrity": "sha512-DSLyd/PvtEBfc4grICTxSLu94Yo/Vm6rNerRZRbbzRrP0HQ9pYaquoY2RD9x6gAmica43gsFimScNpuRYVe54w=="
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@ -10665,14 +10428,14 @@
}
},
"ropm": {
"version": "0.10.9",
"resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.9.tgz",
"integrity": "sha512-HuYCFi90rCsiBYe8+0I6ym2QGeWbmfZkSv3ubL/eAmZQoJl0ebGXcjg6P44IeOIR5tZRyJ6TDiiST+6m2GyUNg==",
"version": "0.10.10",
"resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.10.tgz",
"integrity": "sha512-tkPuDwP/Mva9IXIuTf4pnH1DC27WLeLfu8QJ70WwaX9tepNMZeDi4eEQdWQ7kalXxxlwXGnG4jUaaA1B4v8zWw==",
"dev": true,
"requires": {
"@xml-tools/ast": "^5.0.5",
"@xml-tools/parser": "1.0.10",
"brighterscript": "^0.57.0",
"brighterscript": "^0.57.2",
"del": "6.0.0",
"fs-extra": "9.1.0",
"glob-all": "3.2.1",
@ -10692,102 +10455,6 @@
"chevrotain": "7.1.1"
}
},
"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.57.0",
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.57.0.tgz",
"integrity": "sha512-lv7/qIBLrF62fnukTQUR7OZlzKugMSDkSpdtMTZKElTCY/CqU3Kueprg+fKC4zuQeFdZlKnVrD5fpSYZiOhe2A==",
"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",
"fast-glob": "^3.2.11",
"file-url": "^3.0.0",
"fs-extra": "^8.0.0",
"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",
"require-relative": "^0.8.7",
"roku-deploy": "^3.8.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": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
}
}
},
"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"
}
},
"chevrotain": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-7.1.1.tgz",
@ -10797,31 +10464,6 @@
"regexp-to-ast": "0.5.0"
}
},
"chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
}
},
"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": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
@ -10832,95 +10474,24 @@
"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-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
}
},
"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": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"dev": true
},
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"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
},
"source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"dev": true
},
"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"
}
},
"yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",

View File

@ -8,11 +8,12 @@
},
"devDependencies": {
"@rokucommunity/bslint": "0.7.5",
"brighterscript": "0.57.0",
"brighterscript": "0.57.2",
"rooibos-cli": "1.4.0",
"ropm": "0.10.9"
"ropm": "0.10.10"
},
"scripts": {
"postinstall": "npx ropm copy",
"validate": "npx bsc --copy-to-staging=false --create-package=false",
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "bslint",
@ -37,6 +38,7 @@
"api": "npm:jellyfin-api-bs-client@^1.0.5",
"bgv": "npm:button-group-vert@^1.0.1",
"brighterscript-formatter": "^1.6.8",
"sob": "npm:slide-out-button@^1.0.1"
"sob": "npm:slide-out-button@^1.0.1",
"intKeyboard": "npm:integer-keyboard@^1.0.12"
}
}
}

View File

@ -37,6 +37,19 @@
"title": "User Interface",
"description": "Settings relating to how the application looks.",
"children": [
{
"title": "Home Page",
"description": "Options for Home page.",
"children": [
{
"title": "Max Days Next Up",
"description": "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.",
"settingName": "ui.details.maxdaysnextup",
"type": "integer",
"default": "365"
}
]
},
{
"title": "Details Page",
"description": "Options for Details pages.",
@ -49,8 +62,8 @@
"default": "false"
}
]
},
{
},
{
"title": "TV Shows",
"description": "Options for TV Shows.",
"children": [
@ -62,8 +75,8 @@
"default": "false"
}
]
},
{
},
{
"title": "Screensaver",
"description": "Options for Jellyfin's screensaver.",
"children": [
@ -75,7 +88,7 @@
"default": "false"
}
]
},
},
{
"title": "Design Elements",
"description": "Options that alter the design of Jellyfin.",
@ -125,4 +138,4 @@
}
]
}
]
]

View File

@ -9,7 +9,6 @@ sub Main (args as dynamic) as void
' Set global constants
setConstants()
' Write screen tracker for screensaver
WriteAsciiFile("tmp:/scene.temp", "")
MoveFile("tmp:/scene.temp", "tmp:/scene")
@ -250,8 +249,29 @@ sub Main (args as dynamic) as void
' types: [ Series (Show), Episode, Movie, Audio, Person, Studio, MusicArtist ]
if node.type = "Series"
group = CreateSeriesDetailsGroup(node)
else
else if node.type = "Movie"
group = CreateMovieDetailsGroup(node)
else if node.type = "MusicArtist"
group = CreateArtistView(node.json)
else if node.type = "MusicAlbum"
group = CreateAlbumView(node.json)
else if node.type = "Audio"
group = CreateAudioPlayerGroup([node.json])
else if node.type = "Person"
group = CreatePersonView(node)
else if node.type = "TvChannel"
group = CreateVideoPlayerGroup(node.id)
sceneManager.callFunc("pushScene", group)
else if node.type = "Episode"
group = CreateVideoPlayerGroup(node.id)
sceneManager.callFunc("pushScene", group)
else if node.type = "Audio"
selectedIndex = msg.getData()
screenContent = msg.getRoSGNode()
group = CreateAudioPlayerGroup([screenContent.albumData.items[node.id]])
else
' TODO - switch on more node types
message_dialog("This type is not yet supported: " + node.type + ".")
end if
else if isNodeEvent(msg, "buttonSelected")
' If a button is selected, we have some determining to do
@ -334,8 +354,8 @@ sub Main (args as dynamic) as void
end if
group = CreateSearchPage()
sceneManager.callFunc("pushScene", group)
group.findNode("SearchBox").findNode("search-input").setFocus(true)
group.findNode("SearchBox").findNode("search-input").active = true
group.findNode("SearchBox").findNode("search_Key").setFocus(true)
group.findNode("SearchBox").findNode("search_Key").active = true
else if button.id = "change_server"
unset_setting("server")
unset_setting("port")
@ -365,9 +385,19 @@ sub Main (args as dynamic) as void
changeSubtitleDuringPlayback(trackSelected)
end if
end if
else if isNodeEvent(msg, "selectPlaybackInfoPressed")
node = m.scene.focusedChild
if node.focusedChild <> invalid and node.focusedChild.isSubType("JFVideo")
info = GetPlaybackInfo()
show_dialog(tr("Playback Information"), info)
end if
else if isNodeEvent(msg, "state")
node = msg.getRoSGNode()
if node.state = "finished"
if selectedItem.Type = "TvChannel" and node.state = "finished"
video = CreateVideoPlayerGroup(node.id)
m.global.sceneManager.callFunc("pushScene", video)
m.global.sceneManager.callFunc("clearPreviousScene")
else if node.state = "finished"
node.control = "stop"
' If node allows retrying using Transcode Url, give that shot

View File

@ -446,12 +446,8 @@ end function
function CreateSearchPage()
' Search + Results Page
group = CreateObject("roSGNode", "SearchResults")
search = group.findNode("SearchBox")
search.observeField("search_value", m.port)
options = group.findNode("SearchSelect")
group = CreateObject("roSGNode", "searchResults")
options = group.findNode("searchSelect")
options.observeField("itemSelected", m.port)
return group
@ -471,6 +467,7 @@ function CreateVideoPlayerGroup(video_id, mediaSourceId = invalid, audio_stream_
if video = invalid then return invalid
if video.errorMsg = "introaborted" then return video
video.observeField("selectSubtitlePressed", m.port)
video.observeField("selectPlaybackInfoPressed", m.port)
video.observeField("state", m.port)
return video

View File

@ -45,11 +45,6 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
end if
end if
if m.videotype = "Episode" or m.videotype = "Series"
video.skipIntroParams = api_API().introskipper.get(video.id)
video.content.contenttype = "episode"
end if
video.content.title = meta.title
video.showID = meta.showID
@ -175,18 +170,18 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
end if
if meta.live then mediaSourceId = "" ' Don't send mediaSourceId for Live media
playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
video.videoId = video.id
video.mediaSourceId = mediaSourceId
video.audioIndex = audio_stream_idx
if playbackInfo = invalid
if m.playbackInfo = invalid
video.content = invalid
return
end if
params = {}
video.PlaySessionId = playbackInfo.PlaySessionId
video.PlaySessionId = m.playbackInfo.PlaySessionId
if meta.live
video.content.live = true
@ -195,17 +190,17 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
video.container = getContainerType(meta)
if playbackInfo.MediaSources[0] = invalid
playbackInfo = meta.json
if m.playbackInfo.MediaSources[0] = invalid
m.playbackInfo = meta.json
end if
subtitles = sortSubtitles(meta.id, playbackInfo.MediaSources[0].MediaStreams)
subtitles = sortSubtitles(meta.id, m.playbackInfo.MediaSources[0].MediaStreams)
video.Subtitles = subtitles["all"]
if meta.live
video.transcodeParams = {
"MediaSourceId": playbackInfo.MediaSources[0].Id,
"LiveStreamId": playbackInfo.MediaSources[0].LiveStreamId,
"MediaSourceId": m.playbackInfo.MediaSources[0].Id,
"LiveStreamId": m.playbackInfo.MediaSources[0].LiveStreamId,
"PlaySessionId": video.PlaySessionId
}
end if
@ -214,7 +209,7 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
' 'TODO: allow user selection of subtitle track before playback initiated, for now set to no subtitles
video.directPlaySupported = playbackInfo.MediaSources[0].SupportsDirectPlay
video.directPlaySupported = m.playbackInfo.MediaSources[0].SupportsDirectPlay
fully_external = false
@ -223,7 +218,7 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
' artifacts. If the user preference is set, and the only reason the server says we need to
' transcode is that the Encoding Level is not supported, then try to direct play but silently
' fall back to the transcode if that fails.
if playbackInfo.MediaSources[0].MediaStreams.Count() > 0
if playbackInfo.MediaSources[0].MediaStreams.Count() > 0 and meta.live = false
tryDirectPlay = get_user_setting("playback.tryDirect.h264ProfileLevel") = "true" and playbackInfo.MediaSources[0].MediaStreams[0].codec = "h264"
tryDirectPlay = tryDirectPlay or (get_user_setting("playback.tryDirect.hevcProfileLevel") = "true" and playbackInfo.MediaSources[0].MediaStreams[0].codec = "hevc")
if tryDirectPlay and playbackInfo.MediaSources[0].TranscodingUrl <> invalid and forceTranscoding = false
@ -236,10 +231,10 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
end if
if video.directPlaySupported
protocol = LCase(playbackInfo.MediaSources[0].Protocol)
protocol = LCase(m.playbackInfo.MediaSources[0].Protocol)
if protocol <> "file"
uriRegex = CreateObject("roRegex", "^(.*:)//([A-Za-z0-9\-\.]+)(:[0-9]+)?(.*)$", "")
uri = uriRegex.Match(playbackInfo.MediaSources[0].Path)
uri = uriRegex.Match(m.playbackInfo.MediaSources[0].Path)
' proto $1, host $2, port $3, the-rest $4
localhost = CreateObject("roRegex", "^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$", "i")
' https://stackoverflow.com/questions/8426171/what-regex-will-match-all-loopback-addresses
@ -250,7 +245,7 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
video.content.url = buildURL(uri[4])
else
fully_external = true
video.content.url = playbackInfo.MediaSources[0].Path
video.content.url = m.playbackInfo.MediaSources[0].Path
end if
else:
params.append({
@ -267,15 +262,15 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
end if
video.isTranscoded = false
else
if playbackInfo.MediaSources[0].TranscodingUrl = invalid
if m.playbackInfo.MediaSources[0].TranscodingUrl = invalid
' If server does not provide a transcode URL, display a message to the user
m.global.sceneManager.callFunc("userMessage", tr("Error Getting Playback Information"), tr("An error was encountered while playing this item. Server did not provide required transcoding data."))
video.content = invalid
return
end if
' Get transcoding reason
video.transcodeReasons = getTranscodeReasons(playbackInfo.MediaSources[0].TranscodingUrl)
video.content.url = buildURL(playbackInfo.MediaSources[0].TranscodingUrl)
video.transcodeReasons = getTranscodeReasons(m.playbackInfo.MediaSources[0].TranscodingUrl)
video.content.url = buildURL(m.playbackInfo.MediaSources[0].TranscodingUrl)
video.isTranscoded = true
end if
@ -286,10 +281,6 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
' is enabled/will be enabled, indexed on the provided list of subtitles
video.SelectedSubtitle = setupSubtitle(video, video.Subtitles, subtitle_idx)
video.content.SDBifUrl = api_API().jellyscrub.get(video.id)
video.content.HDBifUrl = api_API().jellyscrub.get(video.id)
video.content.FHDBifUrl = api_API().jellyscrub.get(video.id)
if not fully_external
video.content = authorize_request(video.content)
end if
@ -348,9 +339,9 @@ function getTranscodeReasons(url as string) as object
return []
end function
'Opens dialog asking user if they want to resume video or start playback over
'Opens dialog asking user if they want to resume video or start playback over only on the home screen
function startPlayBackOver(time as longinteger) as integer
if m.videotype = "Episode" or m.videotype = "Series"
if m.scene.focusedChild.focusedChild.overhangTitle = tr("Home") and (m.videotype = "Episode" or m.videotype = "Series")
return option_dialog([tr("Resume playing at ") + ticksToHuman(time) + ".", tr("Start over from the beginning."), tr("Watched"), tr("Go to series"), tr("Go to season"), tr("Go to episode")])
else
return option_dialog(["Resume playing at " + ticksToHuman(time) + ".", "Start over from the beginning."])
@ -431,10 +422,10 @@ sub autoPlayNextEpisode(videoID as string, showID as string)
data = getJson(resp)
if data <> invalid and data.Items.Count() = 2
' remove finished video node
m.global.sceneManager.callFunc("popScene")
' setup new video node
nextVideo = CreateVideoPlayerGroup(data.Items[1].Id, invalid, 1, false, false)
' remove last video scene
m.global.sceneManager.callFunc("clearPreviousScene")
if nextVideo <> invalid
m.global.sceneManager.callFunc("pushScene", nextVideo)
else
@ -448,3 +439,135 @@ sub autoPlayNextEpisode(videoID as string, showID as string)
m.global.sceneManager.callFunc("popScene")
end if
end sub
' Returns an array of playback info to be displayed during playback.
' In the future, with a custom playback info view, we can return an associated array.
function GetPlaybackInfo()
sessions = api_API().sessions.get()
if sessions <> invalid and sessions.Count() > 0
return GetTranscodingStats(sessions[0])
end if
errMsg = tr("Unable to get playback information")
return [errMsg]
end function
function GetTranscodingStats(session)
sessionStats = []
if isValid(session.TranscodingInfo) and session.TranscodingInfo.Count() > 0
transcodingReasons = session.TranscodingInfo.TranscodeReasons
videoCodec = session.TranscodingInfo.VideoCodec
audioCodec = session.TranscodingInfo.AudioCodec
totalBitrate = session.TranscodingInfo.Bitrate
audioChannels = session.TranscodingInfo.AudioChannels
if isValid(transcodingReasons) and transcodingReasons.Count() > 0
sessionStats.push("** " + tr("Transcoding Information") + " **")
for each item in transcodingReasons
sessionStats.push(tr("Reason") + ": " + item)
end for
end if
if isValid(videoCodec)
data = tr("Video Codec") + ": " + videoCodec
if session.TranscodingInfo.IsVideoDirect
data = data + " (" + tr("direct") + ")"
end if
sessionStats.push(data)
end if
if isValid(audioCodec)
data = tr("Audio Codec") + ": " + audioCodec
if session.TranscodingInfo.IsAudioDirect
data = data + " (" + tr("direct") + ")"
end if
sessionStats.push(data)
end if
if isValid(totalBitrate)
data = tr("Total Bitrate") + ": " + getDisplayBitrate(totalBitrate)
sessionStats.push(data)
end if
if isValid(audioChannels)
data = tr("Audio Channels") + ": " + Str(audioChannels)
sessionStats.push(data)
end if
end if
if havePlaybackInfo()
stream = m.playbackInfo.mediaSources[0].MediaStreams[0]
sessionStats.push("** " + tr("Stream Information") + " **")
if isValid(stream.Container)
data = tr("Container") + ": " + stream.Container
sessionStats.push(data)
end if
if isValid(stream.Size)
data = tr("Size") + ": " + stream.Size
sessionStats.push(data)
end if
if isValid(stream.BitRate)
data = tr("Bit Rate") + ": " + getDisplayBitrate(stream.BitRate)
sessionStats.push(data)
end if
if isValid(stream.Codec)
data = tr("Codec") + ": " + stream.Codec
sessionStats.push(data)
end if
if isValid(stream.CodecTag)
data = tr("Codec Tag") + ": " + stream.CodecTag
sessionStats.push(data)
end if
if isValid(stream.VideoRangeType)
data = tr("Video range type") + ": " + stream.VideoRangeType
sessionStats.push(data)
end if
if isValid(stream.PixelFormat)
data = tr("Pixel format") + ": " + stream.PixelFormat
sessionStats.push(data)
end if
if isValid(stream.Width) and isValid(stream.Height)
data = tr("WxH") + ": " + Str(stream.Width) + " x " + Str(stream.Height)
sessionStats.push(data)
end if
if isValid(stream.Level)
data = tr("Level") + ": " + Str(stream.Level)
sessionStats.push(data)
end if
end if
return sessionStats
end function
function havePlaybackInfo()
if not isValid(m.playbackInfo)
return false
end if
if not isValid(m.playbackInfo.mediaSources)
return false
end if
if m.playbackInfo.mediaSources.Count() <= 0
return false
end if
if not isValid(m.playbackInfo.mediaSources[0].MediaStreams)
return false
end if
if m.playbackInfo.mediaSources[0].MediaStreams.Count() <= 0
return false
end if
return true
end function
function getDisplayBitrate(bitrate)
if bitrate > 1000000
return Str(Fix(bitrate / 1000000)) + " Mbps"
else
return Str(Fix(bitrate / 1000)) + " Kbps"
end if
end function

View File

@ -34,34 +34,40 @@ function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioT
end function
' Search across all libraries
function SearchMedia(query as string)
function searchMedia(query as string)
' This appears to be done differently on the web now
' For each potential type, a separate query is done:
' varying item types, and artists, and people
resp = APIRequest(Substitute("Users/{0}/Items", get_setting("active_user")), {
"searchTerm": query,
"IncludePeople": true,
"IncludeMedia": true,
"IncludeShows": true,
"IncludeGenres": false,
"IncludeStudios": false,
"IncludeArtists": false,
"IncludeItemTypes": "TvChannel,Movie,BoxSet,Series,Episode,Video",
"EnableTotalRecordCount": false,
"ImageTypeLimit": 1,
"Recursive": true
})
data = getJson(resp)
results = []
for each item in data.Items
tmp = CreateObject("roSGNode", "SearchData")
tmp.image = PosterImage(item.id)
tmp.json = item
results.push(tmp)
end for
data.SearchHints = results
return data
if query <> ""
resp = APIRequest(Substitute("Search/Hints", get_setting("active_user")), {
"searchTerm": query,
"IncludePeople": true,
"IncludeMedia": true,
"IncludeShows": true,
"IncludeGenres": true,
"IncludeStudios": true,
"IncludeArtists": true,
"IncludeItemTypes": "LiveTvChannel,Movie,BoxSet,Series,Episode,Video,Person,Audio,MusicAlbum,MusicArtist,Playlist",
"EnableTotalRecordCount": false,
"ImageTypeLimit": 1,
"Recursive": true,
"limit": 100
})
data = getJson(resp)
results = []
for each item in data.SearchHints
tmp = CreateObject("roSGNode", "SearchData")
tmp.image = PosterImage(item.id)
tmp.json = item
results.push(tmp)
end for
data.SearchHints = results
return data
end if
return []
end function
' MetaData about an item
@ -77,7 +83,7 @@ function ItemMetaData(id as string)
imgParams.Append(param)
end if
end if
if data.type = "Movie"
if data.type = "Movie" or data.type = "MusicVideo"
tmp = CreateObject("roSGNode", "MovieData")
tmp.image = PosterImage(data.id, imgParams)
tmp.json = data
@ -94,7 +100,7 @@ function ItemMetaData(id as string)
tmp.image = PosterImage(data.id, imgParams)
tmp.json = data
return tmp
else if data.type = "BoxSet"
else if data.type = "BoxSet" or data.type = "Playlist"
tmp = CreateObject("roSGNode", "CollectionData")
tmp.image = PosterImage(data.id, imgParams)
tmp.json = data