Merge branch 'unstable' into CreateUnitTestSuite
This commit is contained in:
commit
d8bc511640
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
|
@ -6,16 +6,16 @@ on:
|
|||
- 'locale/**'
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3
|
||||
- uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 # tag=v3
|
||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
|
||||
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3
|
||||
with:
|
||||
node-version: "14.12.0"
|
||||
node-version: "18.13.0"
|
||||
- run: npm ci
|
||||
- run: npx ropm install
|
||||
- run: make dev
|
||||
- uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3
|
||||
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3
|
||||
with:
|
||||
name: Jellyfin-Roku-dev-${{ github.sha }}
|
||||
path: ${{ github.workspace }}/out/staging
|
||||
|
|
8
.github/workflows/master-release.yml
vendored
8
.github/workflows/master-release.yml
vendored
|
@ -6,12 +6,12 @@ on:
|
|||
|
||||
jobs:
|
||||
test-build-release:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: "14.12.0"
|
||||
node-version: "18.13.0"
|
||||
- run: npm ci
|
||||
- run: npx ropm install
|
||||
- run: npm run validate
|
||||
|
@ -22,7 +22,7 @@ jobs:
|
|||
run: awk 'BEGIN { FS="=" } /^minor_version/ { print "MINOR="$2; }' manifest >> $GITHUB_ENV
|
||||
- name: "Find and save build_version from manifest"
|
||||
run: awk 'BEGIN { FS="=" } /^build_version/ { print "BUILD="$2; }' manifest >> $GITHUB_ENV
|
||||
- uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3
|
||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
|
||||
- uses: vimtor/action-zip@5f1c4aa587ea41db1110df6a99981dbe19cee310 # tag=v1
|
||||
with:
|
||||
recursive: false
|
||||
|
@ -36,7 +36,7 @@ jobs:
|
|||
prerelease: false
|
||||
title: v${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.BUILD }}
|
||||
files: ${{ github.workspace }}/jellyfin_v${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.BUILD }}.zip
|
||||
- uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3
|
||||
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3
|
||||
with:
|
||||
name: jellyfin_v${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.BUILD }}.zip
|
||||
path: ${{ github.workspace }}/jellyfin_v${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.BUILD }}.zip
|
||||
|
|
8
.github/workflows/unstable-release.yml
vendored
8
.github/workflows/unstable-release.yml
vendored
|
@ -6,12 +6,12 @@ on:
|
|||
|
||||
jobs:
|
||||
test-build-release:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: "14.12.0"
|
||||
node-version: "18.13.0"
|
||||
- run: npm ci
|
||||
- run: npx ropm install
|
||||
- run: npm run validate
|
||||
|
@ -22,7 +22,7 @@ jobs:
|
|||
run: awk 'BEGIN { FS="=" } /^minor_version/ { print "MINOR="$2; }' manifest >> $GITHUB_ENV
|
||||
- name: "Find and save build_version from manifest"
|
||||
run: awk 'BEGIN { FS="=" } /^build_version/ { print "BUILD="$2; }' manifest >> $GITHUB_ENV
|
||||
- uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3
|
||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
|
||||
- uses: vimtor/action-zip@5f1c4aa587ea41db1110df6a99981dbe19cee310 # tag=v1
|
||||
with:
|
||||
recursive: false
|
||||
|
@ -35,7 +35,7 @@ jobs:
|
|||
prerelease: true
|
||||
title: v${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.BUILD }}
|
||||
files: ${{ github.workspace }}/jellyfin_v${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.BUILD }}.zip
|
||||
- uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3
|
||||
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3
|
||||
with:
|
||||
name: jellyfin_v${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.BUILD }}.zip
|
||||
path: ${{ github.workspace }}/jellyfin_v${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.BUILD }}.zip
|
||||
|
|
4
.github/workflows/validate.yml
vendored
4
.github/workflows/validate.yml
vendored
|
@ -3,12 +3,12 @@ on: [push, pull_request]
|
|||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: "14.12.0"
|
||||
node-version: "18.13.0"
|
||||
- run: npm ci
|
||||
- run: npx ropm install
|
||||
- run: npm run validate
|
||||
|
|
|
@ -8,6 +8,9 @@ sub init()
|
|||
|
||||
m.itemPoster.observeField("loadStatus", "onPosterLoadStatusChanged")
|
||||
|
||||
m.unplayedCount = m.top.findNode("unplayedCount")
|
||||
m.unplayedEpisodeCount = m.top.findNode("unplayedEpisodeCount")
|
||||
|
||||
m.itemText.translation = [0, m.itemPoster.height + 7]
|
||||
|
||||
m.alwaysShowTitles = get_user_setting("itemgrid.alwaysShowTitles") = "true"
|
||||
|
@ -40,6 +43,13 @@ sub itemContentChanged()
|
|||
m.itemIcon.uri = itemData.iconUrl
|
||||
m.itemText.text = itemData.Title
|
||||
else if itemData.type = "Series"
|
||||
if itemData?.json?.UserData?.UnplayedItemCount <> invalid
|
||||
if itemData.json.UserData.UnplayedItemCount > 0
|
||||
m.unplayedCount.visible = true
|
||||
m.unplayedEpisodeCount.text = itemData.json.UserData.UnplayedItemCount
|
||||
end if
|
||||
end if
|
||||
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.itemIcon.uri = itemData.iconUrl
|
||||
m.itemText.text = itemData.Title
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="GridItem" extends="Group">
|
||||
<children>
|
||||
<maskGroup id="posterMask" maskUri="pkg:/images/postermask.png" scaleRotateCenter="[145, 212.5]" scale="[0.85,0.85]" >
|
||||
<maskGroup id="posterMask" maskUri="pkg:/images/postermask.png" scaleRotateCenter="[145, 212.5]" scale="[0.85,0.85]">
|
||||
<Poster id="backdrop" width="290" height="425" loadDisplayMode="scaleToZoom" uri="pkg:/images/white.9.png" />
|
||||
<Poster id="itemPoster" width="290" height="425" loadDisplayMode="scaleToZoom" />
|
||||
<Poster id="itemPoster" width="290" height="425" loadDisplayMode="scaleToZoom">
|
||||
<Rectangle id="unplayedCount" visible="false" width="90" height="60" color="#00a4dcFF" translation="[201, 0]">
|
||||
<Label id="unplayedEpisodeCount" width="90" height="60" font="font:SmallestBoldSystemFont" horizAlign="center" vertAlign="center" />
|
||||
</Rectangle>
|
||||
</Poster>
|
||||
<Poster id="itemIcon" width="50" height="50" translation="[230,10]" />
|
||||
<Label id="posterText" width="280" height="415" translation="[5,5]" horizAlign="center" vertAlign="center" ellipsizeOnBoundary="true" wrap="true" />
|
||||
</maskGroup>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
sub init()
|
||||
m.itemPoster = m.top.findNode("itemPoster")
|
||||
m.posterText = m.top.findNode("posterText")
|
||||
m.title = m.top.findNode("title")
|
||||
m.posterText.font.size = 30
|
||||
m.title.font.size = 25
|
||||
m.backdrop = m.top.findNode("backdrop")
|
||||
|
||||
m.itemPoster.observeField("loadStatus", "onPosterLoadStatusChanged")
|
||||
|
@ -9,22 +11,32 @@ sub init()
|
|||
'Parent is MarkupGrid and it's parent is the ItemGrid
|
||||
m.topParent = m.top.GetParent().GetParent()
|
||||
|
||||
m.title.visible = false
|
||||
|
||||
'Get the imageDisplayMode for these grid items
|
||||
if m.topParent.imageDisplayMode <> invalid
|
||||
m.itemPoster.loadDisplayMode = m.topParent.imageDisplayMode
|
||||
end if
|
||||
|
||||
end sub
|
||||
|
||||
sub itemContentChanged()
|
||||
m.backdrop.blendColor = "#101010"
|
||||
|
||||
m.title.visible = false
|
||||
|
||||
if isValid(m.topParent.showItemTitles)
|
||||
if LCase(m.topParent.showItemTitles) = "showalways"
|
||||
m.title.visible = true
|
||||
end if
|
||||
end if
|
||||
|
||||
itemData = m.top.itemContent
|
||||
|
||||
if not isValid(itemData) then return
|
||||
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.posterText.text = itemData.title
|
||||
m.title.text = itemData.title
|
||||
|
||||
'If Poster not loaded, ensure "blue box" is shown until loaded
|
||||
if m.itemPoster.loadStatus <> "ready"
|
||||
|
@ -33,6 +45,20 @@ sub itemContentChanged()
|
|||
end if
|
||||
end sub
|
||||
|
||||
sub focusChanged()
|
||||
if m.top.itemHasFocus = true
|
||||
m.title.repeatCount = -1
|
||||
else
|
||||
m.title.repeatCount = 0
|
||||
end if
|
||||
|
||||
if isValid(m.topParent.showItemTitles)
|
||||
if LCase(m.topParent.showItemTitles) = "showonhover"
|
||||
m.title.visible = m.top.itemHasFocus
|
||||
end if
|
||||
end if
|
||||
end sub
|
||||
|
||||
'Hide backdrop and text when poster loaded
|
||||
sub onPosterLoadStatusChanged()
|
||||
if m.itemPoster.loadStatus = "ready"
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
<children>
|
||||
<Poster id="backdrop" translation="[0,15]" width="230" height="320" loadDisplayMode="scaleToZoom" uri="pkg:/images/white.9.png" />
|
||||
<Poster id="itemPoster" translation="[0,15]" width="230" height="320" loadDisplayMode="scaleToZoom" />
|
||||
<ScrollingLabel translation="[0,340]" id="title" horizAlign="center" font="font:SmallSystemFont" repeatCount="0" maxWidth="230" />
|
||||
<Poster id="itemIcon" width="50" height="50" translation="[230,10]" />
|
||||
<Label id="posterText" width="230" height="320" translation="[5,5]" horizAlign="center" vertAlign="center" ellipsizeOnBoundary="true" wrap="true" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="itemContentChanged" />
|
||||
<field id="itemHasFocus" type="boolean" onChange="focusChanged" />
|
||||
<field id="itemHasFocus" type="boolean" onChange="focusChanged" alwaysNotify="true" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="GridItemSmall.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
|
|
|
@ -12,6 +12,11 @@ sub init()
|
|||
m.newBackdrop = m.top.findNode("backdropTransition")
|
||||
m.emptyText = m.top.findNode("emptyText")
|
||||
|
||||
m.genreList = m.top.findNode("genrelist")
|
||||
m.genreList.observeField("itemSelected", "onGenreItemSelected")
|
||||
m.genreData = CreateObject("roSGNode", "ContentNode")
|
||||
m.genreList.content = m.genreData
|
||||
|
||||
m.swapAnimation = m.top.findNode("backroundSwapAnimation")
|
||||
m.swapAnimation.observeField("state", "swapDone")
|
||||
|
||||
|
@ -74,6 +79,12 @@ sub init()
|
|||
end if
|
||||
end sub
|
||||
|
||||
'
|
||||
'Genre Item Selected
|
||||
sub onGenreItemSelected()
|
||||
m.top.selectedItem = m.genreList.content.getChild(m.genreList.rowItemSelected[0]).getChild(m.genreList.rowItemSelected[1])
|
||||
end sub
|
||||
|
||||
'
|
||||
'Load initial set of Data
|
||||
sub loadInitialItems()
|
||||
|
@ -241,7 +252,10 @@ sub setMoviesOptions(options)
|
|||
]
|
||||
options.filter = [
|
||||
{ "Title": tr("All"), "Name": "All" },
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" }
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" },
|
||||
{ "Title": tr("Played"), "Name": "Played" },
|
||||
{ "Title": tr("Unplayed"), "Name": "Unplayed" },
|
||||
{ "Title": tr("Resumable"), "Name": "Resumable" }
|
||||
]
|
||||
end sub
|
||||
|
||||
|
@ -256,7 +270,9 @@ sub setBoxsetsOptions(options)
|
|||
]
|
||||
options.filter = [
|
||||
{ "Title": tr("All"), "Name": "All" },
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" }
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" },
|
||||
{ "Title": tr("Played"), "Name": "Played" },
|
||||
{ "Title": tr("Unplayed"), "Name": "Unplayed" }
|
||||
]
|
||||
end sub
|
||||
|
||||
|
@ -278,8 +294,18 @@ sub setTvShowsOptions(options)
|
|||
]
|
||||
options.filter = [
|
||||
{ "Title": tr("All"), "Name": "All" },
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" }
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" },
|
||||
{ "Title": tr("Played"), "Name": "Played" },
|
||||
{ "Title": tr("Unplayed"), "Name": "Unplayed" }
|
||||
]
|
||||
|
||||
if isValid(m.view)
|
||||
if LCase(m.options.view) = "genres" or LCase(m.view) = "genres"
|
||||
options.sort = [{ "Title": tr("TITLE"), "Name": "SortName" }]
|
||||
options.filter = []
|
||||
end if
|
||||
end if
|
||||
|
||||
end sub
|
||||
|
||||
' Set Live TV view, sort, and filter options
|
||||
|
@ -423,10 +449,32 @@ sub ItemDataLoaded(msg)
|
|||
return
|
||||
end if
|
||||
|
||||
if m.loadItemsTask.view = "Genres"
|
||||
' Reset genre list data
|
||||
m.genreData.removeChildren(m.genreData.getChildren(-1, 0))
|
||||
|
||||
for each item in itemData
|
||||
m.genreData.appendChild(item)
|
||||
end for
|
||||
|
||||
m.itemGrid.opacity = "0"
|
||||
m.genreList.opacity = "1"
|
||||
|
||||
m.itemGrid.setFocus(false)
|
||||
m.genreList.setFocus(true)
|
||||
|
||||
m.loading = false
|
||||
m.spinner.visible = false
|
||||
return
|
||||
end if
|
||||
|
||||
for each item in itemData
|
||||
m.data.appendChild(item)
|
||||
end for
|
||||
|
||||
m.itemGrid.opacity = "1"
|
||||
m.genreList.opacity = "0"
|
||||
|
||||
'Update the stored counts
|
||||
m.loadedItems = m.itemGrid.content.getChildCount()
|
||||
m.loadedRows = m.loadedItems / m.itemGrid.numColumns
|
||||
|
@ -438,6 +486,7 @@ sub ItemDataLoaded(msg)
|
|||
end if
|
||||
|
||||
m.itemGrid.setFocus(true)
|
||||
m.genreList.setFocus(false)
|
||||
m.spinner.visible = false
|
||||
end sub
|
||||
|
||||
|
@ -645,7 +694,10 @@ sub optionsClosed()
|
|||
m.itemGrid.content = m.data
|
||||
loadInitialItems()
|
||||
end if
|
||||
m.itemGrid.setFocus(true)
|
||||
|
||||
m.itemGrid.setFocus(m.itemGrid.opacity = 1)
|
||||
m.genreList.setFocus(m.genreList.opacity = 1)
|
||||
|
||||
if m.tvGuide <> invalid
|
||||
m.tvGuide.lastFocus.setFocus(true)
|
||||
end if
|
||||
|
@ -681,13 +733,19 @@ end sub
|
|||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
topGrp = m.top.findNode("itemGrid")
|
||||
|
||||
if m.itemGrid.opacity = 1
|
||||
topGrp = m.itemGrid
|
||||
else
|
||||
topGrp = m.genreList
|
||||
end if
|
||||
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
|
||||
|
@ -773,14 +831,16 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
end function
|
||||
|
||||
sub updateTitle()
|
||||
if m.filter = "All"
|
||||
m.top.overhangTitle = m.top.parentItem.title
|
||||
else if m.filter = "Favorites"
|
||||
m.top.overhangTitle = m.top.parentItem.title
|
||||
|
||||
if m.filter = "Favorites"
|
||||
m.top.overhangTitle = m.top.parentItem.title + " " + tr("(Favorites)")
|
||||
end if
|
||||
|
||||
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
|
||||
|
@ -794,14 +854,18 @@ sub updateTitle()
|
|||
if m.options.view = "Networks" or m.view = "Networks"
|
||||
m.top.overhangTitle = "%s (%s)".Format(m.top.parentItem.title, tr("Networks"))
|
||||
end if
|
||||
|
||||
if m.options.view = "Studios" or m.view = "Studios"
|
||||
m.top.overhangTitle = "%s (%s)".Format(m.top.parentItem.title, tr("Studios"))
|
||||
end if
|
||||
|
||||
if m.options.view = "Genres" or m.view = "Genres"
|
||||
m.top.overhangTitle = "%s (%s)".Format(m.top.parentItem.title, tr("Genres"))
|
||||
end if
|
||||
|
||||
actInt = m.itemGrid.itemFocused + 1
|
||||
if m.showItemCount and m.loadItemsTask.totalRecordCount > 0
|
||||
|
||||
if m.showItemCount and m.loadItemsTask.totalRecordCount > 0 and m.options.view <> "Genres" and m.view <> "Genres"
|
||||
m.top.overhangTitle += " (" + tr("%1 of %2").Replace("%1", actInt.toStr()).Replace("%2", m.loadItemsTask.totalRecordCount.toStr()) + ")"
|
||||
end if
|
||||
|
||||
|
|
|
@ -3,18 +3,8 @@
|
|||
<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"
|
||||
height="1080"
|
||||
opacity="0.25"
|
||||
/>
|
||||
<poster id="backdropTransition"
|
||||
loadDisplayMode="scaleToFill"
|
||||
width="1920"
|
||||
height="1080"
|
||||
opacity="0.25"
|
||||
/>
|
||||
<poster id="backdrop" loadDisplayMode="scaleToFill" width="1920" height="1080" opacity="0.25" />
|
||||
<poster id="backdropTransition" loadDisplayMode="scaleToFill" width="1920" height="1080" opacity="0.25" />
|
||||
<MarkupGrid
|
||||
id = "itemGrid"
|
||||
translation = "[ 96, 160 ]"
|
||||
|
@ -25,14 +15,17 @@
|
|||
itemSize = "[ 290, 425 ]"
|
||||
itemSpacing = "[ 0, 45 ]"
|
||||
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"/>
|
||||
|
||||
<RowList opacity="0" id="genrelist" translation="[120, 160]" showRowLabel="true" itemComponentName="GridItemSmall" numColumns="1" numRows="3" vertFocusAnimationStyle="fixed" itemSize = "[1900, 360]" rowItemSize="[ [230, 320] ]" rowItemSpacing="[ [20, 0] ]" itemSpacing="[0, 60]" />
|
||||
|
||||
<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="[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" />
|
||||
<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" />
|
||||
</Animation>
|
||||
<Alpha id="AlphaMenu" />
|
||||
</children>
|
||||
|
|
|
@ -75,6 +75,12 @@ sub loadItems()
|
|||
else if filter = "favorites"
|
||||
params.append({ Filters: "IsFavorite" })
|
||||
params.append({ isFavorite: true })
|
||||
else if filter = "unplayed"
|
||||
params.append({ Filters: "IsUnplayed" })
|
||||
else if filter = "played"
|
||||
params.append({ Filters: "IsPlayed" })
|
||||
else if filter = "resumable"
|
||||
params.append({ Filters: "IsResumable" })
|
||||
end if
|
||||
|
||||
if m.top.ItemType <> ""
|
||||
|
@ -89,13 +95,14 @@ sub loadItems()
|
|||
params.append({ UserId: get_setting("active_user") })
|
||||
else if m.top.view = "Genres"
|
||||
url = "Genres"
|
||||
params.append({ UserId: get_setting("active_user") })
|
||||
params.append({ UserId: get_setting("active_user"), includeItemTypes: m.top.itemType })
|
||||
else if m.top.ItemType = "MusicArtist"
|
||||
url = "Artists"
|
||||
params.append({
|
||||
UserId: get_setting("active_user")
|
||||
UserId: get_setting("active_user"),
|
||||
Fields: "Genres"
|
||||
})
|
||||
params.IncludeItemTypes = ""
|
||||
params.IncludeItemTypes = "MusicAlbum,Audio"
|
||||
else if m.top.ItemType = "MusicAlbum"
|
||||
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
|
||||
params.append({ ImageTypeLimit: 1 })
|
||||
|
@ -103,6 +110,7 @@ sub loadItems()
|
|||
else
|
||||
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
|
||||
end if
|
||||
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
if data <> invalid
|
||||
|
@ -121,7 +129,7 @@ sub loadItems()
|
|||
tmp = CreateObject("roSGNode", "ChannelData")
|
||||
else if item.Type = "Folder" or item.Type = "ChannelFolderItem" or item.Type = "CollectionFolder"
|
||||
tmp = CreateObject("roSGNode", "FolderData")
|
||||
else if item.Type = "Video"
|
||||
else if item.Type = "Video" or item.Type = "Recording"
|
||||
tmp = CreateObject("roSGNode", "VideoData")
|
||||
else if item.Type = "Photo"
|
||||
tmp = CreateObject("roSGNode", "PhotoData")
|
||||
|
@ -136,7 +144,7 @@ sub loadItems()
|
|||
genreData = api_API().users.getitemsbyquery(get_setting("active_user"), {
|
||||
SortBy: "Random",
|
||||
SortOrder: "Ascending",
|
||||
IncludeItemTypes: "Movie",
|
||||
IncludeItemTypes: m.top.itemType,
|
||||
Recursive: true,
|
||||
Fields: "PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo",
|
||||
ImageTypeLimit: 1,
|
||||
|
@ -151,26 +159,38 @@ sub loadItems()
|
|||
' Add View All item to the start of the row
|
||||
row = tmp.createChild("FolderData")
|
||||
row.parentFolder = m.top.itemId
|
||||
genreMovieImage = api_API().items.getimageurl(item.id)
|
||||
row.title = item.name
|
||||
row.title = tr("View All") + " " + item.name
|
||||
item.name = tr("View All") + " " + item.name
|
||||
row.json = item
|
||||
row.FHDPOSTERURL = genreMovieImage
|
||||
row.HDPOSTERURL = genreMovieImage
|
||||
row.SDPOSTERURL = genreMovieImage
|
||||
row.type = "Folder"
|
||||
|
||||
if LCase(m.top.itemType) = "movie"
|
||||
genreItemImage = api_API().items.getimageurl(item.id)
|
||||
else
|
||||
genreItemImage = invalid
|
||||
row.posterURL = invalid
|
||||
end if
|
||||
|
||||
row.FHDPOSTERURL = genreItemImage
|
||||
row.HDPOSTERURL = genreItemImage
|
||||
row.SDPOSTERURL = genreItemImage
|
||||
end if
|
||||
|
||||
for each genreMovie in genreData.Items
|
||||
row = tmp.createChild("MovieData")
|
||||
for each genreItem in genreData.Items
|
||||
if LCase(m.top.itemType) = "movie"
|
||||
row = tmp.createChild("MovieData")
|
||||
else
|
||||
row = tmp.createChild("SeriesData")
|
||||
end if
|
||||
|
||||
genreMovieImage = api_API().items.getimageurl(genreMovie.id)
|
||||
row.title = genreMovie.name
|
||||
row.FHDPOSTERURL = genreMovieImage
|
||||
row.HDPOSTERURL = genreMovieImage
|
||||
row.SDPOSTERURL = genreMovieImage
|
||||
row.json = genreMovie
|
||||
row.id = genreMovie.id
|
||||
row.type = genreMovie.type
|
||||
genreItemImage = api_API().items.getimageurl(genreItem.id)
|
||||
row.title = genreItem.name
|
||||
row.FHDPOSTERURL = genreItemImage
|
||||
row.HDPOSTERURL = genreItemImage
|
||||
row.SDPOSTERURL = genreItemImage
|
||||
row.json = genreItem
|
||||
row.id = genreItem.id
|
||||
row.type = genreItem.type
|
||||
end for
|
||||
|
||||
else if item.Type = "Studio"
|
||||
|
@ -187,12 +207,20 @@ sub loadItems()
|
|||
tmp = CreateObject("roSGNode", "MusicArtistData")
|
||||
else if item.Type = "Audio"
|
||||
tmp = CreateObject("roSGNode", "MusicSongData")
|
||||
else if item.Type = "MusicGenre"
|
||||
tmp = CreateObject("roSGNode", "FolderData")
|
||||
tmp.title = item.name
|
||||
tmp.parentFolder = m.top.itemId
|
||||
tmp.json = item
|
||||
tmp.type = "Folder"
|
||||
tmp.posterUrl = api_API().items.getimageurl(item.id, "primary", 0, { "maxHeight": 280, "maxWidth": 280, "quality": "90" })
|
||||
|
||||
else
|
||||
print "[LoadItems] Unknown Type: " item.Type
|
||||
end if
|
||||
|
||||
if tmp <> invalid
|
||||
if item.Type <> "Genre"
|
||||
if item.Type <> "Genre" and item.Type <> "MusicGenre"
|
||||
tmp.parentFolder = m.top.itemId
|
||||
tmp.json = item
|
||||
if item.UserData <> invalid and item.UserData.isFavorite <> invalid
|
||||
|
|
|
@ -22,6 +22,7 @@ sub setupNodes()
|
|||
m.overhang = m.top.getScene().findNode("overhang")
|
||||
m.genreList = m.top.findNode("genrelist")
|
||||
m.infoGroup = m.top.findNode("infoGroup")
|
||||
m.star = m.top.findNode("star")
|
||||
end sub
|
||||
|
||||
sub init()
|
||||
|
@ -124,9 +125,14 @@ sub loadInitialItems()
|
|||
end if
|
||||
|
||||
m.sortField = get_user_setting("display." + m.top.parentItem.Id + ".sortField")
|
||||
sortAscendingStr = get_user_setting("display." + m.top.parentItem.Id + ".sortAscending")
|
||||
m.filter = get_user_setting("display." + m.top.parentItem.Id + ".filter")
|
||||
m.view = get_user_setting("display." + m.top.parentItem.Id + ".landing")
|
||||
sortAscendingStr = get_user_setting("display." + m.top.parentItem.Id + ".sortAscending")
|
||||
|
||||
' If user has not set a preferred view for this folder, check if they've set a default view
|
||||
if not isValid(m.view)
|
||||
m.view = get_user_setting("itemgrid.movieDefaultView")
|
||||
end if
|
||||
|
||||
if not isValid(m.sortField) then m.sortField = "SortName"
|
||||
if not isValid(m.filter) then m.filter = "All"
|
||||
|
@ -171,9 +177,12 @@ sub loadInitialItems()
|
|||
m.loadItemsTask.studioIds = ""
|
||||
m.loadItemsTask.view = "Movies"
|
||||
m.itemGrid.translation = "[96, 650]"
|
||||
m.itemGrid.itemSize = "[230, 310]"
|
||||
m.itemGrid.rowHeights = "[310]"
|
||||
m.itemGrid.numRows = "2"
|
||||
m.selectedMovieOverview.visible = true
|
||||
m.infoGroup.visible = true
|
||||
m.top.showItemTitles = "hidealways"
|
||||
|
||||
if m.options.view = "Studios" or m.view = "Studios"
|
||||
m.itemGrid.translation = "[96, 60]"
|
||||
|
@ -182,6 +191,19 @@ sub loadInitialItems()
|
|||
m.top.imageDisplayMode = "scaleToFit"
|
||||
m.selectedMovieOverview.visible = false
|
||||
m.infoGroup.visible = false
|
||||
else if LCase(m.options.view) = "moviesgrid" or LCase(m.view) = "moviesgrid"
|
||||
m.itemGrid.translation = "[96, 60]"
|
||||
m.itemGrid.numRows = "3"
|
||||
m.selectedMovieOverview.visible = false
|
||||
m.infoGroup.visible = false
|
||||
m.top.showItemTitles = get_user_setting("itemgrid.movieGridTitles")
|
||||
if LCase(m.top.showItemTitles) = "hidealways"
|
||||
m.itemGrid.itemSize = "[230, 315]"
|
||||
m.itemGrid.rowHeights = "[315]"
|
||||
else
|
||||
m.itemGrid.itemSize = "[230, 350]"
|
||||
m.itemGrid.rowHeights = "[350]"
|
||||
end if
|
||||
else if m.options.view = "Genres" or m.view = "Genres"
|
||||
m.loadItemsTask.StudioIds = m.top.parentItem.Id
|
||||
m.loadItemsTask.view = "Genres"
|
||||
|
@ -201,14 +223,16 @@ end sub
|
|||
sub setMoviesOptions(options)
|
||||
|
||||
options.views = [
|
||||
{ "Title": tr("Movies"), "Name": "Movies" },
|
||||
{ "Title": tr("Movies (Presentation)"), "Name": "Movies" },
|
||||
{ "Title": tr("Movies (Grid)"), "Name": "MoviesGrid" },
|
||||
{ "Title": tr("Studios"), "Name": "Studios" },
|
||||
{ "Title": tr("Genres"), "Name": "Genres" }
|
||||
]
|
||||
|
||||
if m.top.parentItem.json.type = "Genre"
|
||||
options.views = [
|
||||
{ "Title": tr("Movies"), "Name": "Movies" }
|
||||
{ "Title": tr("Movies (Presentation)"), "Name": "Movies" },
|
||||
{ "Title": tr("Movies (Grid)"), "Name": "MoviesGrid" },
|
||||
]
|
||||
end if
|
||||
|
||||
|
@ -226,11 +250,14 @@ sub setMoviesOptions(options)
|
|||
|
||||
options.filter = [
|
||||
{ "Title": tr("All"), "Name": "All" },
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" }
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" },
|
||||
{ "Title": tr("Played"), "Name": "Played" },
|
||||
{ "Title": tr("Unplayed"), "Name": "Unplayed" },
|
||||
{ "Title": tr("Resumable"), "Name": "Resumable" }
|
||||
]
|
||||
|
||||
if m.options.view = "Genres" or m.view = "Genres"
|
||||
options.sort = []
|
||||
options.sort = [{ "Title": tr("TITLE"), "Name": "SortName" }]
|
||||
options.filter = []
|
||||
end if
|
||||
|
||||
|
@ -239,6 +266,10 @@ sub setMoviesOptions(options)
|
|||
{ "Title": tr("TITLE"), "Name": "SortName" },
|
||||
{ "Title": tr("DATE_ADDED"), "Name": "DateCreated" },
|
||||
]
|
||||
options.filter = [
|
||||
{ "Title": tr("All"), "Name": "All" },
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" }
|
||||
]
|
||||
end if
|
||||
end sub
|
||||
|
||||
|
@ -340,6 +371,10 @@ sub ItemDataLoaded(msg)
|
|||
|
||||
m.loading = false
|
||||
m.spinner.visible = false
|
||||
' Return focus to options menu if it was opened while library was loading
|
||||
if m.options.visible
|
||||
m.options.setFocus(true)
|
||||
end if
|
||||
return
|
||||
end if
|
||||
|
||||
|
@ -359,11 +394,33 @@ sub ItemDataLoaded(msg)
|
|||
m.Loading = false
|
||||
'If there are no items to display, show message
|
||||
if m.loadedItems = 0
|
||||
m.selectedMovieOverview.visible = false
|
||||
m.infoGroup.visible = false
|
||||
|
||||
m.movieLogo.visible = false
|
||||
m.movieLogo.uri = ""
|
||||
|
||||
m.selectedMovieName.visible = false
|
||||
|
||||
SetName("")
|
||||
SetOverview("")
|
||||
SetOfficialRating("")
|
||||
SetProductionYear("")
|
||||
setFieldText("runtime", "")
|
||||
setFieldText("communityRating", "")
|
||||
setFieldText("criticRatingLabel", "")
|
||||
m.criticRatingIcon.uri = ""
|
||||
m.star.uri = ""
|
||||
|
||||
m.emptyText.text = tr("NO_ITEMS").Replace("%1", m.top.parentItem.Type)
|
||||
m.emptyText.visible = true
|
||||
end if
|
||||
|
||||
m.spinner.visible = false
|
||||
' Return focus to options menu if it was opened while library was loading
|
||||
if m.options.visible
|
||||
m.options.setFocus(true)
|
||||
end if
|
||||
end sub
|
||||
|
||||
'
|
||||
|
@ -430,12 +487,20 @@ sub onItemFocused()
|
|||
m.communityRatingGroup.visible = false
|
||||
m.criticRatingGroup.visible = false
|
||||
|
||||
if m.options.view = "Studios" or m.view = "Studios"
|
||||
if not isValid(m.selectedFavoriteItem)
|
||||
return
|
||||
end if
|
||||
|
||||
if LCase(m.options.view) = "studios" or LCase(m.view) = "studios"
|
||||
return
|
||||
else if LCase(m.options.view) = "moviesgrid" or LCase(m.view) = "moviesgrid"
|
||||
return
|
||||
end if
|
||||
|
||||
itemData = m.selectedFavoriteItem.json
|
||||
|
||||
m.star.uri = "pkg:/images/sharp_star_white_18dp.png"
|
||||
|
||||
if isValid(itemData.communityRating)
|
||||
setFieldText("communityRating", int(itemData.communityRating * 10) / 10)
|
||||
m.communityRatingGroup.visible = true
|
||||
|
@ -762,7 +827,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
else
|
||||
m.itemGrid.jumpToItem = 0
|
||||
end if
|
||||
|
||||
return true
|
||||
else if key = "replay" and m.genreList.isinFocusChain()
|
||||
if m.resetGrid = true
|
||||
m.genreList.animateToItem = 0
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
<field id="imageDisplayMode" type="string" value="scaleToZoom" />
|
||||
<field id="AlphaSelected" type="string" alias="AlphaMenu.itemAlphaSelected" alwaysNotify="true" onChange="onItemAlphaSelected" />
|
||||
<field id="alphaActive" type="boolean" value="false" />
|
||||
<field id="showItemTitles" type="string" value="showonhover" />
|
||||
<field id="jumpToItem" type="integer" value="" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
|
|
48
components/ItemGrid/MusicArtistGridItem.brs
Normal file
48
components/ItemGrid/MusicArtistGridItem.brs
Normal file
|
@ -0,0 +1,48 @@
|
|||
sub init()
|
||||
m.itemPoster = m.top.findNode("itemPoster")
|
||||
m.posterText = m.top.findNode("posterText")
|
||||
m.posterText.font.size = 30
|
||||
m.backdrop = m.top.findNode("backdrop")
|
||||
|
||||
m.itemPoster.observeField("loadStatus", "onPosterLoadStatusChanged")
|
||||
|
||||
'Parent is MarkupGrid and it's parent is the ItemGrid
|
||||
m.topParent = m.top.GetParent().GetParent()
|
||||
|
||||
'Get the imageDisplayMode for these grid items
|
||||
if m.topParent.imageDisplayMode <> invalid
|
||||
m.itemPoster.loadDisplayMode = m.topParent.imageDisplayMode
|
||||
end if
|
||||
|
||||
end sub
|
||||
|
||||
sub itemContentChanged()
|
||||
m.backdrop.blendColor = "#101010"
|
||||
|
||||
itemData = m.top.itemContent
|
||||
|
||||
if not isValid(itemData) then return
|
||||
|
||||
if LCase(itemData.type) = "musicalbum"
|
||||
m.backdrop.uri = "pkg:/images/icons/album.png"
|
||||
else if LCase(itemData.type) = "musicartist"
|
||||
m.backdrop.uri = "pkg:/images/missingArtist.png"
|
||||
else if LCase(itemData.json.type) = "musicgenre"
|
||||
m.backdrop.uri = "pkg:/images/icons/musicFolder.png"
|
||||
end if
|
||||
|
||||
m.itemPoster.uri = itemData.PosterUrl
|
||||
m.posterText.text = itemData.title
|
||||
|
||||
'If Poster not loaded, ensure "blue box" is shown until loaded
|
||||
if m.itemPoster.loadStatus <> "ready"
|
||||
m.backdrop.visible = true
|
||||
end if
|
||||
end sub
|
||||
|
||||
'Hide backdrop and text when poster loaded
|
||||
sub onPosterLoadStatusChanged()
|
||||
if m.itemPoster.loadStatus = "ready"
|
||||
m.backdrop.visible = false
|
||||
end if
|
||||
end sub
|
17
components/ItemGrid/MusicArtistGridItem.xml
Normal file
17
components/ItemGrid/MusicArtistGridItem.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="MusicArtistGridItem" extends="Group">
|
||||
<children>
|
||||
<Poster id="backdrop" translation="[0,15]" width="280" height="280" loadDisplayMode="scaleToZoom" uri="pkg:/images/white.9.png" />
|
||||
<Poster id="itemPoster" translation="[0,15]" width="280" height="280" loadDisplayMode="scaleToZoom" />
|
||||
<Rectangle id="postTextBackground" height="50" width="270" color="0x000000DD" translation = "[5, 240]">
|
||||
<ScrollingLabel id="posterText" color="#FFFFFF" maxWidth="270" height="50" horizAlign="center" vertAlign="center" />
|
||||
</Rectangle>
|
||||
</children>
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="itemContentChanged" />
|
||||
<field id="itemHasFocus" type="boolean" onChange="focusChanged" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="MusicArtistGridItem.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
||||
</component>
|
778
components/ItemGrid/MusicLibraryView.brs
Normal file
778
components/ItemGrid/MusicLibraryView.brs
Normal file
|
@ -0,0 +1,778 @@
|
|||
sub setupNodes()
|
||||
m.options = m.top.findNode("options")
|
||||
m.itemGrid = m.top.findNode("itemGrid")
|
||||
m.voiceBox = m.top.findNode("voiceBox")
|
||||
m.backdrop = m.top.findNode("backdrop")
|
||||
m.newBackdrop = m.top.findNode("backdropTransition")
|
||||
m.emptyText = m.top.findNode("emptyText")
|
||||
m.selectedArtistName = m.top.findNode("selectedArtistName")
|
||||
m.selectedArtistSongCount = m.top.findNode("selectedArtistSongCount")
|
||||
m.selectedArtistAlbumCount = m.top.findNode("selectedArtistAlbumCount")
|
||||
m.selectedArtistGenres = m.top.findNode("selectedArtistGenres")
|
||||
m.artistLogo = m.top.findNode("artistLogo")
|
||||
m.swapAnimation = m.top.findNode("backroundSwapAnimation")
|
||||
m.spinner = m.top.findNode("spinner")
|
||||
m.Alpha = m.top.findNode("AlphaMenu")
|
||||
m.AlphaSelected = m.top.findNode("AlphaSelected")
|
||||
m.micButton = m.top.findNode("micButton")
|
||||
m.micButtonText = m.top.findNode("micButtonText")
|
||||
m.overhang = m.top.getScene().findNode("overhang")
|
||||
m.genreList = m.top.findNode("genrelist")
|
||||
end sub
|
||||
|
||||
sub init()
|
||||
setupNodes()
|
||||
|
||||
m.overhang.isVisible = false
|
||||
|
||||
m.showItemCount = get_user_setting("itemgrid.showItemCount") = "true"
|
||||
|
||||
m.swapAnimation.observeField("state", "swapDone")
|
||||
|
||||
m.loadedRows = 0
|
||||
m.loadedItems = 0
|
||||
|
||||
m.data = CreateObject("roSGNode", "ContentNode")
|
||||
|
||||
m.itemGrid.content = m.data
|
||||
|
||||
m.genreData = CreateObject("roSGNode", "ContentNode")
|
||||
m.genreList.observeField("itemSelected", "onGenreItemSelected")
|
||||
m.genreList.observeField("itemFocused", "onGenreItemFocused")
|
||||
m.genreList.content = m.genreData
|
||||
|
||||
m.itemGrid.observeField("itemFocused", "onItemFocused")
|
||||
m.itemGrid.observeField("itemSelected", "onItemSelected")
|
||||
m.itemGrid.observeField("alphaSelected", "onItemalphaSelected")
|
||||
|
||||
'Voice filter setup
|
||||
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
|
||||
m.queuedBGUri = ""
|
||||
|
||||
'Item sort - maybe load defaults from user prefs?
|
||||
m.sortField = "SortName"
|
||||
m.sortAscending = true
|
||||
|
||||
m.filter = "All"
|
||||
m.favorite = "Favorite"
|
||||
|
||||
m.loadItemsTask = createObject("roSGNode", "LoadItemsTask2")
|
||||
m.loadLogoTask = createObject("roSGNode", "LoadItemsTask2")
|
||||
|
||||
'set inital counts for overhang before content is loaded.
|
||||
m.loadItemsTask.totalRecordCount = 0
|
||||
|
||||
m.spinner.visible = true
|
||||
|
||||
'Get reset folder setting
|
||||
m.resetGrid = get_user_setting("itemgrid.reset") = "true"
|
||||
|
||||
'Check if device has voice remote
|
||||
devinfo = CreateObject("roDeviceInfo")
|
||||
|
||||
'Hide voice search if device does not have voice remote
|
||||
if devinfo.HasFeature("voice_remote") = false
|
||||
m.micButton.visible = false
|
||||
m.micButtonText.visible = false
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub OnScreenHidden()
|
||||
if not m.overhang.isVisible
|
||||
m.overhang.disableMoveAnimation = true
|
||||
m.overhang.isVisible = true
|
||||
m.overhang.disableMoveAnimation = false
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub OnScreenShown()
|
||||
m.overhang.isVisible = false
|
||||
|
||||
if m.top.lastFocus <> invalid
|
||||
m.top.lastFocus.setFocus(true)
|
||||
else
|
||||
m.top.setFocus(true)
|
||||
end if
|
||||
end sub
|
||||
|
||||
'
|
||||
'Load initial set of Data
|
||||
sub loadInitialItems()
|
||||
m.loadItemsTask.control = "stop"
|
||||
m.spinner.visible = true
|
||||
|
||||
if LCase(m.top.parentItem.json.Type) = "collectionfolder"
|
||||
m.top.HomeLibraryItem = m.top.parentItem.Id
|
||||
end if
|
||||
|
||||
if m.top.parentItem.backdropUrl <> invalid
|
||||
SetBackground(m.top.parentItem.backdropUrl)
|
||||
else
|
||||
SetBackground("")
|
||||
end if
|
||||
|
||||
m.sortField = get_user_setting("display." + m.top.parentItem.Id + ".sortField")
|
||||
sortAscendingStr = get_user_setting("display." + m.top.parentItem.Id + ".sortAscending")
|
||||
m.filter = get_user_setting("display." + m.top.parentItem.Id + ".filter")
|
||||
m.view = get_user_setting("display." + m.top.parentItem.Id + ".landing")
|
||||
|
||||
if not isValid(m.sortField) then m.sortField = "SortName"
|
||||
if not isValid(m.filter) then m.filter = "All"
|
||||
if not isValid(m.view) then m.view = "ArtistsPresentation"
|
||||
|
||||
if sortAscendingStr = invalid or LCase(sortAscendingStr) = "true"
|
||||
m.sortAscending = true
|
||||
else
|
||||
m.sortAscending = false
|
||||
end if
|
||||
|
||||
if LCase(m.top.parentItem.json.type) = "musicgenre"
|
||||
m.itemGrid.translation = "[96, 60]"
|
||||
m.loadItemsTask.itemType = "MusicAlbum"
|
||||
m.loadItemsTask.recursive = true
|
||||
m.loadItemsTask.genreIds = m.top.parentItem.id
|
||||
m.loadItemsTask.itemId = m.top.parentItem.parentFolder
|
||||
else if LCase(m.view) = "artistspresentation" or LCase(m.options.view) = "artistspresentation"
|
||||
m.loadItemsTask.genreIds = ""
|
||||
else if LCase(m.view) = "artistsgrid" or LCase(m.options.view) = "artistsgrid"
|
||||
m.loadItemsTask.genreIds = ""
|
||||
else
|
||||
m.loadItemsTask.itemId = m.top.parentItem.Id
|
||||
end if
|
||||
|
||||
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 getCollectionType() = "music"
|
||||
m.loadItemsTask.itemType = "MusicArtist"
|
||||
m.loadItemsTask.itemId = m.top.parentItem.Id
|
||||
end if
|
||||
|
||||
' By default we load Artists
|
||||
m.loadItemsTask.view = "Artists"
|
||||
m.itemGrid.translation = "[96, 420]"
|
||||
m.itemGrid.numRows = "3"
|
||||
|
||||
if LCase(m.options.view) = "albums" or LCase(m.view) = "albums"
|
||||
m.itemGrid.translation = "[96, 60]"
|
||||
m.itemGrid.numRows = "4"
|
||||
m.loadItemsTask.itemType = "MusicAlbum"
|
||||
m.top.imageDisplayMode = "scaleToFit"
|
||||
else if LCase(m.options.view) = "artistsgrid" or LCase(m.view) = "artistsgrid"
|
||||
m.itemGrid.translation = "[96, 60]"
|
||||
m.itemGrid.numRows = "4"
|
||||
else if LCase(m.options.view) = "genres" or LCase(m.view) = "genres"
|
||||
m.loadItemsTask.itemType = ""
|
||||
m.loadItemsTask.recursive = true
|
||||
m.loadItemsTask.view = "Genres"
|
||||
m.artistLogo.visible = false
|
||||
m.selectedArtistName.visible = false
|
||||
end if
|
||||
|
||||
if LCase(m.top.parentItem.json.type) = "musicgenre"
|
||||
m.itemGrid.translation = "[96, 60]"
|
||||
m.itemGrid.numRows = "4"
|
||||
m.artistLogo.visible = false
|
||||
m.selectedArtistName.visible = false
|
||||
end if
|
||||
|
||||
m.loadItemsTask.observeField("content", "ItemDataLoaded")
|
||||
m.spinner.visible = true
|
||||
m.loadItemsTask.control = "RUN"
|
||||
SetUpOptions()
|
||||
end sub
|
||||
|
||||
' Set Music view, sort, and filter options
|
||||
sub setMusicOptions(options)
|
||||
|
||||
options.views = [
|
||||
{ "Title": tr("Artists (Presentation)"), "Name": "ArtistsPresentation" },
|
||||
{ "Title": tr("Artists (Grid)"), "Name": "ArtistsGrid" },
|
||||
{ "Title": tr("Albums"), "Name": "Albums" },
|
||||
{ "Title": tr("Genres"), "Name": "Genres" }
|
||||
]
|
||||
|
||||
if LCase(m.top.parentItem.json.type) = "musicgenre"
|
||||
options.views = [
|
||||
{ "Title": tr("Albums"), "Name": "Albums" }
|
||||
]
|
||||
end if
|
||||
|
||||
options.sort = [
|
||||
{ "Title": tr("TITLE"), "Name": "SortName" },
|
||||
{ "Title": tr("DATE_ADDED"), "Name": "DateCreated" },
|
||||
{ "Title": tr("DATE_PLAYED"), "Name": "DatePlayed" },
|
||||
{ "Title": tr("RELEASE_DATE"), "Name": "PremiereDate" },
|
||||
]
|
||||
|
||||
options.filter = [
|
||||
{ "Title": tr("All"), "Name": "All" },
|
||||
{ "Title": tr("Favorites"), "Name": "Favorites" }
|
||||
]
|
||||
|
||||
if LCase(m.options.view) = "genres" or LCase(m.view) = "genres"
|
||||
options.sort = [
|
||||
{ "Title": tr("TITLE"), "Name": "SortName" },
|
||||
]
|
||||
options.filter = []
|
||||
end if
|
||||
|
||||
if LCase(m.options.view) = "albums" or LCase(m.view) = "albums"
|
||||
options.sort = [
|
||||
{ "Title": tr("TITLE"), "Name": "SortName" },
|
||||
{ "Title": tr("DATE_ADDED"), "Name": "DateCreated" },
|
||||
]
|
||||
end if
|
||||
end sub
|
||||
|
||||
' Return parent collection type
|
||||
function getCollectionType() as string
|
||||
if m.top.parentItem.collectionType = invalid
|
||||
return LCase(m.top.parentItem.Type)
|
||||
else
|
||||
return LCase(m.top.parentItem.CollectionType)
|
||||
end if
|
||||
end function
|
||||
|
||||
' Search string array for search value. Return if it's found
|
||||
function inStringArray(array, searchValue) as boolean
|
||||
for each item in array
|
||||
if lcase(item) = lcase(searchValue) then return true
|
||||
end for
|
||||
return false
|
||||
end function
|
||||
|
||||
' Data to display when options button selected
|
||||
sub SetUpOptions()
|
||||
options = {}
|
||||
options.filter = []
|
||||
options.favorite = []
|
||||
|
||||
setMusicOptions(options)
|
||||
|
||||
' Set selected view option
|
||||
for each o in options.views
|
||||
if LCase(o.Name) = LCase(m.view)
|
||||
o.Selected = true
|
||||
o.Ascending = m.sortAscending
|
||||
m.options.view = o.Name
|
||||
end if
|
||||
end for
|
||||
|
||||
' Set selected sort option
|
||||
for each o in options.sort
|
||||
if LCase(o.Name) = LCase(m.sortField)
|
||||
o.Selected = true
|
||||
o.Ascending = m.sortAscending
|
||||
m.options.sortField = o.Name
|
||||
end if
|
||||
end for
|
||||
|
||||
' Set selected filter option
|
||||
for each o in options.filter
|
||||
if LCase(o.Name) = LCase(m.filter)
|
||||
o.Selected = true
|
||||
m.options.filter = o.Name
|
||||
end if
|
||||
end for
|
||||
|
||||
m.options.options = options
|
||||
end sub
|
||||
|
||||
'
|
||||
' Logo Image Loaded Event Handler
|
||||
sub LogoImageLoaded(msg)
|
||||
data = msg.GetData()
|
||||
m.loadLogoTask.unobserveField("content")
|
||||
m.loadLogoTask.content = []
|
||||
|
||||
if data.Count() > 0
|
||||
m.artistLogo.uri = data[0]
|
||||
m.artistLogo.visible = true
|
||||
else
|
||||
m.selectedArtistName.visible = true
|
||||
end if
|
||||
end sub
|
||||
|
||||
'
|
||||
'Handle loaded data, and add to Grid
|
||||
sub ItemDataLoaded(msg)
|
||||
m.top.alphaActive = false
|
||||
itemData = msg.GetData()
|
||||
m.loadItemsTask.unobserveField("content")
|
||||
m.loadItemsTask.content = []
|
||||
|
||||
if itemData = invalid
|
||||
m.Loading = false
|
||||
return
|
||||
end if
|
||||
|
||||
if LCase(m.loadItemsTask.view) = "genres"
|
||||
for each item in itemData
|
||||
m.genreData.appendChild(item)
|
||||
end for
|
||||
|
||||
m.itemGrid.opacity = "0"
|
||||
m.genreList.opacity = "1"
|
||||
|
||||
m.itemGrid.setFocus(false)
|
||||
m.genreList.setFocus(true)
|
||||
|
||||
m.loadedItems = m.genreList.content.getChildCount()
|
||||
m.loadedRows = m.loadedItems / m.genreList.numColumns
|
||||
|
||||
m.loading = false
|
||||
m.spinner.visible = false
|
||||
return
|
||||
end if
|
||||
|
||||
m.itemGrid.opacity = "1"
|
||||
m.genreList.opacity = "0"
|
||||
|
||||
m.itemGrid.setFocus(true)
|
||||
m.genreList.setFocus(false)
|
||||
|
||||
for each item in itemData
|
||||
m.data.appendChild(item)
|
||||
end for
|
||||
|
||||
'Update the stored counts
|
||||
m.loadedItems = m.itemGrid.content.getChildCount()
|
||||
m.loadedRows = m.loadedItems / m.itemGrid.numColumns
|
||||
m.Loading = false
|
||||
'If there are no items to display, show message
|
||||
if m.loadedItems = 0
|
||||
m.emptyText.text = tr("NO_ITEMS").Replace("%1", m.top.parentItem.Type)
|
||||
m.emptyText.visible = true
|
||||
end if
|
||||
|
||||
m.spinner.visible = false
|
||||
end sub
|
||||
|
||||
'
|
||||
'Set Selected Artist Name
|
||||
sub SetName(artistName as string)
|
||||
m.selectedArtistName.text = artistName
|
||||
end sub
|
||||
|
||||
'
|
||||
'Set Selected Artist Song Count
|
||||
sub SetSongCount(totalCount)
|
||||
appendText = " " + tr("Songs")
|
||||
if totalCount = 1
|
||||
appendText = " " + tr("Song")
|
||||
end if
|
||||
|
||||
m.selectedArtistSongCount.text = totalCount.tostr() + appendText
|
||||
end sub
|
||||
'
|
||||
'Set Selected Artist Album Count
|
||||
sub SetAlbumCount(totalCount)
|
||||
appendText = " " + tr("Albums")
|
||||
if totalCount = 1
|
||||
appendText = " " + tr("Album")
|
||||
end if
|
||||
|
||||
m.selectedArtistAlbumCount.text = totalCount.tostr() + appendText
|
||||
end sub
|
||||
|
||||
'
|
||||
'Set Selected Artist Genres
|
||||
sub SetGenres(artistGenres)
|
||||
m.selectedArtistGenres.text = artistGenres.join(", ")
|
||||
end sub
|
||||
|
||||
'
|
||||
'Set Background Image
|
||||
sub SetBackground(backgroundUri as string)
|
||||
if backgroundUri = ""
|
||||
m.backdrop.opacity = 0
|
||||
end if
|
||||
|
||||
'If a new image is being loaded, or transitioned to, store URL to load next
|
||||
if LCase(m.swapAnimation.state) <> "stopped" or LCase(m.newBackdrop.loadStatus) = "loading"
|
||||
m.queuedBGUri = backgroundUri
|
||||
return
|
||||
end if
|
||||
|
||||
m.newBackdrop.uri = backgroundUri
|
||||
end sub
|
||||
|
||||
'
|
||||
'Handle new item being focused
|
||||
sub onItemFocused()
|
||||
focusedRow = m.itemGrid.currFocusRow
|
||||
|
||||
itemInt = m.itemGrid.itemFocused
|
||||
|
||||
' If no selected item, set background to parent backdrop
|
||||
if itemInt = -1
|
||||
return
|
||||
end if
|
||||
|
||||
m.artistLogo.visible = false
|
||||
m.selectedArtistName.visible = false
|
||||
m.selectedArtistGenres.visible = false
|
||||
m.selectedArtistSongCount.visible = false
|
||||
m.selectedArtistAlbumCount.visible = false
|
||||
|
||||
' Load more data if focus is within last 5 rows, and there are more items to load
|
||||
if focusedRow >= m.loadedRows - 5 and m.loadeditems < m.loadItemsTask.totalRecordCount
|
||||
loadMoreData()
|
||||
end if
|
||||
|
||||
m.selectedFavoriteItem = getItemFocused()
|
||||
|
||||
if LCase(m.options.view) = "albums" or LCase(m.view) = "albums" or LCase(m.top.parentItem.json.type) = "musicgenre"
|
||||
return
|
||||
end if
|
||||
|
||||
if LCase(m.options.view) = "artistsgrid" or LCase(m.view) = "artistsgrid"
|
||||
return
|
||||
end if
|
||||
|
||||
if not m.selectedArtistGenres.visible
|
||||
m.selectedArtistGenres.visible = true
|
||||
end if
|
||||
|
||||
if not m.selectedArtistSongCount.visible
|
||||
m.selectedArtistSongCount.visible = true
|
||||
end if
|
||||
|
||||
if not m.selectedArtistAlbumCount.visible
|
||||
m.selectedArtistAlbumCount.visible = true
|
||||
end if
|
||||
|
||||
itemData = m.selectedFavoriteItem.json
|
||||
|
||||
if isValid(itemData.SongCount)
|
||||
SetSongCount(itemData.SongCount)
|
||||
else
|
||||
SetSongCount("")
|
||||
end if
|
||||
|
||||
if isValid(itemData.AlbumCount)
|
||||
SetAlbumCount(itemData.AlbumCount)
|
||||
else
|
||||
SetAlbumCount("")
|
||||
end if
|
||||
|
||||
if isValid(itemData.Genres)
|
||||
SetGenres(itemData.Genres)
|
||||
else
|
||||
SetGenres([])
|
||||
end if
|
||||
|
||||
if isValid(itemData.Name)
|
||||
SetName(itemData.Name)
|
||||
else
|
||||
SetName("")
|
||||
end if
|
||||
|
||||
m.loadLogoTask.itemId = itemData.id
|
||||
m.loadLogoTask.itemType = "LogoImage"
|
||||
m.loadLogoTask.observeField("content", "LogoImageLoaded")
|
||||
m.loadLogoTask.control = "RUN"
|
||||
|
||||
' Set Background to item backdrop
|
||||
SetBackground(m.selectedFavoriteItem.backdropUrl)
|
||||
end sub
|
||||
|
||||
sub setFieldText(field, value)
|
||||
node = m.top.findNode(field)
|
||||
if node = invalid or value = invalid then return
|
||||
|
||||
' Handle non strings... Which _shouldn't_ happen, but hey
|
||||
if type(value) = "roInt" or type(value) = "Integer"
|
||||
value = str(value)
|
||||
else if type(value) = "roFloat" or type(value) = "Float"
|
||||
value = str(value)
|
||||
else if type(value) <> "roString" and type(value) <> "String"
|
||||
value = ""
|
||||
end if
|
||||
|
||||
node.text = value
|
||||
end sub
|
||||
|
||||
'
|
||||
'When Image Loading Status changes
|
||||
sub newBGLoaded()
|
||||
'If image load was sucessful, start the fade swap
|
||||
if LCase(m.newBackdrop.loadStatus) = "ready"
|
||||
m.swapAnimation.control = "start"
|
||||
end if
|
||||
end sub
|
||||
|
||||
'
|
||||
'Swap Complete
|
||||
sub swapDone()
|
||||
if LCase(m.swapAnimation.state) = "stopped"
|
||||
'Set main BG node image and hide transitioning node
|
||||
m.backdrop.uri = m.newBackdrop.uri
|
||||
m.backdrop.opacity = 1
|
||||
m.newBackdrop.opacity = 0
|
||||
|
||||
'If there is another one to load
|
||||
if m.newBackdrop.uri <> m.queuedBGUri and m.queuedBGUri <> ""
|
||||
SetBackground(m.queuedBGUri)
|
||||
m.queuedBGUri = ""
|
||||
end if
|
||||
end if
|
||||
end sub
|
||||
|
||||
'
|
||||
'Load next set of items
|
||||
sub loadMoreData()
|
||||
m.spinner.visible = true
|
||||
if m.Loading = true then return
|
||||
m.Loading = true
|
||||
m.loadItemsTask.startIndex = m.loadedItems
|
||||
m.loadItemsTask.observeField("content", "ItemDataLoaded")
|
||||
m.loadItemsTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
'
|
||||
'Item Selected
|
||||
sub onItemSelected()
|
||||
m.top.selectedItem = m.itemGrid.content.getChild(m.itemGrid.itemSelected)
|
||||
end sub
|
||||
|
||||
'
|
||||
'Returns Focused Item
|
||||
function getItemFocused()
|
||||
return m.itemGrid.content.getChild(m.itemGrid.itemFocused)
|
||||
end function
|
||||
|
||||
'
|
||||
'Genre Item Selected
|
||||
sub onGenreItemSelected()
|
||||
m.top.selectedItem = m.genreList.content.getChild(m.genreList.itemSelected)
|
||||
end sub
|
||||
|
||||
'
|
||||
'Genre Item Focused
|
||||
sub onGenreItemFocused()
|
||||
focusedRow = m.genreList.currFocusRow
|
||||
|
||||
' Load more data if focus is within last 5 rows, and there are more items to load
|
||||
if focusedRow >= m.loadedRows - 5 and m.loadeditems < m.loadItemsTask.totalRecordCount
|
||||
loadMoreData()
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub onItemalphaSelected()
|
||||
if m.top.alphaSelected <> ""
|
||||
m.loadedRows = 0
|
||||
m.loadedItems = 0
|
||||
|
||||
m.data = CreateObject("roSGNode", "ContentNode")
|
||||
m.itemGrid.content = m.data
|
||||
|
||||
m.genreData = CreateObject("roSGNode", "ContentNode")
|
||||
m.genreList.content = m.genreData
|
||||
|
||||
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
|
||||
|
||||
|
||||
'
|
||||
'Check if options updated and any reloading required
|
||||
sub optionsClosed()
|
||||
reload = false
|
||||
|
||||
if m.options.sortField <> m.sortField or m.options.sortAscending <> m.sortAscending
|
||||
m.sortField = m.options.sortField
|
||||
m.sortAscending = m.options.sortAscending
|
||||
reload = true
|
||||
|
||||
sortAscendingStr = "true"
|
||||
|
||||
'Store sort settings
|
||||
if not m.sortAscending
|
||||
sortAscendingStr = "false"
|
||||
end if
|
||||
|
||||
set_user_setting("display." + m.top.parentItem.Id + ".sortField", m.sortField)
|
||||
set_user_setting("display." + m.top.parentItem.Id + ".sortAscending", sortAscendingStr)
|
||||
end if
|
||||
|
||||
if m.options.filter <> m.filter
|
||||
m.filter = m.options.filter
|
||||
reload = true
|
||||
set_user_setting("display." + m.top.parentItem.Id + ".filter", m.options.filter)
|
||||
end if
|
||||
|
||||
m.view = get_user_setting("display." + m.top.parentItem.Id + ".landing")
|
||||
|
||||
if m.options.view <> m.view
|
||||
m.view = m.options.view
|
||||
m.top.view = m.view
|
||||
set_user_setting("display." + m.top.parentItem.Id + ".landing", m.view)
|
||||
|
||||
' Reset any filtering or search terms
|
||||
m.top.alphaSelected = ""
|
||||
m.loadItemsTask.NameStartsWith = " "
|
||||
m.loadItemsTask.searchTerm = ""
|
||||
m.filter = "All"
|
||||
m.sortField = "SortName"
|
||||
m.sortAscending = true
|
||||
|
||||
' Reset view to defaults
|
||||
set_user_setting("display." + m.top.parentItem.Id + ".sortField", m.sortField)
|
||||
set_user_setting("display." + m.top.parentItem.Id + ".sortAscending", "true")
|
||||
set_user_setting("display." + m.top.parentItem.Id + ".filter", m.filter)
|
||||
|
||||
reload = true
|
||||
end if
|
||||
|
||||
if reload
|
||||
m.loadedRows = 0
|
||||
m.loadedItems = 0
|
||||
m.data = CreateObject("roSGNode", "ContentNode")
|
||||
m.genreData = CreateObject("roSGNode", "ContentNode")
|
||||
m.itemGrid.content = m.data
|
||||
m.genreList.content = m.genreData
|
||||
loadInitialItems()
|
||||
end if
|
||||
|
||||
m.itemGrid.setFocus(m.itemGrid.opacity = 1)
|
||||
m.genreList.setFocus(m.genreList.opacity = 1)
|
||||
end sub
|
||||
|
||||
sub onChannelSelected(msg)
|
||||
node = msg.getRoSGNode()
|
||||
m.top.lastFocus = lastFocusedChild(node)
|
||||
if node.watchChannel <> invalid
|
||||
' Clone the node when it's reused/update in the TimeGrid it doesn't automatically start playing
|
||||
m.top.selectedItem = node.watchChannel.clone(false)
|
||||
end if
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
||||
if key = "left" and m.voiceBox.isinFocusChain()
|
||||
m.itemGrid.setFocus(m.itemGrid.opacity = 1)
|
||||
m.genreList.setFocus(m.genreList.opacity = 1)
|
||||
m.voiceBox.setFocus(false)
|
||||
end if
|
||||
|
||||
if key = "options"
|
||||
if m.options.visible = true
|
||||
m.options.visible = false
|
||||
m.top.removeChild(m.options)
|
||||
optionsClosed()
|
||||
else
|
||||
|
||||
itemSelected = m.selectedFavoriteItem
|
||||
if itemSelected <> invalid
|
||||
m.options.selectedFavoriteItem = itemSelected
|
||||
end if
|
||||
|
||||
m.options.visible = true
|
||||
m.top.appendChild(m.options)
|
||||
m.options.setFocus(true)
|
||||
end if
|
||||
return true
|
||||
else if key = "back"
|
||||
if m.options.visible = true
|
||||
m.options.visible = false
|
||||
optionsClosed()
|
||||
return true
|
||||
else
|
||||
m.global.sceneManager.callfunc("popScene")
|
||||
m.loadItemsTask.control = "stop"
|
||||
return true
|
||||
end if
|
||||
else if key = "left"
|
||||
if m.itemGrid.isinFocusChain()
|
||||
m.top.alphaActive = true
|
||||
m.itemGrid.setFocus(false)
|
||||
alpha = m.alpha.getChild(0).findNode("Alphamenu")
|
||||
alpha.setFocus(true)
|
||||
return true
|
||||
else if m.genreList.isinFocusChain()
|
||||
m.top.alphaActive = true
|
||||
m.genreList.setFocus(false)
|
||||
alpha = m.alpha.getChild(0).findNode("Alphamenu")
|
||||
alpha.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
||||
else if key = "right" and m.Alpha.isinFocusChain()
|
||||
m.top.alphaActive = false
|
||||
m.Alpha.setFocus(false)
|
||||
m.Alpha.visible = true
|
||||
|
||||
m.itemGrid.setFocus(m.itemGrid.opacity = 1)
|
||||
m.genreList.setFocus(m.genreList.opacity = 1)
|
||||
|
||||
return true
|
||||
|
||||
else if key = "replay" and m.itemGrid.isinFocusChain()
|
||||
if m.resetGrid = true
|
||||
m.itemGrid.animateToItem = 0
|
||||
else
|
||||
m.itemGrid.jumpToItem = 0
|
||||
end if
|
||||
|
||||
else if key = "replay" and m.genreList.isinFocusChain()
|
||||
if m.resetGrid = true
|
||||
m.genreList.animateToItem = 0
|
||||
else
|
||||
m.genreList.jumpToItem = 0
|
||||
end if
|
||||
return true
|
||||
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
|
51
components/ItemGrid/MusicLibraryView.xml
Normal file
51
components/ItemGrid/MusicLibraryView.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="MusicLibraryView" extends="JFScreen">
|
||||
<children>
|
||||
<Rectangle id="screenSaverBackground" width="1920" height="1080" color="#000000" />
|
||||
|
||||
<VoiceTextEditBox id="VoiceBox" visible="true" width = "40" translation = "[52, 120]" />
|
||||
<Rectangle id="VoiceBoxCover" height="240" width="100" color="0x000000ff" translation = "[25, 75]" />
|
||||
|
||||
<maskGroup translation="[820, 0]" id="backgroundMask" maskUri="pkg:/images/backgroundmask.png" maskSize="[1220,445]">
|
||||
<poster id="backdrop" loadDisplayMode="scaleToFill" width="1100" height="450" opacity="1" />
|
||||
<poster id="backdropTransition" loadDisplayMode="scaleToFill" width="1100" height="450" opacity="1" />
|
||||
</maskGroup>
|
||||
|
||||
<Label id="selectedArtistName" visible="false" translation="[120, 40]" wrap="true" font="font:LargeBoldSystemFont" width="850" height="196" horizAlign="left" vertAlign="center" />
|
||||
<Poster id="artistLogo" visible="false" translation="[120, 40]" loadDisplayMode="scaleToFit" width="384" height="196" />
|
||||
<Label id="selectedArtistSongCount" translation="[120, 270]" wrap="true" font="font:SmallestSystemFont" width="850" height="30" horizAlign="left" />
|
||||
<Label id="selectedArtistAlbumCount" translation="[120, 310]" wrap="true" font="font:SmallestSystemFont" width="850" height="30" horizAlign="left" />
|
||||
<Label id="selectedArtistGenres" translation="[120, 350]" wrap="true" font="font:SmallestSystemFont" width="850" height="30" horizAlign="left" />
|
||||
|
||||
<MarkupGrid id="itemGrid" itemComponentName="MusicArtistGridItem" numColumns="6" numRows="2" vertFocusAnimationStyle="fixed" itemSize="[280, 280]" itemSpacing="[20, 20]" />
|
||||
<MarkupGrid id="genrelist" itemComponentName="MusicArtistGridItem" numColumns="6" numRows="4" vertFocusAnimationStyle="fixed" translation="[96, 60]" itemSize="[280, 280]" itemSpacing="[20, 20]" opacity="0" />
|
||||
|
||||
<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="[900, 450]" />
|
||||
<Animation id="backroundSwapAnimation" duration="1" repeat="false" easeFunction="linear">
|
||||
<FloatFieldInterpolator id = "fadeinLoading" key="[0.0, 1.0]" keyValue="[ 0.00, 1.00 ]" fieldToInterp="backdropTransition.opacity" />
|
||||
<FloatFieldInterpolator id = "fadeoutLoaded" key="[0.0, 1.0]" keyValue="[ 1.00, 0.00 ]" fieldToInterp="backdrop.opacity" />
|
||||
</Animation>
|
||||
<Alpha id="AlphaMenu" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="HomeLibraryItem" type="string"/>
|
||||
<field id="View" type="string"/>
|
||||
<field id="parentItem" type="node" onChange="loadInitialItems" />
|
||||
<field id="selectedItem" type="node" alwaysNotify="true" />
|
||||
<field id="quickPlayNode" type="node" alwaysNotify="true" />
|
||||
<field id="imageDisplayMode" type="string" value="scaleToZoom" />
|
||||
<field id="AlphaSelected" type="string" alias="AlphaMenu.itemAlphaSelected" alwaysNotify="true" onChange="onItemAlphaSelected" />
|
||||
<field id="alphaActive" type="boolean" value="false" />
|
||||
<field id="jumpToItem" type="integer" value="" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/deviceCapabilities.brs" />
|
||||
<script type="text/brightscript" uri="MusicLibraryView.brs" />
|
||||
</component>
|
|
@ -11,7 +11,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
return true
|
||||
else if key = "options"
|
||||
group = m.global.sceneManager.callFunc("getActiveScene")
|
||||
if group <> invalid and group.optionsAvailable
|
||||
if isValid(group) and isValid(group.optionsAvailable) and group.optionsAvailable
|
||||
group.lastFocus = group.focusedChild
|
||||
panel = group.findNode("options")
|
||||
panel.visible = true
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
<field id="exit" type="boolean" alwaysNotify="true" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="JFScene.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
</component>
|
||||
|
|
|
@ -3,6 +3,8 @@ sub init()
|
|||
m.staticTitle = m.top.findNode("staticTitle")
|
||||
m.series = m.top.findNode("Series")
|
||||
m.poster = m.top.findNode("poster")
|
||||
m.unplayedCount = m.top.findNode("unplayedCount")
|
||||
m.unplayedEpisodeCount = m.top.findNode("unplayedEpisodeCount")
|
||||
|
||||
m.backdrop = m.top.findNode("backdrop")
|
||||
|
||||
|
@ -55,6 +57,13 @@ sub itemContentChanged() as void
|
|||
itemData = m.top.itemContent
|
||||
m.title.text = itemData.title
|
||||
|
||||
if itemData?.json?.UserData?.UnplayedItemCount <> invalid
|
||||
if itemData.json.UserData.UnplayedItemCount > 0
|
||||
m.unplayedCount.visible = true
|
||||
m.unplayedEpisodeCount.text = itemData.json.UserData.UnplayedItemCount
|
||||
end if
|
||||
end if
|
||||
|
||||
if itemData.json.lookup("Type") = "Episode" and itemData.json.IndexNumber <> invalid
|
||||
m.title.text = StrI(itemData.json.IndexNumber) + ". " + m.title.text
|
||||
|
||||
|
|
|
@ -2,32 +2,20 @@
|
|||
<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"
|
||||
font="font:SmallSystemFont"
|
||||
repeatCount="0"
|
||||
visible="false"
|
||||
/>
|
||||
<Label id="staticTitle"
|
||||
horizAlign="center"
|
||||
font="font:SmallSystemFont"
|
||||
wrap="false"
|
||||
/>
|
||||
<ScrollingLabel id="Series" horizAlign="center" font="font:SmallSystemFont" repeatCount="0" visible="false" />
|
||||
<Poster id="poster" translation="[2,0]" loadDisplayMode="scaleToFit">
|
||||
<Rectangle id="unplayedCount" visible="false" width="90" height="60" color="#00a4dcFF" translation="[104, 0]">
|
||||
<Label id="unplayedEpisodeCount" width="90" height="60" font="font:SmallestBoldSystemFont" horizAlign="center" vertAlign="center" />
|
||||
</Rectangle>
|
||||
</Poster>
|
||||
<ScrollingLabel id="title" horizAlign="center" font="font:SmallSystemFont" repeatCount="0" visible="false" />
|
||||
<Label id="staticTitle" horizAlign="center" font="font:SmallSystemFont" wrap="false" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="itemContent" type="node" onChange="itemContentChanged"/>
|
||||
<field id="itemWidth" type="integer" />
|
||||
<field id="itemHasFocus" type="boolean" onChange="focusChanged" />
|
||||
<!-- mediatype -->
|
||||
</interface>
|
||||
|
||||
<script type="text/brightscript" uri="ListPoster.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
||||
</component>
|
||||
|
|
46
components/WhatsNewDialog.brs
Normal file
46
components/WhatsNewDialog.brs
Normal file
|
@ -0,0 +1,46 @@
|
|||
sub init()
|
||||
m.content = m.top.findNode("content")
|
||||
|
||||
setPalette()
|
||||
|
||||
m.top.id = "OKDialog"
|
||||
m.top.height = 900
|
||||
m.top.title = "What's New?"
|
||||
m.top.buttons = [tr("OK")]
|
||||
|
||||
dialogStyles = {
|
||||
"default": {
|
||||
"fontSize": 27,
|
||||
"fontUri": "font:SystemFontFile",
|
||||
"color": "#EFEFEFFF"
|
||||
},
|
||||
"author": {
|
||||
"fontSize": 27,
|
||||
"fontUri": "font:SystemFontFile",
|
||||
"color": "#00a4dcFF"
|
||||
}
|
||||
}
|
||||
|
||||
whatsNewList = ParseJSON(ReadAsciiFile("pkg:/source/static/whatsNew.json"))
|
||||
|
||||
for each item in whatsNewList
|
||||
textLine = m.content.CreateChild("StdDlgMultiStyleTextItem")
|
||||
textLine.drawingStyles = dialogStyles
|
||||
textLine.text = "• " + item.description + " <author>" + item.author + "</author>"
|
||||
end for
|
||||
|
||||
end sub
|
||||
|
||||
sub setPalette()
|
||||
dlgPalette = createObject("roSGNode", "RSGPalette")
|
||||
dlgPalette.colors = {
|
||||
DialogBackgroundColor: "0x262828FF",
|
||||
DialogFocusColor: "0xcececeFF",
|
||||
DialogFocusItemColor: "0x202020FF",
|
||||
DialogSecondaryTextColor: "0xf8f8f8ff",
|
||||
DialogSecondaryItemColor: "#00a4dcFF",
|
||||
DialogTextColor: "0xeeeeeeFF"
|
||||
}
|
||||
|
||||
m.top.palette = dlgPalette
|
||||
end sub
|
7
components/WhatsNewDialog.xml
Normal file
7
components/WhatsNewDialog.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="WhatsNewDialog" extends="StandardMessageDialog">
|
||||
<children>
|
||||
<StdDlgContentArea id="content" />
|
||||
</children>
|
||||
<script type="text/brightscript" uri="WhatsNewDialog.brs" />
|
||||
</component>
|
|
@ -11,10 +11,10 @@ sub setPoster()
|
|||
if m.top.image <> invalid
|
||||
m.top.posterURL = m.top.image.url
|
||||
else if m.top.json.ImageTags <> invalid and m.top.json.ImageTags.Primary <> invalid
|
||||
imgParams = { "maxHeight": 60 }
|
||||
imgParams = { "maxHeight": 60, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.hdsmalliconurl = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
end if
|
||||
end sub
|
||||
|
|
|
@ -18,16 +18,16 @@ sub setPoster()
|
|||
else
|
||||
|
||||
if m.top.json.ImageTags.Primary <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
else if m.top.json.BackdropImageTags <> invalid
|
||||
imgParams = { "maxHeight": 440 }
|
||||
imgParams = { "maxHeight": 440, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
' Add Backdrop Image
|
||||
if m.top.json.BackdropImageTags <> invalid
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280 }
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ sub setPoster()
|
|||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ParentThumbImageTag }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Thumb", imgParams)
|
||||
else if m.top.json.ImageTags.Primary <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
end if
|
||||
end sub
|
||||
|
|
|
@ -16,7 +16,7 @@ sub setData()
|
|||
' Set appropriate Images for Wide and Tall based on type
|
||||
|
||||
if datum.type = "CollectionFolder" or datum.type = "UserView"
|
||||
params = { "maxHeight": 261, "maxWidth": 464 }
|
||||
params = { "Tag": datum.ImageTags.Primary, "maxHeight": 261, "maxWidth": 464 }
|
||||
m.top.thumbnailURL = ImageURL(datum.id, "Primary", params)
|
||||
m.top.widePosterUrl = m.top.thumbnailURL
|
||||
|
||||
|
@ -33,14 +33,22 @@ sub setData()
|
|||
imgParams.Append({ "maxHeight": 261 })
|
||||
imgParams.Append({ "maxWidth": 464 })
|
||||
|
||||
if datum.ImageTags.Primary <> invalid
|
||||
param = { "Tag": datum.ImageTags.Primary }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
|
||||
m.top.thumbnailURL = ImageURL(datum.id, "Primary", imgParams)
|
||||
|
||||
' Add Wide Poster (Series Backdrop)
|
||||
if datum.ParentThumbImageTag <> invalid
|
||||
imgParams["Tag"] = datum.ParentThumbImageTag
|
||||
m.top.widePosterUrl = ImageURL(datum.ParentThumbItemId, "Thumb", imgParams)
|
||||
else if datum.ParentBackdropImageTags <> invalid
|
||||
imgParams["Tag"] = datum.ParentBackdropImageTags[0]
|
||||
m.top.widePosterUrl = ImageURL(datum.ParentBackdropItemId, "Backdrop", imgParams)
|
||||
else if datum.ImageTags.Primary <> invalid
|
||||
imgParams["Tag"] = datum.SeriesPrimaryImageTag
|
||||
m.top.widePosterUrl = ImageURL(datum.id, "Primary", imgParams)
|
||||
end if
|
||||
|
||||
|
@ -48,16 +56,14 @@ sub setData()
|
|||
imgParams = { "maxHeight": 261 }
|
||||
imgParams.Append({ "maxWidth": 464 })
|
||||
|
||||
if datum.UserData.UnplayedItemCount > 0
|
||||
imgParams["UnplayedCount"] = datum.UserData.UnplayedItemCount
|
||||
end if
|
||||
|
||||
m.top.posterURL = ImageURL(datum.id, "Primary", imgParams)
|
||||
|
||||
' Add Wide Poster (Series Backdrop)
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid
|
||||
imgParams["Tag"] = datum.imageTags.Thumb
|
||||
m.top.widePosterUrl = ImageURL(datum.Id, "Thumb", imgParams)
|
||||
else if datum.BackdropImageTags <> invalid
|
||||
imgParams["Tag"] = datum.BackdropImageTags[0]
|
||||
m.top.widePosterUrl = ImageURL(datum.Id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
|
@ -67,14 +73,21 @@ sub setData()
|
|||
imgParams.Append({ "maxHeight": 261 })
|
||||
imgParams.Append({ "maxWidth": 175 })
|
||||
|
||||
if datum.ImageTags.Primary <> invalid
|
||||
param = { "Tag": datum.ImageTags.Primary }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
|
||||
m.top.posterURL = ImageURL(datum.id, "Primary", imgParams)
|
||||
|
||||
' For wide image, use backdrop
|
||||
imgParams["maxWidth"] = 464
|
||||
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid
|
||||
imgParams["Tag"] = datum.imageTags.Thumb
|
||||
m.top.thumbnailUrl = ImageURL(datum.Id, "Thumb", imgParams)
|
||||
else if datum.BackdropImageTags[0] <> invalid
|
||||
imgParams["Tag"] = datum.BackdropImageTags[0]
|
||||
m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
|
@ -84,24 +97,31 @@ sub setData()
|
|||
imgParams.Append({ "maxHeight": 261 })
|
||||
imgParams.Append({ "maxWidth": 175 })
|
||||
|
||||
if datum.ImageTags.Primary <> invalid
|
||||
param = { "Tag": datum.ImageTags.Primary }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
|
||||
m.top.posterURL = ImageURL(datum.id, "Primary", imgParams)
|
||||
|
||||
' For wide image, use backdrop
|
||||
imgParams["maxWidth"] = 464
|
||||
|
||||
if datum.ImageTags <> invalid and datum.imageTags.Thumb <> invalid
|
||||
imgParams["Tag"] = datum.imageTags.Thumb
|
||||
m.top.thumbnailUrl = ImageURL(datum.Id, "Thumb", imgParams)
|
||||
else if datum.BackdropImageTags[0] <> invalid
|
||||
imgParams["Tag"] = datum.BackdropImageTags[0]
|
||||
m.top.thumbnailUrl = ImageURL(datum.id, "Backdrop", imgParams)
|
||||
end if
|
||||
else if datum.type = "MusicAlbum"
|
||||
params = { "maxHeight": 261, "maxWidth": 261 }
|
||||
params = { "Tag": datum.ImageTags.Primary, "maxHeight": 261, "maxWidth": 261 }
|
||||
m.top.thumbnailURL = ImageURL(datum.id, "Primary", params)
|
||||
m.top.widePosterUrl = m.top.thumbnailURL
|
||||
m.top.posterUrl = m.top.thumbnailURL
|
||||
|
||||
else if datum.type = "TvChannel" or datum.type = "Channel"
|
||||
params = { "maxHeight": 261, "maxWidth": 464 }
|
||||
params = { "Tag": datum.ImageTags.Primary, "maxHeight": 261, "maxWidth": 464 }
|
||||
m.top.thumbnailURL = ImageURL(datum.id, "Primary", params)
|
||||
m.top.widePosterUrl = m.top.thumbnailURL
|
||||
m.top.iconUrl = "pkg:/images/media_type_icons/live_tv_white.png"
|
||||
|
|
|
@ -40,20 +40,20 @@ sub setPoster()
|
|||
else
|
||||
|
||||
if m.top.json.ImageTags.Primary <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
else if m.top.json.BackdropImageTags[0] <> invalid
|
||||
imgParams = { "maxHeight": 440 }
|
||||
imgParams = { "maxHeight": 440, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
else if m.top.json.ParentThumbImageTag <> invalid and m.top.json.ParentThumbItemId <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ParentThumbImageTag }
|
||||
m.top.posterURL = ImageURL(m.top.json.ParentThumbItemId, "Thumb", imgParams)
|
||||
end if
|
||||
|
||||
' Add Backdrop Image
|
||||
if m.top.json.BackdropImageTags <> invalid
|
||||
if m.top.json.BackdropImageTags[0] <> invalid
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280 }
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
end if
|
||||
|
|
|
@ -12,19 +12,19 @@ sub setPoster()
|
|||
else
|
||||
|
||||
if m.top.json.ImageTags.Primary <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
else if m.top.json.BackdropImageTags[0] <> invalid
|
||||
imgParams = { "maxHeight": 440 }
|
||||
imgParams = { "maxHeight": 440, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
else if m.top.json.ParentThumbImageTag <> invalid and m.top.json.ParentThumbItemId <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ParentThumbImageTag }
|
||||
m.top.posterURL = ImageURL(m.top.json.ParentThumbItemId, "Thumb", imgParams)
|
||||
end if
|
||||
|
||||
' Add Backdrop Image
|
||||
if m.top.json.BackdropImageTags[0] <> invalid
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280 }
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
|
|
|
@ -14,19 +14,19 @@ sub setPoster()
|
|||
else
|
||||
|
||||
if m.top.json.ImageTags.Primary <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
else if m.top.json.BackdropImageTags[0] <> invalid
|
||||
imgParams = { "maxHeight": 440 }
|
||||
imgParams = { "maxHeight": 440, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
else if m.top.json.ParentThumbImageTag <> invalid and m.top.json.ParentThumbItemId <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ParentThumbImageTag }
|
||||
m.top.posterURL = ImageURL(m.top.json.ParentThumbItemId, "Thumb", imgParams)
|
||||
end if
|
||||
|
||||
' Add Backdrop Image
|
||||
if m.top.json.BackdropImageTags[0] <> invalid
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280 }
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ sub setPoster()
|
|||
m.top.posterURL = m.top.image.url
|
||||
else
|
||||
if m.top.json.ImageTags <> invalid and m.top.json.ImageTags.Thumb <> invalid
|
||||
imgParams = { "maxHeight": 500, "maxWidth": 500 }
|
||||
imgParams = { "maxHeight": 500, "maxWidth": 500, "Tag": m.top.json.ImageTags.Thumb }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Thumb", imgParams)
|
||||
end if
|
||||
end if
|
||||
|
|
|
@ -32,16 +32,16 @@ sub setPoster()
|
|||
|
||||
if m.top.json.ImageTags.Primary <> invalid
|
||||
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
else if m.top.json.BackdropImageTags <> invalid
|
||||
imgParams = { "maxHeight": 440 }
|
||||
imgParams = { "maxHeight": 440, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
' Add Backdrop Image
|
||||
if m.top.json.BackdropImageTags <> invalid
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280 }
|
||||
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag": m.top.json.BackdropImageTags[0] }
|
||||
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
|
||||
end if
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ sub setPoster()
|
|||
if m.top.image <> invalid
|
||||
m.top.posterURL = m.top.image.url
|
||||
else if m.top.json.ImageTags.Primary <> invalid
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295 }
|
||||
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag": m.top.json.ImageTags.Primary }
|
||||
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
|
||||
end if
|
||||
end sub
|
||||
|
|
|
@ -16,4 +16,5 @@
|
|||
<script type="text/brightscript" uri="Home.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" />
|
||||
</component>
|
||||
|
|
|
@ -7,6 +7,8 @@ sub init()
|
|||
m.itemIcon = m.top.findNode("itemIcon")
|
||||
m.itemTextExtra = m.top.findNode("itemTextExtra")
|
||||
m.itemPoster.observeField("loadStatus", "onPosterLoadStatusChanged")
|
||||
m.unplayedCount = m.top.findNode("unplayedCount")
|
||||
m.unplayedEpisodeCount = m.top.findNode("unplayedEpisodeCount")
|
||||
|
||||
m.showProgressBarAnimation = m.top.findNode("showProgressBar")
|
||||
m.showProgressBarField = m.top.findNode("showProgressBarField")
|
||||
|
@ -24,19 +26,26 @@ sub itemContentChanged()
|
|||
if itemData = invalid then return
|
||||
itemData.Title = itemData.name ' Temporarily required while we move from "HomeItem" to "JFContentItem"
|
||||
|
||||
|
||||
m.itemPoster.width = itemData.imageWidth
|
||||
m.itemText.maxWidth = itemData.imageWidth
|
||||
m.itemTextExtra.width = itemData.imageWidth
|
||||
m.itemTextExtra.visible = true
|
||||
|
||||
|
||||
m.backdrop.width = itemData.imageWidth
|
||||
|
||||
if itemData.iconUrl <> invalid
|
||||
m.itemIcon.uri = itemData.iconUrl
|
||||
end if
|
||||
|
||||
if LCase(itemData.type) = "series"
|
||||
if itemData?.json?.UserData?.UnplayedItemCount <> invalid
|
||||
if itemData.json.UserData.UnplayedItemCount > 0
|
||||
m.unplayedCount.visible = true
|
||||
m.unplayedEpisodeCount.text = itemData.json.UserData.UnplayedItemCount
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
|
||||
' Format the Data based on the type of Home Data
|
||||
if itemData.type = "CollectionFolder" or itemData.type = "UserView" or itemData.type = "Channel"
|
||||
m.itemText.text = itemData.name
|
||||
|
@ -64,8 +73,12 @@ sub itemContentChanged()
|
|||
' "Program" is from clicking on an "On Now" item on the Home Screen
|
||||
if itemData.type = "Program"
|
||||
m.itemText.Text = itemData.json.name
|
||||
if itemData.json.ImageURL <> invalid
|
||||
m.itemPoster.uri = itemData.json.ImageURL
|
||||
m.itemTextExtra.Text = itemData.json.ChannelName
|
||||
if itemData.widePosterURL <> ""
|
||||
m.itemPoster.uri = ImageURL(itemData.widePosterURL)
|
||||
else
|
||||
m.itemPoster.uri = ImageURL(itemData.json.ChannelId)
|
||||
m.itemPoster.loadDisplayMode = "scaleToFill"
|
||||
end if
|
||||
|
||||
' Set Episode title if available
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
<children>
|
||||
<Rectangle id="backdrop" width="464" height="261" translation="[8,5]" />
|
||||
<Poster id="itemIcon" width="100" height="100" translation="[190,85]" loadDisplayMode="scaleToFit" />
|
||||
<Poster id="itemPoster" width="464" height="261" translation="[8,5]" loadDisplayMode="scaleToZoom" />
|
||||
|
||||
<Poster id="itemPoster" width="464" height="261" translation="[8,5]" loadDisplayMode="scaleToZoom">
|
||||
<Rectangle id="unplayedCount" visible="false" width="90" height="60" color="#00a4dcFF" translation="[375, 0]">
|
||||
<Label id="unplayedEpisodeCount" width="90" height="60" font="font:SmallestBoldSystemFont" horizAlign="center" vertAlign="center" />
|
||||
</Rectangle>
|
||||
</Poster>
|
||||
<Rectangle id="progressBackground" visible="false" color="0x00000098" width="464" height="8" translation="[8,260]">
|
||||
<Rectangle id="progress" color="#00a4dcFF" width="0" height="8" />
|
||||
</Rectangle>
|
||||
|
||||
<ScrollingLabel id="itemText" horizAlign="center" vertAlign="center" font="font:SmallBoldSystemFont" height="64" maxWidth="456" translation="[8,267]" repeatCount="0" />
|
||||
<Label id="itemTextExtra" horizAlign="left" vertAlign="center" font="font:SmallBoldSystemFont" height="32" width="456" translation="[8,300]" visible="false" color="#777777FF" />
|
||||
|
||||
|
|
|
@ -61,10 +61,13 @@ sub onLibrariesLoaded()
|
|||
m.LoadLibrariesTask.content = []
|
||||
' create My Media, Continue Watching, and Next Up rows
|
||||
content = CreateObject("roSGNode", "ContentNode")
|
||||
|
||||
mediaRow = content.CreateChild("HomeRow")
|
||||
mediaRow.title = tr("My Media")
|
||||
|
||||
continueRow = content.CreateChild("HomeRow")
|
||||
continueRow.title = tr("Continue Watching")
|
||||
|
||||
nextUpRow = content.CreateChild("HomeRow")
|
||||
nextUpRow.title = tr("Next Up >")
|
||||
|
||||
|
@ -79,6 +82,19 @@ sub onLibrariesLoaded()
|
|||
]
|
||||
|
||||
haveLiveTV = false
|
||||
|
||||
' Load the NextUp Data
|
||||
m.LoadNextUpTask.observeField("content", "updateNextUpItems")
|
||||
m.LoadNextUpTask.control = "RUN"
|
||||
|
||||
' Load the Continue Watching Data
|
||||
m.LoadContinueTask.observeField("content", "updateContinueItems")
|
||||
m.LoadContinueTask.control = "RUN"
|
||||
|
||||
' Load the Favorites Data
|
||||
m.LoadFavoritesTask.observeField("content", "updateFavoritesItems")
|
||||
m.LoadFavoritesTask.control = "RUN"
|
||||
|
||||
' validate library data
|
||||
if m.libraryData <> invalid and m.libraryData.count() > 0
|
||||
userConfig = m.top.userConfig
|
||||
|
@ -92,36 +108,38 @@ sub onLibrariesLoaded()
|
|||
' create a "Latest In" row for each library
|
||||
filteredLatest = filterNodeArray(m.libraryData, "id", userConfig.LatestItemsExcludes)
|
||||
for each lib in filteredLatest
|
||||
if lib.collectionType <> "boxsets" and lib.collectionType <> "livetv"
|
||||
if lib.collectionType <> "boxsets" and lib.collectionType <> "livetv" and lib.json.CollectionType <> "Program"
|
||||
latestInRow = content.CreateChild("HomeRow")
|
||||
latestInRow.title = tr("Latest in") + " " + lib.name + " >"
|
||||
sizeArray.Push([464, 331])
|
||||
|
||||
loadLatest = createObject("roSGNode", "LoadItemsTask")
|
||||
loadLatest.itemsToLoad = "latest"
|
||||
loadLatest.itemId = lib.id
|
||||
|
||||
metadata = { "title": lib.name }
|
||||
metadata.Append({ "contentType": lib.json.CollectionType })
|
||||
loadLatest.metadata = metadata
|
||||
|
||||
loadLatest.observeField("content", "updateLatestItems")
|
||||
loadLatest.control = "RUN"
|
||||
else if lib.collectionType = "livetv"
|
||||
' If we have Live TV, add "On Now"
|
||||
onNowRow = content.CreateChild("HomeRow")
|
||||
onNowRow.title = tr("On Now")
|
||||
sizeArray.Push([464, 331])
|
||||
haveLiveTV = true
|
||||
' If we have Live TV access, load "On Now" data
|
||||
if haveLiveTV
|
||||
m.LoadOnNowTask.observeField("content", "updateOnNowItems")
|
||||
m.LoadOnNowTask.control = "RUN"
|
||||
end if
|
||||
end if
|
||||
end for
|
||||
end if
|
||||
|
||||
m.top.rowItemSize = sizeArray
|
||||
m.top.content = content
|
||||
|
||||
' Load the Continue Watching Data
|
||||
m.LoadContinueTask.observeField("content", "updateContinueItems")
|
||||
m.LoadContinueTask.control = "RUN"
|
||||
|
||||
' Load the Favorites Data
|
||||
m.LoadFavoritesTask.observeField("content", "updateFavoritesItems")
|
||||
m.LoadFavoritesTask.control = "RUN"
|
||||
|
||||
' If we have Live TV access, load "On Now" data
|
||||
if haveLiveTV
|
||||
m.LoadOnNowTask.observeField("content", "updateOnNowItems")
|
||||
m.LoadOnNowTask.control = "RUN"
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub updateHomeRows()
|
||||
|
@ -219,9 +237,6 @@ sub updateContinueItems()
|
|||
homeRows.replaceChild(row, continueRowIndex)
|
||||
end if
|
||||
end if
|
||||
|
||||
m.LoadNextUpTask.observeField("content", "updateNextUpItems")
|
||||
m.LoadNextUpTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
sub updateNextUpItems()
|
||||
|
@ -274,24 +289,6 @@ sub updateNextUpItems()
|
|||
m.global.app_loaded = true
|
||||
end if
|
||||
|
||||
|
||||
' create task nodes for "Latest In" rows
|
||||
userConfig = m.top.userConfig
|
||||
filteredLatest = filterNodeArray(m.libraryData, "id", userConfig.LatestItemsExcludes)
|
||||
for each lib in filteredLatest
|
||||
if lib.collectionType <> "livetv" and lib.collectionType <> "boxsets" and lib.json.CollectionType <> "Program"
|
||||
loadLatest = createObject("roSGNode", "LoadItemsTask")
|
||||
loadLatest.itemsToLoad = "latest"
|
||||
loadLatest.itemId = lib.id
|
||||
|
||||
metadata = { "title": lib.name }
|
||||
metadata.Append({ "contentType": lib.json.CollectionType })
|
||||
loadLatest.metadata = metadata
|
||||
|
||||
loadLatest.observeField("content", "updateLatestItems")
|
||||
loadLatest.control = "RUN"
|
||||
end if
|
||||
end for
|
||||
end sub
|
||||
|
||||
sub updateLatestItems(msg)
|
||||
|
|
|
@ -30,6 +30,7 @@ sub loadItems()
|
|||
params["ParentId"] = m.top.itemId
|
||||
params["EnableImageTypes"] = "Primary,Backdrop,Thumb"
|
||||
params["ImageTypeLimit"] = 1
|
||||
params["EnableTotalRecordCount"] = false
|
||||
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
|
@ -53,6 +54,10 @@ sub loadItems()
|
|||
params["SortOrder"] = "Descending"
|
||||
params["ImageTypeLimit"] = 1
|
||||
params["UserId"] = get_setting("active_user")
|
||||
params["EnableRewatching"] = false
|
||||
params["DisableFirstEpisode"] = false
|
||||
params["limit"] = 24
|
||||
params["EnableTotalRecordCount"] = false
|
||||
|
||||
maxDaysInNextUp = get_user_setting("ui.details.maxdaysnextup", "365")
|
||||
if isValid(maxDaysInNextUp)
|
||||
|
@ -64,9 +69,6 @@ sub loadItems()
|
|||
dateCutoff.FromSeconds(dateToday.AsSeconds() - (maxDaysInNextUp * 86400))
|
||||
|
||||
params["NextUpDateCutoff"] = dateCutoff.ToISOString()
|
||||
params["EnableRewatching"] = false
|
||||
params["DisableFirstEpisode"] = false
|
||||
params["limit"] = 24
|
||||
end if
|
||||
end if
|
||||
|
||||
|
@ -88,6 +90,7 @@ sub loadItems()
|
|||
params["SortBy"] = "DatePlayed"
|
||||
params["SortOrder"] = "Descending"
|
||||
params["Filters"] = "IsResumable"
|
||||
params["EnableTotalRecordCount"] = false
|
||||
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
|
@ -109,6 +112,7 @@ sub loadItems()
|
|||
params["Limit"] = 20
|
||||
params["recursive"] = true
|
||||
params["sortby"] = "random"
|
||||
params["EnableTotalRecordCount"] = false
|
||||
|
||||
resp = APIRequest(url, params)
|
||||
data = getJson(resp)
|
||||
|
|
|
@ -243,8 +243,8 @@ function getRelativeDayName(date) as string
|
|||
end if
|
||||
|
||||
' Check for Yesterday
|
||||
todayMidnight = now.AsSeconds() - (now.AsSeconds() MOD 86400)
|
||||
dateMidnight = date.AsSeconds() - (date.AsSeconds() MOD 86400)
|
||||
todayMidnight = now.AsSeconds() - (now.AsSeconds() mod 86400)
|
||||
dateMidnight = date.AsSeconds() - (date.AsSeconds() mod 86400)
|
||||
|
||||
if todayMidnight - dateMidnight = 86400
|
||||
return "yesterday"
|
||||
|
@ -266,8 +266,8 @@ function getDurationStringFromSeconds(seconds) as string
|
|||
minutes = seconds / 60.0
|
||||
|
||||
if minutes > 60
|
||||
hours = (minutes - (minutes MOD 60)) / 60
|
||||
minutes = minutes MOD 60
|
||||
hours = (minutes - (minutes mod 60)) / 60
|
||||
minutes = minutes mod 60
|
||||
end if
|
||||
|
||||
if hours > 0
|
||||
|
|
129
components/manager/QueueManager.brs
Normal file
129
components/manager/QueueManager.brs
Normal file
|
@ -0,0 +1,129 @@
|
|||
sub init()
|
||||
m.queue = []
|
||||
m.position = 0
|
||||
end sub
|
||||
|
||||
'
|
||||
' Clear all content from play queue
|
||||
sub clear()
|
||||
m.queue = []
|
||||
setPosition(0)
|
||||
end sub
|
||||
|
||||
|
||||
'
|
||||
' Delete item from play queue at passed index
|
||||
sub deleteAtIndex(index)
|
||||
m.queue.Delete(index)
|
||||
end sub
|
||||
|
||||
|
||||
'
|
||||
' Return the number of items in the play queue
|
||||
function getCount()
|
||||
return m.queue.count()
|
||||
end function
|
||||
|
||||
|
||||
'
|
||||
' Return the item currently in focus from the play queue
|
||||
function getCurrentItem()
|
||||
return getItemByIndex(m.position)
|
||||
end function
|
||||
|
||||
|
||||
'
|
||||
' Return the item in the passed index from the play queue
|
||||
function getItemByIndex(index)
|
||||
return m.queue[index]
|
||||
end function
|
||||
|
||||
|
||||
'
|
||||
' Returns current playback position within the queue
|
||||
function getPosition()
|
||||
return m.position
|
||||
end function
|
||||
|
||||
|
||||
'
|
||||
' Move queue position back one
|
||||
sub moveBack()
|
||||
m.position--
|
||||
end sub
|
||||
|
||||
|
||||
'
|
||||
' Move queue position ahead one
|
||||
sub moveForward()
|
||||
m.position++
|
||||
end sub
|
||||
|
||||
|
||||
'
|
||||
' Return the current play queue
|
||||
function getQueue()
|
||||
return m.queue
|
||||
end function
|
||||
|
||||
|
||||
'
|
||||
' Return item at end of play queue without removing
|
||||
function peek()
|
||||
return m.queue.peek()
|
||||
end function
|
||||
|
||||
|
||||
'
|
||||
' Play items in queue
|
||||
sub playQueue()
|
||||
nextItem = top()
|
||||
nextItemMediaType = invalid
|
||||
|
||||
if isValid(nextItem?.json?.mediatype) and nextItem.json.mediatype <> ""
|
||||
nextItemMediaType = LCase(nextItem.json.mediatype)
|
||||
else if isValid(nextItem?.type) and nextItem.type <> ""
|
||||
nextItemMediaType = LCase(nextItem.type)
|
||||
end if
|
||||
|
||||
if not isValid(nextItemMediaType) then return
|
||||
|
||||
if nextItemMediaType = "audio"
|
||||
CreateAudioPlayerView()
|
||||
end if
|
||||
end sub
|
||||
|
||||
|
||||
'
|
||||
' Remove item at end of play queue
|
||||
sub pop()
|
||||
m.queue.pop()
|
||||
end sub
|
||||
|
||||
|
||||
'
|
||||
' Push new items to the play queue
|
||||
sub push(newItem)
|
||||
m.queue.push(newItem)
|
||||
end sub
|
||||
|
||||
'
|
||||
' Set the queue position
|
||||
sub setPosition(newPosition)
|
||||
m.position = newPosition
|
||||
end sub
|
||||
|
||||
|
||||
'
|
||||
' Return the fitst item in the play queue
|
||||
function top()
|
||||
return getItemByIndex(0)
|
||||
end function
|
||||
|
||||
|
||||
'
|
||||
' Replace play queue with passed array
|
||||
sub set(items)
|
||||
setPosition(0)
|
||||
m.queue = items
|
||||
end sub
|
24
components/manager/QueueManager.xml
Normal file
24
components/manager/QueueManager.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="QueueManager" extends="Group">
|
||||
<interface>
|
||||
<function name="clear" />
|
||||
<function name="deleteAtIndex" />
|
||||
<function name="getCount" />
|
||||
<function name="getCurrentItem" />
|
||||
<function name="getItemByIndex" />
|
||||
<function name="getPosition" />
|
||||
<function name="getQueue" />
|
||||
<function name="moveBack" />
|
||||
<function name="moveForward" />
|
||||
<function name="peek" />
|
||||
<function name="playQueue" />
|
||||
<function name="pop" />
|
||||
<function name="push" />
|
||||
<function name="set" />
|
||||
<function name="setPosition" />
|
||||
<function name="top" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="QueueManager.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
<script type="text/brightscript" uri="ViewCreator.brs" />
|
||||
</component>
|
6
components/manager/ViewCreator.brs
Normal file
6
components/manager/ViewCreator.brs
Normal file
|
@ -0,0 +1,6 @@
|
|||
' Play Audio
|
||||
sub CreateAudioPlayerView()
|
||||
view = CreateObject("roSGNode", "AudioPlayerView")
|
||||
view.observeField("state", m.port)
|
||||
m.global.sceneManager.callFunc("pushScene", view)
|
||||
end sub
|
|
@ -8,11 +8,8 @@ sub init()
|
|||
setupDataTasks()
|
||||
setupScreenSaver()
|
||||
|
||||
m.currentSongIndex = 0
|
||||
m.buttonsNeedToBeLoaded = true
|
||||
m.shuffleEnabled = false
|
||||
m.loopMode = ""
|
||||
m.shuffleEvent = ""
|
||||
m.buttonCount = m.buttons.getChildCount()
|
||||
m.playReported = false
|
||||
|
||||
|
@ -26,6 +23,9 @@ sub init()
|
|||
' Write screen tracker for screensaver
|
||||
WriteAsciiFile("tmp:/scene.temp", "nowplaying")
|
||||
MoveFile("tmp:/scene.temp", "tmp:/scene")
|
||||
|
||||
loadButtons()
|
||||
pageContentChanged()
|
||||
end sub
|
||||
|
||||
sub onScreensaverTimeoutLoaded()
|
||||
|
@ -62,7 +62,7 @@ sub setupAnimationTasks()
|
|||
m.screenSaverStartAnimation = m.top.FindNode("screenSaverStartAnimation")
|
||||
end sub
|
||||
|
||||
' Creates tasks to gather data needed to renger NowPlaying Scene and play song
|
||||
' Creates tasks to gather data needed to render Scene and play song
|
||||
sub setupDataTasks()
|
||||
' Load meta data
|
||||
m.LoadMetaDataTask = CreateObject("roSGNode", "LoadItemsTask")
|
||||
|
@ -219,18 +219,25 @@ sub audioStateChanged()
|
|||
|
||||
' Song Finished, attempt to move to next song
|
||||
if m.top.audio.state = "finished"
|
||||
' User has enabled single song loop, play current song again
|
||||
if m.loopMode = "one"
|
||||
playAction()
|
||||
return
|
||||
else if m.loopMode = "all"
|
||||
m.currentSongIndex = -1
|
||||
LoadNextSong()
|
||||
return
|
||||
end if
|
||||
|
||||
if m.currentSongIndex < m.top.pageContent.count() - 1
|
||||
if m.global.queueManager.callFunc("getPosition") < m.global.queueManager.callFunc("getCount") - 1
|
||||
' We are not at the end of the song queue, advance to next song
|
||||
LoadNextSong()
|
||||
else
|
||||
' We are at the end of the song queue
|
||||
|
||||
' User has enabled loop for entire song queue, move back to first song
|
||||
if m.loopMode = "all"
|
||||
m.global.queueManager.callFunc("setPosition", -1)
|
||||
LoadNextSong()
|
||||
return
|
||||
end if
|
||||
|
||||
' Return to previous screen
|
||||
m.top.state = "finished"
|
||||
end if
|
||||
|
@ -263,8 +270,8 @@ function previousClicked() as boolean
|
|||
m.top.audio.control = "stop"
|
||||
end if
|
||||
|
||||
if m.currentSongIndex > 0
|
||||
m.currentSongIndex--
|
||||
if m.global.queueManager.callFunc("getPosition") > 0
|
||||
m.global.queueManager.callFunc("moveBack")
|
||||
pageContentChanged()
|
||||
end if
|
||||
|
||||
|
@ -289,7 +296,7 @@ function loopClicked() as boolean
|
|||
end function
|
||||
|
||||
function nextClicked() as boolean
|
||||
if m.currentSongIndex < m.top.pageContent.count() - 1
|
||||
if m.global.queueManager.callFunc("getPosition") < m.global.queueManager.callFunc("getCount") - 1
|
||||
LoadNextSong()
|
||||
end if
|
||||
|
||||
|
@ -302,7 +309,7 @@ end sub
|
|||
|
||||
function findCurrentSongIndex(songList) as integer
|
||||
for i = 0 to songList.count() - 1
|
||||
if songList[i] = m.top.pageContent[m.currentSongIndex]
|
||||
if songList[i].id = m.global.queueManager.callFunc("getCurrentItem").id
|
||||
return i
|
||||
end if
|
||||
end for
|
||||
|
@ -318,10 +325,10 @@ function shuffleClicked() as boolean
|
|||
m.shuffleIndicator.opacity = ".4"
|
||||
m.shuffleIndicator.uri = m.shuffleIndicator.uri.Replace("-on", "-off")
|
||||
|
||||
m.shuffleEvent = "enabled"
|
||||
m.currentSongIndex = findCurrentSongIndex(m.originalSongList)
|
||||
m.top.pageContent = m.originalSongList
|
||||
setFieldTextValue("numberofsongs", "Track " + stri(m.currentSongIndex + 1) + "/" + stri(m.top.pageContent.count()))
|
||||
currentSongIndex = findCurrentSongIndex(m.originalSongList)
|
||||
m.global.queueManager.callFunc("set", m.originalSongList)
|
||||
m.global.queueManager.callFunc("setPosition", currentSongIndex)
|
||||
setFieldTextValue("numberofsongs", "Track " + stri(m.global.queueManager.callFunc("getPosition") + 1) + "/" + stri(m.global.queueManager.callFunc("getCount")))
|
||||
|
||||
return true
|
||||
end if
|
||||
|
@ -329,14 +336,14 @@ function shuffleClicked() as boolean
|
|||
m.shuffleIndicator.opacity = "1"
|
||||
m.shuffleIndicator.uri = m.shuffleIndicator.uri.Replace("-off", "-on")
|
||||
|
||||
m.originalSongList = m.top.pageContent
|
||||
m.originalSongList = m.global.queueManager.callFunc("getQueue")
|
||||
|
||||
songIDArray = m.top.pageContent
|
||||
songIDArray = m.global.queueManager.callFunc("getQueue")
|
||||
|
||||
' Move the currently playing song to the front of the queue
|
||||
temp = m.top.pageContent[0]
|
||||
songIDArray[0] = m.top.pageContent[m.currentSongIndex]
|
||||
songIDArray[m.currentSongIndex] = temp
|
||||
temp = m.global.queueManager.callFunc("top")
|
||||
songIDArray[0] = m.global.queueManager.callFunc("getCurrentItem")
|
||||
songIDArray[m.global.queueManager.callFunc("getPosition")] = temp
|
||||
|
||||
for i = 1 to songIDArray.count() - 1
|
||||
j = Rnd(songIDArray.count() - 1)
|
||||
|
@ -345,10 +352,7 @@ function shuffleClicked() as boolean
|
|||
songIDArray[j] = temp
|
||||
end for
|
||||
|
||||
m.currentSongIndex = 0
|
||||
m.shuffleEvent = "enabled"
|
||||
|
||||
m.top.pageContent = songIDArray
|
||||
m.global.queueManager.callFunc("set", songIDArray)
|
||||
|
||||
return true
|
||||
end function
|
||||
|
@ -360,30 +364,64 @@ sub LoadNextSong()
|
|||
|
||||
' Reset playPosition bar without animation
|
||||
m.playPosition.width = 0
|
||||
m.currentSongIndex++
|
||||
m.global.queueManager.callFunc("moveForward")
|
||||
pageContentChanged()
|
||||
end sub
|
||||
|
||||
' Update values on screen when page content changes
|
||||
sub pageContentChanged()
|
||||
' pageContent Changed due to shuffle event, don't update screen values
|
||||
if m.shuffleEvent = "enabled"
|
||||
m.shuffleEvent = ""
|
||||
return
|
||||
end if
|
||||
|
||||
' Reset buffer bar without animation
|
||||
m.bufferPosition.width = 0
|
||||
|
||||
m.LoadMetaDataTask.itemId = m.top.pageContent[m.currentSongIndex]
|
||||
m.LoadMetaDataTask.observeField("content", "onMetaDataLoaded")
|
||||
m.LoadMetaDataTask.control = "RUN"
|
||||
useMetaTask = false
|
||||
currentItem = m.global.queueManager.callFunc("getCurrentItem")
|
||||
|
||||
m.LoadAudioStreamTask.itemId = m.top.pageContent[m.currentSongIndex]
|
||||
if not isValid(currentItem.RunTimeTicks)
|
||||
useMetaTask = true
|
||||
end if
|
||||
|
||||
if not isValid(currentItem.AlbumArtist)
|
||||
useMetaTask = true
|
||||
end if
|
||||
|
||||
if not isValid(currentItem.name)
|
||||
useMetaTask = true
|
||||
end if
|
||||
|
||||
if not isValid(currentItem.Artists)
|
||||
useMetaTask = true
|
||||
end if
|
||||
|
||||
if useMetaTask
|
||||
m.LoadMetaDataTask.itemId = currentItem.id
|
||||
m.LoadMetaDataTask.observeField("content", "onMetaDataLoaded")
|
||||
m.LoadMetaDataTask.control = "RUN"
|
||||
else
|
||||
if isValid(currentItem.ParentBackdropItemId)
|
||||
setBackdropImage(ImageURL(currentItem.ParentBackdropItemId, "Backdrop", { "maxHeight": "720", "maxWidth": "1280" }))
|
||||
end if
|
||||
|
||||
setPosterImage(ImageURL(currentItem.id, "Primary", { "maxHeight": 500, "maxWidth": 500 }))
|
||||
setScreenTitle(currentItem)
|
||||
setOnScreenTextValues(currentItem)
|
||||
m.songDuration = currentItem.RunTimeTicks / 10000000.0
|
||||
end if
|
||||
|
||||
m.LoadAudioStreamTask.itemId = currentItem.id
|
||||
m.LoadAudioStreamTask.observeField("content", "onAudioStreamLoaded")
|
||||
m.LoadAudioStreamTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
' If we have more and 1 song to play, fade in the next and previous controls
|
||||
sub loadButtons()
|
||||
if m.global.queueManager.callFunc("getCount") > 1
|
||||
m.shuffleIndicator.opacity = ".4"
|
||||
m.loopIndicator.opacity = ".4"
|
||||
m.displayButtonsAnimation.control = "start"
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub onAudioStreamLoaded()
|
||||
data = m.LoadAudioStreamTask.content[0]
|
||||
m.LoadAudioStreamTask.unobserveField("content")
|
||||
|
@ -425,16 +463,6 @@ sub onMetaDataLoaded()
|
|||
setOnScreenTextValues(data.json)
|
||||
|
||||
m.songDuration = data.json.RunTimeTicks / 10000000.0
|
||||
|
||||
' If we have more and 1 song to play, fade in the next and previous controls
|
||||
if m.buttonsNeedToBeLoaded
|
||||
if m.top.pageContent.count() > 1
|
||||
m.shuffleIndicator.opacity = ".4"
|
||||
m.loopIndicator.opacity = ".4"
|
||||
m.displayButtonsAnimation.control = "start"
|
||||
end if
|
||||
m.buttonsNeedToBeLoaded = false
|
||||
end if
|
||||
end if
|
||||
end sub
|
||||
|
||||
|
@ -471,12 +499,12 @@ end sub
|
|||
' Populate on screen text variables
|
||||
sub setOnScreenTextValues(json)
|
||||
if isValid(json)
|
||||
currentSongIndex = m.currentSongIndex
|
||||
currentSongIndex = m.global.queueManager.callFunc("getPosition")
|
||||
|
||||
if m.shuffleEnabled
|
||||
currentSongIndex = findCurrentSongIndex(m.originalSongList)
|
||||
end if
|
||||
setFieldTextValue("numberofsongs", "Track " + stri(currentSongIndex + 1) + "/" + stri(m.top.pageContent.count()))
|
||||
setFieldTextValue("numberofsongs", "Track " + stri(currentSongIndex + 1) + "/" + stri(m.global.queueManager.callFunc("getCount")))
|
||||
setFieldTextValue("artist", json.Artists[0])
|
||||
setFieldTextValue("song", json.name)
|
||||
end if
|
||||
|
@ -511,7 +539,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
else if key = "fastforward"
|
||||
return nextClicked()
|
||||
else if key = "left"
|
||||
if m.top.pageContent.count() = 1 then return false
|
||||
if m.global.queueManager.callFunc("getCount") = 1 then return false
|
||||
|
||||
if m.top.selectedButtonIndex > 0
|
||||
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
|
||||
|
@ -519,7 +547,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
end if
|
||||
return true
|
||||
else if key = "right"
|
||||
if m.top.pageContent.count() = 1 then return false
|
||||
if m.global.queueManager.callFunc("getCount") = 1 then return false
|
||||
|
||||
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
|
||||
if m.top.selectedButtonIndex < m.buttonCount - 1 then m.top.selectedButtonIndex = m.top.selectedButtonIndex + 1
|
||||
|
@ -554,7 +582,7 @@ sub ReportPlayback(state = "update" as string)
|
|||
if m.top.audio.position = invalid then return
|
||||
|
||||
params = {
|
||||
"ItemId": m.top.pageContent[m.currentSongIndex],
|
||||
"ItemId": m.global.queueManager.callFunc("getCurrentItem").id,
|
||||
"PlaySessionId": m.top.audio.content.id,
|
||||
"PositionTicks": int(m.top.audio.position) * 10000000&, 'Ensure a LongInteger is used
|
||||
"IsPaused": (m.top.audio.state = "paused")
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="NowPlaying" extends="JFScreen">
|
||||
<component name="AudioPlayerView" extends="JFScreen">
|
||||
<children>
|
||||
<Poster id="backdrop" opacity=".5" loadDisplayMode="scaleToZoom" width="1920" height="1200" blendColor="#3f3f3f" />
|
||||
<Poster id="shuffleIndicator" width="64" height="64" uri="pkg:/images/icons/shuffleIndicator-off.png" translation="[1150,775]" opacity="0" />
|
||||
|
@ -8,7 +8,7 @@
|
|||
<LayoutGroup id="main_group" layoutDirection="vert" horizAlignment="center" itemSpacings="[15]">
|
||||
<Poster id="albumCover" width="500" height="500" />
|
||||
<Label id="artist" width="900" height="25" horizAlign="center" />
|
||||
<Label id="song" width="900" height="25" horizAlign="center" />
|
||||
<Label id="song" width="900" height="25" horizAlign="center" />
|
||||
<Label id="numberofsongs" width="500" height="25" horizAlign="center" font="font:SmallestSystemFont" color="#999999" />
|
||||
</LayoutGroup>
|
||||
<Rectangle id="seekBar" color="0x00000099" width="500" height="10">
|
||||
|
@ -77,7 +77,7 @@
|
|||
<Vector2DFieldInterpolator id="OneInterp" key="[0.0,1.0]" keyValue="[[450,30],[960,575]]" fieldToInterp="screenSaverAlbumCover.translation" />
|
||||
</Animation>
|
||||
</SequentialAnimation>
|
||||
|
||||
|
||||
<!-- Jellyfin Logo ScreenSaver -->
|
||||
<SequentialAnimation id="BounceAnimation" repeat="true">
|
||||
<Animation id="AnimOne" repeat="false" easeFunction="linear" duration="7.2">
|
||||
|
@ -119,13 +119,12 @@
|
|||
<Poster width="0" height="0" uri="pkg:/images/icons/loopIndicator1-on.png" visible="false" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="pageContent" type="array" onChange="pageContentChanged" />
|
||||
<field id="audio" type="node" />
|
||||
<field id="state" type="string" />
|
||||
<field id="selectedButtonIndex" type="integer" />
|
||||
</interface>
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
|
||||
<script type="text/brightscript" uri="NowPlaying.brs" />
|
||||
<script type="text/brightscript" uri="AudioPlayerView.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
|
||||
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
|
|
@ -13,6 +13,8 @@ sub init()
|
|||
|
||||
m.boolSetting = m.top.findNode("boolSetting")
|
||||
m.integerSetting = m.top.findNode("integerSetting")
|
||||
m.radioSetting = m.top.findNode("radioSetting")
|
||||
|
||||
m.integerSetting.observeField("submit", "onKeyGridSubmit")
|
||||
m.integerSetting.observeField("escape", "onKeyGridEscape")
|
||||
|
||||
|
@ -21,6 +23,7 @@ sub init()
|
|||
m.settingsMenu.observeField("itemSelected", "settingSelected")
|
||||
|
||||
m.boolSetting.observeField("checkedItem", "boolSettingChanged")
|
||||
m.radioSetting.observeField("checkedItem", "radioSettingChanged")
|
||||
|
||||
' Load Configuration Tree
|
||||
m.configTree = GetConfigTree()
|
||||
|
@ -40,7 +43,6 @@ sub onKeyGridEscape()
|
|||
end sub
|
||||
|
||||
sub LoadMenu(configSection)
|
||||
|
||||
if configSection.children = invalid
|
||||
' Load parent menu
|
||||
m.userLocation.pop()
|
||||
|
@ -81,6 +83,7 @@ sub settingFocused()
|
|||
' Hide Settings
|
||||
m.boolSetting.visible = false
|
||||
m.integerSetting.visible = false
|
||||
m.radioSetting.visible = false
|
||||
|
||||
if selectedSetting.type = invalid
|
||||
return
|
||||
|
@ -99,6 +102,26 @@ sub settingFocused()
|
|||
m.integerSetting.text = integerValue
|
||||
end if
|
||||
m.integerSetting.visible = true
|
||||
else if LCase(selectedSetting.type) = "radio"
|
||||
|
||||
selectedValue = get_user_setting(selectedSetting.settingName, selectedSetting.default)
|
||||
|
||||
radioContent = CreateObject("roSGNode", "ContentNode")
|
||||
|
||||
itemIndex = 0
|
||||
for each item in m.userLocation.peek().children[m.settingsMenu.itemFocused].options
|
||||
listItem = radioContent.CreateChild("ContentNode")
|
||||
listItem.title = tr(item.title)
|
||||
listItem.id = item.id
|
||||
if selectedValue = item.id
|
||||
m.radioSetting.checkedItem = itemIndex
|
||||
end if
|
||||
itemIndex++
|
||||
end for
|
||||
|
||||
m.radioSetting.content = radioContent
|
||||
|
||||
m.radioSetting.visible = true
|
||||
else
|
||||
print "Unknown setting type " + selectedSetting.type
|
||||
end if
|
||||
|
@ -117,6 +140,9 @@ sub settingSelected()
|
|||
if selectedItem.type = "integer"
|
||||
m.integerSetting.setFocus(true)
|
||||
end if
|
||||
if (selectedItem.type) = "radio"
|
||||
m.radioSetting.setFocus(true)
|
||||
end if
|
||||
else if selectedItem.children <> invalid and selectedItem.children.Count() > 0 ' Show sub menu
|
||||
LoadMenu(selectedItem)
|
||||
m.settingsMenu.setFocus(true)
|
||||
|
@ -139,9 +165,13 @@ sub boolSettingChanged()
|
|||
else
|
||||
set_user_setting(selectedSetting.settingName, "false")
|
||||
end if
|
||||
|
||||
end sub
|
||||
|
||||
sub radioSettingChanged()
|
||||
if m.radioSetting.focusedChild = invalid then return
|
||||
selectedSetting = m.userLocation.peek().children[m.settingsMenu.itemFocused]
|
||||
set_user_setting(selectedSetting.settingName, m.radioSetting.content.getChild(m.radioSetting.checkedItem).id)
|
||||
end sub
|
||||
|
||||
function onKeyEvent(key as string, press as boolean) as boolean
|
||||
if not press then return false
|
||||
|
@ -152,6 +182,9 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
else if (key = "back" or key = "left") and m.settingDetail.focusedChild <> invalid
|
||||
m.settingsMenu.setFocus(true)
|
||||
return true
|
||||
else if (key = "back" or key = "left") and m.radioSetting.hasFocus()
|
||||
m.settingsMenu.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
||||
if key = "right"
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
</RadioButtonList>
|
||||
</LayoutGroup>
|
||||
|
||||
<RadioButtonList id="radioSetting" translation="[900, 450]" inheritParentTransform="false" vertFocusAnimationStyle="floatingFocus" />
|
||||
<intkeyboard_integerKeyboard translation="[900, 520]" id="integerSetting" maxLength="3" domain="numeric" visible="false" />
|
||||
|
||||
</children>
|
||||
|
|
|
@ -6,6 +6,8 @@ sub init()
|
|||
m.Random = m.top.findNode("Random")
|
||||
m.tvEpisodeRow = m.top.findNode("tvEpisodeRow")
|
||||
|
||||
m.unplayedCount = m.top.findNode("unplayedCount")
|
||||
m.unplayedEpisodeCount = m.top.findNode("unplayedEpisodeCount")
|
||||
|
||||
m.rows.observeField("doneLoading", "updateSeason")
|
||||
end sub
|
||||
|
@ -15,6 +17,13 @@ sub setSeasonLoading()
|
|||
end sub
|
||||
|
||||
sub updateSeason()
|
||||
if m.top.seasonData?.UserData?.UnplayedItemCount <> invalid
|
||||
if m.top.seasonData.UserData.UnplayedItemCount > 0
|
||||
m.unplayedCount.visible = true
|
||||
m.unplayedEpisodeCount.text = m.top.seasonData.UserData.UnplayedItemCount
|
||||
end if
|
||||
end if
|
||||
|
||||
imgParams = { "maxHeight": 450, "maxWidth": 300 }
|
||||
m.poster.uri = ImageURL(m.top.seasonData.Id, "Primary", imgParams)
|
||||
m.Random.visible = true
|
||||
|
@ -54,8 +63,10 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
end if
|
||||
|
||||
if press and key = "play" or proceed = true
|
||||
m.top.lastFocus = focusedChild
|
||||
itemToPlay = focusedChild.content.getChild(focusedChild.rowItemFocused[0]).getChild(0)
|
||||
if itemToPlay <> invalid and itemToPlay.id <> ""
|
||||
itemToPlay.type = "Episode"
|
||||
m.top.quickPlayNode = itemToPlay
|
||||
end if
|
||||
handled = true
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="TVEpisodes" extends="JFGroup">
|
||||
<children>
|
||||
<Poster id="seasonPoster" width="300" height="450" translation="[95,175]" />
|
||||
<Poster id="seasonPoster" width="300" height="450" translation="[95,175]">
|
||||
<Rectangle id="unplayedCount" visible="false" width="90" height="60" color="#00a4dcFF" translation="[210, 0]">
|
||||
<Label id="unplayedEpisodeCount" width="90" height="60" font="font:SmallestBoldSystemFont" horizAlign="center" vertAlign="center" />
|
||||
</Rectangle>
|
||||
</Poster>
|
||||
<JFButton id="Random" text="Play Random" translation="[90, 640]" visible="false"></JFButton>
|
||||
<TVEpisodeRowWithOptions id="picker" visible="true" />
|
||||
</children>
|
||||
|
|
|
@ -8,6 +8,11 @@ sub init()
|
|||
|
||||
m.rating = m.top.findnode("rating")
|
||||
m.infoBar = m.top.findnode("infoBar")
|
||||
m.progressBackground = m.top.findNode("progressBackground")
|
||||
m.progressBar = m.top.findnode("progressBar")
|
||||
m.playedIndicator = m.top.findNode("playedIndicator")
|
||||
m.checkmark = m.top.findNode("checkmark")
|
||||
m.checkmark.font.size = 35
|
||||
end sub
|
||||
|
||||
sub itemContentChanged()
|
||||
|
@ -52,11 +57,30 @@ sub itemContentChanged()
|
|||
end if
|
||||
end if
|
||||
|
||||
if isValid(itemData.communityRating)
|
||||
m.top.findNode("star").visible = true
|
||||
m.top.findNode("communityRating").text = str(int(itemData.communityRating * 10) / 10)
|
||||
if get_user_setting("ui.tvshows.disableCommunityRating") = "false"
|
||||
if isValid(itemData.communityRating)
|
||||
m.top.findNode("star").visible = true
|
||||
m.top.findNode("communityRating").text = str(int(itemData.communityRating * 10) / 10)
|
||||
else
|
||||
m.top.findNode("star").visible = false
|
||||
end if
|
||||
else
|
||||
m.top.findNode("star").visible = false
|
||||
m.rating.visible = false
|
||||
m.infoBar.itemSpacings = [20, -25, 20, 20]
|
||||
end if
|
||||
|
||||
' Add checkmark in corner (if applicable)
|
||||
if isValid(itemData?.UserData?.Played) and itemData.UserData.Played = true
|
||||
m.playedIndicator.visible = true
|
||||
end if
|
||||
|
||||
' Add progress bar on bottom (if applicable)
|
||||
if isValid(itemData?.UserData?.PlayedPercentage) and itemData?.UserData?.PlayedPercentage > 0
|
||||
m.progressBackground.width = m.poster.width
|
||||
m.progressBackground.visible = true
|
||||
progressWidthInPixels = int(m.progressBackground.width * itemData.UserData.PlayedPercentage / 100)
|
||||
m.progressBar.width = progressWidthInPixels
|
||||
m.progressBar.visible = true
|
||||
end if
|
||||
|
||||
videoIdx = invalid
|
||||
|
|
|
@ -2,8 +2,15 @@
|
|||
<component name="TVListDetails" extends="Group">
|
||||
<children>
|
||||
<LayoutGroup id="toplevel" layoutDirection="vert" itemSpacings="[40]">
|
||||
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[30]">
|
||||
<Poster id="poster" width="350" height="300" />
|
||||
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[30]">
|
||||
<Poster id="poster" width="350" height="300" loadDisplayMode="scaleToZoom">
|
||||
<Rectangle id="playedIndicator" color="#00a4dcFF" width="60" height="46" visible="false" translation="[290, 0]">
|
||||
<Label id="checkmark" width="60" height="42" font="font:SmallestBoldSystemFont" horizAlign="center" vertAlign="bottom" text="✓"/>
|
||||
</Rectangle>
|
||||
<Rectangle id="progressBackground" visible="false" color="0x00000098" width="350" height="16" translation="[0,286]">
|
||||
<Rectangle id="progressBar" color="#00a4dcFF" width="0" height="16" visible="false"/>
|
||||
</Rectangle>
|
||||
</Poster>
|
||||
<LayoutGroup id="text" layoutDirection="vert" itemSpacings="[15]">
|
||||
<!-- Using poster of 1 length to get spacing. Not successful with adding translation to title -->
|
||||
<Poster id="null" height="1" />
|
||||
|
|
|
@ -3,6 +3,8 @@ sub init()
|
|||
main = m.top.findNode("toplevel")
|
||||
main.translation = [96, 175]
|
||||
m.extrasSlider = m.top.findNode("tvSeasonExtras")
|
||||
m.unplayedCount = m.top.findNode("unplayedCount")
|
||||
m.unplayedEpisodeCount = m.top.findNode("unplayedEpisodeCount")
|
||||
'm.extrasSlider.translation = [30,1014]
|
||||
m.extrasSlider.visible = true
|
||||
end sub
|
||||
|
@ -13,6 +15,13 @@ sub itemContentChanged()
|
|||
item = m.top.itemContent
|
||||
itemData = item.json
|
||||
|
||||
if itemData?.UserData?.UnplayedItemCount <> invalid
|
||||
if itemData.UserData.UnplayedItemCount > 0
|
||||
m.unplayedCount.visible = true
|
||||
m.unplayedEpisodeCount.text = itemData.UserData.UnplayedItemCount
|
||||
end if
|
||||
end if
|
||||
|
||||
m.top.findNode("tvshowPoster").uri = m.top.itemContent.posterURL
|
||||
|
||||
' Handle all "As Is" fields
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<component name="TVShowDetails" extends="JFGroup">
|
||||
<children>
|
||||
<LayoutGroup id="toplevel" layoutDirection="vert" itemSpacings="[-10]" >
|
||||
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[15]" >
|
||||
<Poster id="tvshowPoster" width="300" height="450" />
|
||||
<LayoutGroup id="toplevel" layoutDirection="vert" itemSpacings="[-10]">
|
||||
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[15]">
|
||||
<Poster id="tvshowPoster" width="300" height="450">
|
||||
<Rectangle id="unplayedCount" visible="false" width="90" height="60" color="#00a4dcFF" translation="[210, 0]">
|
||||
<Label id="unplayedEpisodeCount" width="90" height="60" font="font:SmallestBoldSystemFont" horizAlign="center" vertAlign="center" />
|
||||
</Rectangle>
|
||||
</Poster>
|
||||
<LayoutGroup layoutDirection="vert" itemSpacings="[15]">
|
||||
<LayoutGroup layoutDirection="horiz" itemSpacings="[150]">
|
||||
<Label id="releaseYear" />
|
||||
|
|
BIN
images/icons/musicFolder.png
Normal file
BIN
images/icons/musicFolder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
|
@ -3656,5 +3656,737 @@
|
|||
<source>Save Credentials?</source>
|
||||
<translation>Uložit přihlašovací údaje?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to find any albums or songs belonging to this artist</source>
|
||||
<translation>K tomuto interpretovi nebyly nalezeny žádné skladby ani alba</translation>
|
||||
<extracomment>Popup message when we find no audio data for an artist</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Text Subtitles Only</source>
|
||||
<translation>Pouze textové titulky</translation>
|
||||
<extracomment>Name of a setting - should we hide subtitles that might transcode</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only display text subtitles to minimize transcoding.</source>
|
||||
<translation>Zobrazit pouze textové titulky pro minimalizaci překódování.</translation>
|
||||
<extracomment>Description of a setting - should we hide subtitles that might transcode</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Aired</source>
|
||||
<translation>Vysíláno</translation>
|
||||
<extracomment>Aired date label</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Audio Channels</source>
|
||||
<translation>Audio kanály</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Level</source>
|
||||
<translation>Úroveň</translation>
|
||||
<extracomment>Video profile level</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Bit Rate</source>
|
||||
<translation>Datový tok</translation>
|
||||
<extracomment>Video streaming bit rate</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Název</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Hodnocení IMDb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Datum přehrání</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Délka</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Skončilo v</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Getting Playback Information</source>
|
||||
<translation>Nepodařilo se získat informace o přehrávání</translation>
|
||||
<extracomment>Dialog Title: Received error from server when trying to get information about the selected item for playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to season</source>
|
||||
<translation>Přejít k sérii</translation>
|
||||
<extracomment>Continue Watching Popup Menu - Navigate to the Season Page</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>You can search for Titles, People, Live TV Channels and more</source>
|
||||
<translation>Můžete hledat názvy, osoby, kanály živého vysílání a mnohem více</translation>
|
||||
<extracomment>Help text in search results</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Quick Connect</source>
|
||||
<translation>Rychle připojit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Here is your Quick Connect code:</source>
|
||||
<translation>Zde je Váš kód pro rychlé připojení:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(Dialog will close automatically)</source>
|
||||
<translation>(Dialog se automaticky zavře)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>all</source>
|
||||
<translation>vše</translation>
|
||||
<extracomment>all will reset the searchTerm so all data will be availible</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow On</source>
|
||||
<translation>Slideshow zapnuto</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Random On</source>
|
||||
<translation>Náhodné zapnout</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>Pomocí tlačítka pro přehrávání se můžete pomalu pohybovat k první položce ve složce. (Pokud je vypnuto, složka se znovu hned nastaví na první položku).</translation>
|
||||
<extracomment>Description for option in Setting Screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hides tagline text on details pages.</source>
|
||||
<translation>Skrýt text sloganu na stránce s podrobnostmi.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Blur Unwatched Episodes</source>
|
||||
<translation>Rozmazat neshlédnuté epizody</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use generated splashscreen image as Jellyfin's home background. Jellyfin will need to be closed and reopened for change to take effect.</source>
|
||||
<translation>Použijte vygenerovaný obrázek úvodní obrazovky jako pozadí domovské stránky Jellyfin. Aby se změna projevila, bude třeba Jellyfin zavřít a znovu otevřít.</translation>
|
||||
<extracomment>Description for option in 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>Režim Cinema přináší zážitek z kina přímo do vašeho obývacího pokoje díky možnosti přehrávání vlastních úvodů před hlavním titulem.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hides all clocks in Jellyfin. Jellyfin will need to be closed and reopened for change to take effect.</source>
|
||||
<translation>Skrýt všechny hodiny v Jellyfinu. Aby se změna projevila, je potřeba zavřít a znovu otevřít Jellyfin.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter the server name or IP address</source>
|
||||
<translation>Zadejte název serveru nebo IP adresu</translation>
|
||||
<extracomment>Title of KeyboardDialog when manually entering a server URL</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>Tato %1 neobsahuje žádné položky</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Datum přidání</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Rodičovské hodnocení</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Pohled</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Born</source>
|
||||
<translation>Narozen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Died</source>
|
||||
<translation>Úmrtí</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cast & Crew</source>
|
||||
<translation>Obsazení & štáb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Úterý</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item. Server did not provide required transcoding data.</source>
|
||||
<translation>Při přehrávání této položky došlo k chybě. Server neposkytl požadovaná data pro překódování.</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>** EXPERIMENTAL** Support Direct Play of AV1 content if this Roku device supports it.</source>
|
||||
<translation>**EXPERIMENTÁLNÍ** Podpora přímého přehrávání obsahu AV1, pokud to toto zařízení Roku podporuje.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>direct</source>
|
||||
<translation>přímý</translation>
|
||||
</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>Vždy zobrazovat názvy pod obrázky plakátů. (Pokud je zakázáno, název se zobrazí pouze pod zvýrazněnou položkou).</translation>
|
||||
<extracomment>Description for option in Setting Screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide Clock</source>
|
||||
<translation>Skrýt hodiny</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.</source>
|
||||
<translation>Dříve než selže překódování pokusit se o přímé přehrání H.264 média s nepodporovanými úrovněmi profilu.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Uložit přihlašovací údaje?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Datum vydání</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for HEVC media with unsupported profile levels before falling back to trancoding if it fails.</source>
|
||||
<translation>Dříve než selže překódování pokusit se o přímé přehrání HEVC média s nepodporovanými úrovněmi profilu.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options for Home Page.</source>
|
||||
<translation>Možností domovské stránky.</translation>
|
||||
<extracomment>Description for Home Page user settings.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.</source>
|
||||
<translation>Nastavení maximálního počtu dní, pro setrvání pořadu v seznamu "Další díly", aniž by byl zhlédnut.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Off</source>
|
||||
<translation>Slideshow vypnuto</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.</source>
|
||||
<translation>Podpora přímého přehrávání obsahu MPEG-4. Toto může být nutné deaktivovat pro přehrávání videí kódovaných v DIVX.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>MPEG-4 Support</source>
|
||||
<translation>Podpora MPEG-4</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reason</source>
|
||||
<translation>Důvod</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transcoding Information</source>
|
||||
<translation>Informace o překódování</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cinema Mode</source>
|
||||
<translation>Režim Cinema</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Splashscreen as Home Background</source>
|
||||
<translation>Použít Splashscreen jako pozadí domovské stránky</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Honocení kritiků</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>Více podobných</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Record</source>
|
||||
<translation>Nahrávat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Playback</source>
|
||||
<translation>Přehrávání</translation>
|
||||
<extracomment>Title for Playback section in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>User Interface</source>
|
||||
<translation>Uživatelské prostředí</translation>
|
||||
<extracomment>Title for User Interface section in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enabled</source>
|
||||
<translation>Povoleno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Studios</source>
|
||||
<translation>Studia</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Age</source>
|
||||
<translation>Věk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>Nyní</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Smazat uložené</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Networks</source>
|
||||
<translation>Sítě</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Počet přehrání</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Press 'OK' to Close</source>
|
||||
<translation>Pro zavření stiskni 'OK'</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special Features</source>
|
||||
<translation>Speciální funkce</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional Parts</source>
|
||||
<translation>Další části</translation>
|
||||
<extracomment>Additional parts of a video</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies</source>
|
||||
<translation>FIlmy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Začalo</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Začíná v</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Začíná</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>Channels</source>
|
||||
<translation>Kanály</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Programový průvodce</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Record Series</source>
|
||||
<translation>Nahrávat řady</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>Nenalezeno</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown</source>
|
||||
<translation>Neznámý</translation>
|
||||
<extracomment>Title for a cast member for which we have no information for</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pick a Jellyfin server from the local network</source>
|
||||
<translation>Vyberte server Jellyfin z místní sítě:</translation>
|
||||
<extracomment>Instructions on initial app launch when the user is asked to pick a server from a list</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>MPEG-2 Support</source>
|
||||
<translation>Podpora MPEG-2</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>Podpora přímého přehrávání obsahu MPEG-2 (např. živé TV). To zabrání překódování obsahu MPEG-2, ale využívá podstatně větší šířku pásma.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>AV1 Support</source>
|
||||
<translation>Podpora AV1</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media Grid</source>
|
||||
<translation>Mřížka s médii</translation>
|
||||
<extracomment>UI -> Media Grid section in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Item Titles</source>
|
||||
<translation>Názvy položek</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Title in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media Grid options.</source>
|
||||
<translation>Volby pro mřížku s médii.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Item Count</source>
|
||||
<translation>Počet položek</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Count in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show item count in the library and index of selected item.</source>
|
||||
<translation>Zobrazit počet položek v knihovně a ukazatel označených položek.</translation>
|
||||
<extracomment>Description for option in Setting Screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set Watched</source>
|
||||
<translation>Označit jako shlédnuté</translation>
|
||||
<extracomment>Button Text - When pressed, marks item as Warched</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set Favorite</source>
|
||||
<translation>Nastavit jako oblíbené</translation>
|
||||
<extracomment>Button Text - When pressed, sets item as Favorite</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to episode</source>
|
||||
<translation>Přejít k epizodě</translation>
|
||||
<extracomment>Continue Watching Popup Menu - Navigate to the Episode Detail Page</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use voice remote to search</source>
|
||||
<translation>K hledání použít hlasové ovládání</translation>
|
||||
<extracomment>Help text in search voice text box</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search now</source>
|
||||
<translation>Hledat teď</translation>
|
||||
<extracomment>Help text in search Box</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error authenticating via Quick Connect.</source>
|
||||
<translation>Při ověřování přes rychlé připojení se vyskytla chyba.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Shows</source>
|
||||
<translation>Pořady</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Return to Top</source>
|
||||
<translation>Návrat nahoru</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Title in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options for Details pages.</source>
|
||||
<translation>Možnosti pro stránky s podrobnostmi.</translation>
|
||||
<extracomment>Description for Details page user settings.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide Taglines</source>
|
||||
<translation>Skrýt slogany</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options for TV Shows.</source>
|
||||
<translation>Předvolby pro TV pořady.</translation>
|
||||
<extracomment>Description for TV Shows user settings.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>If enabled, images of unwatched episodes will be blurred.</source>
|
||||
<translation>Pokud je povoleno, obrázky nezobrazených epizod budou zobrazeny rozmazaně.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Screensaver</source>
|
||||
<translation>Spořič obrazovky</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options for Jellyfin's screensaver.</source>
|
||||
<translation>Možnosti spořiče obrazovky Jellyfin.</translation>
|
||||
<extracomment>Description for Screensaver user settings.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Splashscreen as Screensaver Background</source>
|
||||
<translation>Použít Splashscreen jako pozadí spořiče obrazovky</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use generated splashscreen image as Jellyfin's screensaver background. Jellyfin will need to be closed and reopened for change to take effect.</source>
|
||||
<translation>Použijte vygenerovaný obrázek úvodní obrazovky jako pozadí spořiče obrazovky Jellyfin. Aby se změna projevila, bude třeba Jellyfin zavřít a znovu otevřít.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Design Elements</source>
|
||||
<translation>Prvky vzhledu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options that alter the design of Jellyfin.</source>
|
||||
<translation>Možnosti, které mění vzhled Jellyfin.</translation>
|
||||
<extracomment>Description for Design Elements user settings.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Next episode</source>
|
||||
<translation>Další epizoda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Play Trailer</source>
|
||||
<translation>Přehrát trailer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Direct Play H.264 Unsupported Profile Levels</source>
|
||||
<translation>Přímé přehrávání nepodporovaných úrovní profilu H.264</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Direct Play HEVC Unsupported Profile Levels</source>
|
||||
<translation>Přímé přehrání nepodporovaných úrovní profilu HEVC</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settings relating to playback and supported codec and media types.</source>
|
||||
<translation>Nastavení, která se týkají přehrávání, podporovanému kodeku a typům médií.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settings relating to how the application looks.</source>
|
||||
<translation>Nastavení související se vzhledem aplikace.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Home Page</source>
|
||||
<translation>Domovská stránka</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Max Days Next Up</source>
|
||||
<translation>Maximum dní pro další díly</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Total Bitrate</source>
|
||||
<translation>Celkový datový tok</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Playback Information</source>
|
||||
<translation>Informace o přehrávání</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Audio Codec</source>
|
||||
<translation>Audio kodek</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stream Information</source>
|
||||
<translation>Informace o streamu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec</source>
|
||||
<translation>Kodek</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec Tag</source>
|
||||
<translation>Tag kodeku</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Container</source>
|
||||
<translation>Kontejner</translation>
|
||||
<extracomment>Video streaming container</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Size</source>
|
||||
<translation>Velikost</translation>
|
||||
<extracomment>Video size</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Video range type</source>
|
||||
<translation>Typ rozsahu videa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pixel format</source>
|
||||
<translation>Formát pixelu</translation>
|
||||
<extracomment>Video pixel format</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>WxH</source>
|
||||
<translation>ŠxV</translation>
|
||||
<extracomment>Video width x height</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Resumed</source>
|
||||
<translation>Slideshow obnoveno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Paused</source>
|
||||
<translation>Slideshow pozastaveno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Random Off</source>
|
||||
<translation>Náhodné vypnout</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>%1 of %2</source>
|
||||
<translation>%1 z %2</translation>
|
||||
<extracomment>Item position and count. %1 = current item. %2 = total number of items</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to series</source>
|
||||
<translation>Přejít k seriálům</translation>
|
||||
<extracomment>Continue Watching Popup Menu - Navigate to the Series Detail Page</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Details Page</source>
|
||||
<translation>Stránka s podrobnostmi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Video Codec</source>
|
||||
<translation>Video kodek</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Version</source>
|
||||
<translation>Verze</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>...or enter server URL manually:</source>
|
||||
<translation>…nebo zadejte URL serveru ručně:</translation>
|
||||
<extracomment>Instructions on initial app launch when the user is asked to manually enter a server URL</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>Požadovaný obsah na serveru neexistuje</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Close</source>
|
||||
<translation>Zavřít</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 options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtr</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Čtvrtek</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Pátek</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>Živě</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Skončilo v</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Opakovat</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Připojování k serveru</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disabled</source>
|
||||
<translation>Zakázáno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel Series Recording</source>
|
||||
<translation>Zrušit nahrávání sérií</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel Recording</source>
|
||||
<translation>Zrušit nahrávání</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Channel</source>
|
||||
<translation>Zobrazit kanál</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Neděle</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Začátek v</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>Sobota</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Středa</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Pondělí</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Shows</source>
|
||||
<translation>TV pořady</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>dnes</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>včera</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>zítra</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.</source>
|
||||
<translation>Podpora přímého přehrávání obsahu MPEG-4. Toto může být nutné deaktivovat pro přehrávání videí kódovaných v DIVX.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>AV1</source>
|
||||
<translation>AV1</translation>
|
||||
<extracomment>Name of a setting - should we try to direct play experimental av1 codec</extracomment>
|
||||
</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>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2188,5 +2188,832 @@
|
|||
<source>Cancel Series Recording</source>
|
||||
<translation>Cancel Series Recording</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Save Credentials?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Delete Saved</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>On Now</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>Cast & Crew</source>
|
||||
<translation>Cast & Crew</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>More Like This</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Delete Saved</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>More Like This</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cast & Crew</source>
|
||||
<translation>Cast & Crew</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Age</source>
|
||||
<translation>Age</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional Parts</source>
|
||||
<translation>Additional Parts</translation>
|
||||
<extracomment>Additional parts of a video</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Died</source>
|
||||
<translation>Died</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Born</source>
|
||||
<translation>Born</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Save Credentials?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>On Now</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Press 'OK' to Close</source>
|
||||
<translation>Press 'OK' to Close</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special Features</source>
|
||||
<translation>Special Features</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Delete Saved</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special Features</source>
|
||||
<translation>Special Features</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>More Like This</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Age</source>
|
||||
<translation>Age</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional Parts</source>
|
||||
<translation>Additional Parts</translation>
|
||||
<extracomment>Additional parts of a video</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Died</source>
|
||||
<translation>Died</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Born</source>
|
||||
<translation>Born</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Save Credentials?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Press 'OK' to Close</source>
|
||||
<translation>Press 'OK' to Close</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>On Now</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cast & Crew</source>
|
||||
<translation>Cast & Crew</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Delete Saved</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>On Now</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>More Like This</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Age</source>
|
||||
<translation>Age</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional Parts</source>
|
||||
<translation>Additional Parts</translation>
|
||||
<extracomment>Additional parts of a video</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Died</source>
|
||||
<translation>Died</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Born</source>
|
||||
<translation>Born</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special Features</source>
|
||||
<translation>Special Features</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Save Credentials?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Press 'OK' to Close</source>
|
||||
<translation>Press 'OK' to Close</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cast & Crew</source>
|
||||
<translation>Cast & Crew</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Delete Saved</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>On Now</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>Save Credentials?</source>
|
||||
<translation>Save Credentials?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cast & Crew</source>
|
||||
<translation>Cast & Crew</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional Parts</source>
|
||||
<translation>Additional Parts</translation>
|
||||
<extracomment>Additional parts of a video</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>More Like This</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special Features</source>
|
||||
<translation>Special Features</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Press 'OK' to Close</source>
|
||||
<translation>Press 'OK' to Close</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Shows</source>
|
||||
<translation>TV Shows</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Channel</source>
|
||||
<translation>View Channel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Record</source>
|
||||
<translation>Record</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel Recording</source>
|
||||
<translation>Cancel Recording</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Record Series</source>
|
||||
<translation>Record Series</translation>
|
||||
</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>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>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>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>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-4</source>
|
||||
<translation>MPEG-4</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.</source>
|
||||
<translation>Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>AV1</source>
|
||||
<translation>AV1</translation>
|
||||
<extracomment>Name of a setting - should we try to direct play experimental av1 codec</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>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>If enabled, selecting a TV series with only one season will go straight to the episode list rather than the show details and season list.</source>
|
||||
<translation>If enabled, selecting a TV series with only one season will go straight to the episode list rather than the show details and season list.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</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>Options for Jellyfin's screensaver.</source>
|
||||
<translation>Options for Jellyfin's screensaver.</translation>
|
||||
<extracomment>Description for Screensaver user settings.</extracomment>
|
||||
</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>Use generated splashscreen image as Jellyfin's screensaver background. Jellyfin will need to be closed and reopened for change to take effect.</source>
|
||||
<translation>Use generated splashscreen image as Jellyfin's screensaver background. Jellyfin will need to be closed and reopened for change to take effect.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use generated splashscreen image as Jellyfin's home background. Jellyfin will need to be closed and reopened for change to take effect.</source>
|
||||
<translation>Use generated splashscreen image as Jellyfin'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>Hide Clock</source>
|
||||
<translation>Hide Clock</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</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>Options for Home Page.</source>
|
||||
<translation>Options for Home Page.</translation>
|
||||
<extracomment>Description for Home Page user settings.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>direct</source>
|
||||
<translation>direct</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Details Page</source>
|
||||
<translation>Details Page</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Video Codec</source>
|
||||
<translation>Video Codec</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Level</source>
|
||||
<translation>Level</translation>
|
||||
<extracomment>Video profile level</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Size</source>
|
||||
<translation>Size</translation>
|
||||
<extracomment>Video size</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>WxH</source>
|
||||
<translation>WxH</translation>
|
||||
<extracomment>Video width x height</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec Tag</source>
|
||||
<translation>Codec Tag</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Presentation)</source>
|
||||
<translation>Movies (Presentation)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Grid)</source>
|
||||
<translation>Movies (Grid)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel Series Recording</source>
|
||||
<translation>Cancel Series Recording</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Close</source>
|
||||
<translation>Close</translation>
|
||||
</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>...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>Media Grid</source>
|
||||
<translation>Media Grid</translation>
|
||||
<extracomment>UI -> Media Grid section in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media Grid options.</source>
|
||||
<translation>Media Grid options.</translation>
|
||||
</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>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>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>Quick Connect</source>
|
||||
<translation>Quick Connect</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(Dialog will close automatically)</source>
|
||||
<translation>(Dialogue will close automatically)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Return to Top</source>
|
||||
<translation>Return to Top</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Title in user setting screen.</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>Audio Codec</source>
|
||||
<translation>Audio Codec</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Total Bitrate</source>
|
||||
<translation>Total Bitrate</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Bit Rate</source>
|
||||
<translation>Bit Rate</translation>
|
||||
<extracomment>Video streaming bit rate</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pixel format</source>
|
||||
<translation>Pixel format</translation>
|
||||
<extracomment>Video pixel format</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to find any albums or songs belonging to this artist</source>
|
||||
<translation>Unable to find any albums or songs belonging to this artist</translation>
|
||||
<extracomment>Popup message when we find no audio data for an artist</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies</source>
|
||||
<translation>Films</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Version</source>
|
||||
<translation>Version</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Playback</source>
|
||||
<translation>Playback</translation>
|
||||
<extracomment>Title for Playback 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>Item Count</source>
|
||||
<translation>Item Count</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Count in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set Watched</source>
|
||||
<translation>Set Watched</translation>
|
||||
<extracomment>Button Text - When pressed, marks item as Warched</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Here is your Quick Connect code:</source>
|
||||
<translation>Here is your Quick Connect code:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Networks</source>
|
||||
<translation>Networks</translation>
|
||||
</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>Play Trailer</source>
|
||||
<translation>Play Trailer</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>Home Page</source>
|
||||
<translation>Home Page</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transcoding Information</source>
|
||||
<translation>Transcoding Information</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reason</source>
|
||||
<translation>Reason</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Audio Channels</source>
|
||||
<translation>Audio Channels</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec Support</source>
|
||||
<translation>Codec Support</translation>
|
||||
<extracomment>Settings Menu - Title for settings group related to codec support</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>MPEG-2</source>
|
||||
<translation>MPEG-2</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable or disable Direct Play for optional codecs</source>
|
||||
<translation>Enable or disable Direct Play for optional codecs</translation>
|
||||
<extracomment>Settings Menu - Title for settings group related to codec support</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disabled</source>
|
||||
<translation>Disabled</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>** EXPERIMENTAL** Support Direct Play of AV1 content if this Roku device supports it.</source>
|
||||
<translation>** EXPERIMENTAL** Support Direct Play of AV1 content if this Roku device supports it.</translation>
|
||||
<extracomment>Description of a setting - should we try to direct play experimental av1 codec</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Item Titles</source>
|
||||
<translation>Item Titles</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Title in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set Favorite</source>
|
||||
<translation>Set Favourite</translation>
|
||||
<extracomment>Button Text - When pressed, sets item as Favorite</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>Go to season</source>
|
||||
<translation>Go to season</translation>
|
||||
<extracomment>Continue Watching Popup Menu - Navigate to the Season Page</extracomment>
|
||||
</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>There was an error authenticating via Quick Connect.</source>
|
||||
<translation>There was an error authenticating via Quick Connect.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Studios</source>
|
||||
<translation>Studios</translation>
|
||||
</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>Hide Taglines</source>
|
||||
<translation>Hide Taglines</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hides tagline text on details pages.</source>
|
||||
<translation>Hides tagline text on details pages.</translation>
|
||||
</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>Skip Details for Single Seasons</source>
|
||||
<translation>Skip Details for Single Seasons</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Blur Unwatched Episodes</source>
|
||||
<translation>Blur Unwatched Episodes</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 cinema 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>Settings relating to how the application looks.</source>
|
||||
<translation>Settings relating to how the application looks.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stream Information</source>
|
||||
<translation>Stream Information</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec</source>
|
||||
<translation>Codec</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Container</source>
|
||||
<translation>Container</translation>
|
||||
<extracomment>Video streaming container</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Video range type</source>
|
||||
<translation>Video range type</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cinema Mode</source>
|
||||
<translation>Cinema Mode</translation>
|
||||
<extracomment>Settings Menu - Title for option</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>Design Elements</source>
|
||||
<translation>Design Elements</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Screensaver</source>
|
||||
<translation>Screensaver</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search now</source>
|
||||
<translation>Search now</translation>
|
||||
<extracomment>Help text in search 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>Shows</source>
|
||||
<translation>Shows</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enabled</source>
|
||||
<translation>Enabled</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Text Subtitles Only</source>
|
||||
<translation>Text Subtitles Only</translation>
|
||||
<extracomment>Name of a setting - should we hide subtitles that might transcode</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.</source>
|
||||
<translation type="unfinished">Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Next episode</source>
|
||||
<translation>Next episode</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HEVC</source>
|
||||
<translation>HEVC</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>H.264</source>
|
||||
<translation type="unfinished">H.264</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.</source>
|
||||
<translation>Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for HEVC media with unsupported profile levels before falling back to trancoding if it fails.</source>
|
||||
<translation>Attempt Direct Play for HEVC media with unsupported profile levels before falling back to transcoding if it fails.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only display text subtitles to minimize transcoding.</source>
|
||||
<translation>Only display text subtitles to minimise transcoding.</translation>
|
||||
<extracomment>Description of a setting - should we hide subtitles that might transcode</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Aired</source>
|
||||
<translation>Aired</translation>
|
||||
<extracomment>Aired date label</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Off</source>
|
||||
<translation>Slideshow Off</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show What's New Popup</source>
|
||||
<translation>Show What's New Popup</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show What's New popup when Jellyfin is updated to a new version.</source>
|
||||
<translation>Show What's New popup when Jellyfin is updated to a new version.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unplayed</source>
|
||||
<translation>Unplayed</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Resumable</source>
|
||||
<translation>Resumable</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow On</source>
|
||||
<translation>Slideshow On</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Resumed</source>
|
||||
<translation>Slideshow Resumed</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Random On</source>
|
||||
<translation>Random On</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Random Off</source>
|
||||
<translation>Random Off</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>all</source>
|
||||
<translation>all</translation>
|
||||
<extracomment>all will reset the searchTerm so all data will be availible</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Paused</source>
|
||||
<translation>Slideshow Paused</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>MPEG-4 Support</source>
|
||||
<translation>MPEG-4 Support</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Played</source>
|
||||
<translation>Played</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>If enabled, the star and community rating for episodes of a TV show will be removed. This is to prevent spoilers of an upcoming good/bad episode.</source>
|
||||
<translation>If enabled, the star and community rating for episodes of a TV show will be removed. This is to prevent spoilers of an upcoming good/bad episode.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>H.264</source>
|
||||
<translation>H.264</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disable Community Rating for Episodes</source>
|
||||
<translation>Disable Community Rating for Episodes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View All</source>
|
||||
<translation>View All</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select when to show titles.</source>
|
||||
<translation>Select when to show titles.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show On Hover</source>
|
||||
<translation>Show On Hover</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Always Show</source>
|
||||
<translation>Always Show</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Always Hide</source>
|
||||
<translation>Always Hide</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Album</source>
|
||||
<translation>Album</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Artists (Grid)</source>
|
||||
<translation>Artists (Grid)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Song</source>
|
||||
<translation>Song</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Artists (Presentation)</source>
|
||||
<translation>Artists (Presentation)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Songs</source>
|
||||
<translation>Songs</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Albums</source>
|
||||
<translation>Albums</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -302,6 +302,16 @@
|
|||
<source>Movies</source>
|
||||
<translation>Movies</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Presentation)</source>
|
||||
<translation>Movies (Presentation)</translation>
|
||||
<extracomment>Movie library view option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Grid)</source>
|
||||
<translation>Movies (Grid)</translation>
|
||||
<extracomment>Movie library view option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Shows</source>
|
||||
<translation>TV Shows</translation>
|
||||
|
@ -484,10 +494,21 @@
|
|||
<translation>Playback</translation>
|
||||
<extracomment>Title for Playback section in user setting screen.</extracomment>
|
||||
</message>
|
||||
|
||||
<message>
|
||||
<source>MPEG-2 Support</source>
|
||||
<translation>MPEG-2 Support</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
<source>Codec Support</source>
|
||||
<translation>Codec Support</translation>
|
||||
<extracomment>Settings Menu - Title for settings group related to codec support</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable or disable Direct Play for optional codecs</source>
|
||||
<translation>Enable or disable Direct Play for optional codecs</translation>
|
||||
<extracomment>Settings Menu - Title for settings group related to codec support</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>MPEG-2</source>
|
||||
<translation>MPEG-2</translation>
|
||||
<extracomment>Name of codec used in settings menu</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>
|
||||
|
@ -495,14 +516,24 @@
|
|||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>AV1 Support</source>
|
||||
<translation>AV1 Support</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
<source>MPEG-4</source>
|
||||
<translation>MPEG-4</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.</source>
|
||||
<translation>Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>AV1</source>
|
||||
<translation>AV1</translation>
|
||||
<extracomment>Name of a setting - should we try to direct play experimental av1 codec</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>** EXPERIMENTAL** Support Direct Play of AV1 content if this Roku device supports it.</source>
|
||||
<translation>** EXPERIMENTAL** Support Direct Play of AV1 content if this Roku device supports it.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
<extracomment>Description of a setting - should we try to direct play experimental av1 codec</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enabled</source>
|
||||
|
@ -657,6 +688,16 @@
|
|||
<translation>Blur Unwatched Episodes</translation>
|
||||
<extracomment>Option Title in user setting screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip Details for Single Seasons</source>
|
||||
<translation>Skip Details for Single Seasons</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>If enabled, selecting a TV series with only one season will go straight to the episode list rather than the show details and season list.</source>
|
||||
<translation>If enabled, selecting a TV series with only one season will go straight to the episode list rather than the show details and season list.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>If enabled, images of unwatched episodes will be blurred.</source>
|
||||
<translation>If enabled, images of unwatched episodes will be blurred.</translation>
|
||||
|
@ -727,9 +768,9 @@
|
|||
<translation>Play Trailer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Direct Play H.264 Unsupported Profile Levels</source>
|
||||
<translation>Direct Play H.264 Unsupported Profile Levels</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
<source>H.264</source>
|
||||
<translation>H.264</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.</source>
|
||||
|
@ -737,9 +778,9 @@
|
|||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Direct Play HEVC Unsupported Profile Levels</source>
|
||||
<translation>Direct Play HEVC Unsupported Profile Levels</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
<source>HEVC</source>
|
||||
<translation>HEVC</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for HEVC media with unsupported profile levels before falling back to trancoding if it fails.</source>
|
||||
|
@ -865,7 +906,7 @@
|
|||
<source>Only display text subtitles to minimize transcoding.</source>
|
||||
<translation>Only display text subtitles to minimize transcoding.</translation>
|
||||
<extracomment>Description of a setting - should we hide subtitles that might transcode</extracomment>
|
||||
</message>
|
||||
</message>
|
||||
<message>
|
||||
<source>all</source>
|
||||
<translation>all</translation>
|
||||
|
@ -910,5 +951,103 @@
|
|||
<translation>Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show What's New Popup</source>
|
||||
<translation>Show What's New Popup</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show What's New popup when Jellyfin is updated to a new version.</source>
|
||||
<translation>Show What's New popup when Jellyfin is updated to a new version.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unplayed</source>
|
||||
<translation>Unplayed</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Played</source>
|
||||
<translation>Played</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Resumable</source>
|
||||
<translation>Resumable</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movie Library Default View</source>
|
||||
<translation>Movie Library Default View</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Default view for Movie Libraries.</source>
|
||||
<translation>Default view for Movie Libraries.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Presentation)</source>
|
||||
<translation>Movies (Presentation)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Grid)</source>
|
||||
<translation>Movies (Grid)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movie Library Grid Titles</source>
|
||||
<translation>Movie Library Grid Titles</translation>
|
||||
<extracomment>Settings Menu - Title for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select when to show titles.</source>
|
||||
<translation>Select when to show titles.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show On Hover</source>
|
||||
<translation>Show On Hover</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Always Show</source>
|
||||
<translation>Always Show</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Always Hide</source>
|
||||
<translation>Always Hide</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Artists (Presentation)</source>
|
||||
<translation>Artists (Presentation)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Artists (Grid)</source>
|
||||
<translation>Artists (Grid)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Song</source>
|
||||
<translation>Song</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Songs</source>
|
||||
<translation>Songs</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Album</source>
|
||||
<translation>Album</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Albums</source>
|
||||
<translation>Albums</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View All</source>
|
||||
<translation>View All</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disable Community Rating for Episodes</source>
|
||||
<translation>Disable Community Rating for Episodes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>If enabled, the star and community rating for episodes of a TV show will be removed. This is to prevent spoilers of an upcoming good/bad episode.</source>
|
||||
<translation>If enabled, the star and community rating for episodes of a TV show will be removed. This is to prevent spoilers of an upcoming good/bad episode.</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -2199,5 +2199,496 @@
|
|||
<source>NO_ITEMS</source>
|
||||
<translation>Este %1 no contiene elementos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Cambiar Servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Borrar Credenciales</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Puntuación de la crítica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Age</source>
|
||||
<translation>Edad</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>hoy</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Died</source>
|
||||
<translation>Muerto/a</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Puntuación de IMDb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cast & Crew</source>
|
||||
<translation>Reparto y equipo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>Sábado</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Repeat</source>
|
||||
<translation>Repetir</translation>
|
||||
<extracomment>If TV Shows has previously been broadcasted</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media Grid</source>
|
||||
<translation>Cuadrícula de medios</translation>
|
||||
<extracomment>UI -> Media Grid section in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Cerrar sesión</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>¿Guardar credenciales?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Error durante la reproducción</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Ha ocurrido un error al reproducir este contenido.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Reproduciendo contenido del canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Error de reproducción de contenido del canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Nombre</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Fecha en que se agregó</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Fecha de reproducción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Control Parental</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Fecha de estreno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Tiempo de duración</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Vista</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Clasificar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filtro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special Features</source>
|
||||
<translation>Funciones especiales</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to load Channel Data from the server</source>
|
||||
<translation>No se ha podido reproducir el contenido del canal de este servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Conteo de reproducción</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>En directo ahora</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Error recuperando contenido</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Ha ocurrido un error al tratar de recuperar la información de este contenido desde el servidor.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from 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>Este %1 no contiene elementos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Born</source>
|
||||
<translation>Nacido/a</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>Mas de este estilo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item. Server did not provide required transcoding data.</source>
|
||||
<translation>Se ha encontrado un error al reproducir este elemento. El servidor no proveyó la información necesaria para la transcodificación.</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>Use voice remote to search</source>
|
||||
<translation>Utilizar la búsqueda remota por voz</translation>
|
||||
<extracomment>Help text in search voice text box</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Presentation)</source>
|
||||
<translation>Películas (presentación)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Grid)</source>
|
||||
<translation>Películas (cuadrícula)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Shows</source>
|
||||
<translation>Programas de televisión</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>ayer</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>mañana</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>Jueves</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>Viernes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started at</source>
|
||||
<translation>Comenzó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program started today (e.g. Started at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts at</source>
|
||||
<translation>Comienza a las</translation>
|
||||
<extracomment>(Future Tense) For defining time when a program will start today (e.g. Starts at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Live</source>
|
||||
<translation>En vivo</translation>
|
||||
<extracomment>If TV Show is being broadcast live (not pre-recorded)</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Guide</source>
|
||||
<translation>Guía de televisión</translation>
|
||||
<extracomment>Menu option for showing Live TV Guide / Schedule</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Record Series</source>
|
||||
<translation>Grabar serie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel Recording</source>
|
||||
<translation>Cancelar la grabación</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cancel Series Recording</source>
|
||||
<translation>Cancelar la grabación de la serie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Close</source>
|
||||
<translation>Cerrar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connecting to Server</source>
|
||||
<translation>Conectando con el servidor</translation>
|
||||
<extracomment>Message to display to user while client is attempting to connect to the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Not found</source>
|
||||
<translation>No se ha encontrado</translation>
|
||||
<extracomment>Title of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown</source>
|
||||
<translation>Desconocido</translation>
|
||||
<extracomment>Title for a cast member for which we have no information for</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>The requested content does not exist on the server</source>
|
||||
<translation>El contenido solicitado no existe en el servidor</translation>
|
||||
<extracomment>Content of message box when the requested content is not found on the server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enter the server name or IP address</source>
|
||||
<translation>Agregar el nombre del servidor o su dirección de IP</translation>
|
||||
<extracomment>Title of KeyboardDialog when manually entering a server URL</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pick a Jellyfin server from the local network</source>
|
||||
<translation>Elige un servidor Jellyfin disponible en la red local:</translation>
|
||||
<extracomment>Instructions on initial app launch when the user is asked to pick a server from a list</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>Soporte de reproducción directa para contenido MPEG-2 (ej., televisión en vivo). Esto previene la transcodificación de contenido MPEG-2, pero a mayor uso de ancho de banda.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.</source>
|
||||
<translation>Soporte de reproducción directa para contenido MPEG-4. Esto podría requerir ser deshabilitado para poder reproducir los archivos de video con encodificación DIVX.</translation>
|
||||
<extracomment>Settings Menu - Description for option</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>** EXPERIMENTAL** Support Direct Play of AV1 content if this Roku device supports it.</source>
|
||||
<translation>**EXPERIMENTAL** Soporte de reproducción directa para contenido AV1 si este dispositivo Roku es compatible.</translation>
|
||||
<extracomment>Description of a setting - should we try to direct play experimental av1 codec</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>Siempre mostrar los títulos por debajo de las imágenes de cartelera. (Si se deshabilita, el título se mostrará debajo del elemento resaltado solamente).</translation>
|
||||
<extracomment>Description for option in Setting Screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Item Count</source>
|
||||
<translation>Conteo de elementos</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Count in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show item count in the library and index of selected item.</source>
|
||||
<translation>Mostrar el conteo de elementos en la biblioteca y en el índice del elemento seleccionado.</translation>
|
||||
<extracomment>Description for option in Setting Screen</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set Favorite</source>
|
||||
<translation>Agregar a favoritos</translation>
|
||||
<extracomment>Button Text - When pressed, sets item as Favorite</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set Watched</source>
|
||||
<translation>Marcar como visto</translation>
|
||||
<extracomment>Button Text - When pressed, marks item as Warched</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to series</source>
|
||||
<translation>Ir a serie</translation>
|
||||
<extracomment>Continue Watching Popup Menu - Navigate to the Series Detail Page</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to season</source>
|
||||
<translation>Ir a la temporada</translation>
|
||||
<extracomment>Continue Watching Popup Menu - Navigate to the Season Page</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to episode</source>
|
||||
<translation>Ir al episodio</translation>
|
||||
<extracomment>Continue Watching Popup Menu - Navigate to the Episode Detail Page</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search now</source>
|
||||
<translation>Buscar ahora</translation>
|
||||
<extracomment>Help text in search Box</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Press 'OK' to Close</source>
|
||||
<translation>Presiona 'OK' para cerrar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional Parts</source>
|
||||
<translation>Partes adicionales</translation>
|
||||
<extracomment>Additional parts of a video</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies</source>
|
||||
<translation>Películas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>Domingo</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>Lunes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>Martes</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>Miércoles</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Starts</source>
|
||||
<translation>Comienza a las</translation>
|
||||
<extracomment>(Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ended at</source>
|
||||
<translation>Terminó a las</translation>
|
||||
<extracomment>(Past Tense) For defining time when a program will ended (e.g. Ended at 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ends at</source>
|
||||
<translation>Termina a las</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Channels</source>
|
||||
<translation>Canales</translation>
|
||||
<extracomment>Menu option for showing Live TV Channel List</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Channel</source>
|
||||
<translation>Ver Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Record</source>
|
||||
<translation>Grabar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>...or enter server URL manually:</source>
|
||||
<translation>Si no hay servidores disponibles, puedes agregar manualmente la URL:</translation>
|
||||
<extracomment>Instructions on initial app launch when the user is asked to manually enter a server URL</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Getting Playback Information</source>
|
||||
<translation>Error obteniendo la Información de reproducción</translation>
|
||||
<extracomment>Dialog Title: Received error from server when trying to get information about the selected item for playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Version</source>
|
||||
<translation>Versión</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Playback</source>
|
||||
<translation>Reproducción</translation>
|
||||
<extracomment>Title for Playback section in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>User Interface</source>
|
||||
<translation>Interfaz de usuario</translation>
|
||||
<extracomment>Title for User Interface section in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Media Grid options.</source>
|
||||
<translation>Opciones de la cuadrícula de medios.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec Support</source>
|
||||
<translation>Soporte de Codec</translation>
|
||||
<extracomment>Settings Menu - Title for settings group related to codec support</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable or disable Direct Play for optional codecs</source>
|
||||
<translation>Habilitar o desactivar la reproducción directa para codecs opcionales</translation>
|
||||
<extracomment>Settings Menu - Title for settings group related to codec support</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>MPEG-2</source>
|
||||
<translation>MPEG-2</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>MPEG-4</source>
|
||||
<translation>MPEG-4</translation>
|
||||
<extracomment>Name of codec used in settings menu</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>AV1</source>
|
||||
<translation>AV1</translation>
|
||||
<extracomment>Name of a setting - should we try to direct play experimental av1 codec</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Item Titles</source>
|
||||
<translation>Títulos de elementos</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Title in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Started</source>
|
||||
<translation>Comenzó a las</translation>
|
||||
<extracomment>(Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00) </extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enabled</source>
|
||||
<translation>Activado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Disabled</source>
|
||||
<translation>Desactivado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Shows</source>
|
||||
<translation>espectáculos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Quick Connect</source>
|
||||
<translation>Conexión rápida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(Dialog will close automatically)</source>
|
||||
<translation>(El diálogo se cerrará automáticamente)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Return to Top</source>
|
||||
<translation>Vuelva a la parte superior</translation>
|
||||
<extracomment>UI -> Media Grid -> Item Title in user setting screen.</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>You can search for Titles, People, Live TV Channels and more</source>
|
||||
<translation>Puede buscar títulos, personas, canales de TV en vivo y más</translation>
|
||||
<extracomment>Help text in search results</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Here is your Quick Connect code:</source>
|
||||
<translation>Aquí está su código de conexión rápida:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error authenticating via Quick Connect.</source>
|
||||
<translation>Hubo un error al autenticarse a través de Quick Connect.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Networks</source>
|
||||
<translation>Redes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Studios</source>
|
||||
<translation>Estudios</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1124,5 +1124,53 @@
|
|||
<source>Change Server</source>
|
||||
<translation>Changer de serveur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Chargement des données de la chaîne</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Erreur lors du chargement des données de la chaîne</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>En ce moment</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>An error was encountered while playing this item.</source>
|
||||
<translation>Une erreur s'est produite lors de la lecture de cet élément.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Changer de serveur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Se déconnecter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Sauvegarder les informations d'authentification ?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Supprimer les valeurs enregistrées</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Erreur lors de la lecture</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Une erreur s'est produite lors de la récupération des données de cet élément depuis le serveur.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1921,5 +1921,36 @@
|
|||
<translation>Errore durante la riproduzione</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Errore durante la riproduzione</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Esci</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>C'è stato un errore nel recupero dei dati per questo elemento dal server.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>È stato riscontrato un errore durante la riproduzione di questo oggetto.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Caricamento dati del canale</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Salvare le credenziali?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Cambia server</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -2643,5 +2643,53 @@ não contém itens</translation>
|
|||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Classificação Etária</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sign Out</source>
|
||||
<translation>Sair</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Salvar Credenciais?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Deletar Salvos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error During Playback</source>
|
||||
<translation>Erro Durante a Reprodução</translation>
|
||||
<extracomment>Dialog title when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Change Server</source>
|
||||
<translation>Mudar Servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error Retrieving Content</source>
|
||||
<translation>Erro ao Carregar Conteúdo</translation>
|
||||
<extracomment>Dialog title when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading Channel Data</source>
|
||||
<translation>Carregando Dados do Canal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error was encountered while playing this item.</source>
|
||||
<translation>Foi encontrado um erro na reprodução deste item.</translation>
|
||||
<extracomment>Dialog detail when error occurs during playback</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>On Now</source>
|
||||
<translation>Em Exibição</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>There was an error retrieving the data for this item from the server.</source>
|
||||
<translation>Houve um erro ao recuperar os dados deste item do servidor.</translation>
|
||||
<extracomment>Dialog detail when unable to load Content from Server</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Error loading Channel Data</source>
|
||||
<translation>Erro ao carregar os Dados do Canal</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -958,5 +958,169 @@
|
|||
<translation>Prehrávanie</translation>
|
||||
<extracomment>Title for Playback section in user setting screen.</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>Tento %1 neobsahuje žiadne položky</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Tuesday</source>
|
||||
<translation>utorok</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Friday</source>
|
||||
<translation>piatok</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>IMDB_RATING</source>
|
||||
<translation>Hodnotenie IMDb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RELEASE_DATE</source>
|
||||
<translation>Dátum vydania</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for switching "views" when looking at a library</comment>
|
||||
<source>TAB_VIEW</source>
|
||||
<translation>Zobrazenie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save Credentials?</source>
|
||||
<translation>Uložiť prihlasovacie údaje?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_PLAYED</source>
|
||||
<translation>Dátum prehrania</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cast & Crew</source>
|
||||
<translation>Obsadenie a štáb</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies</source>
|
||||
<translation>Filmy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Name or Title field of media item</comment>
|
||||
<source>TITLE</source>
|
||||
<translation>Meno</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete Saved</source>
|
||||
<translation>Odstrániť uložené</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>DATE_ADDED</source>
|
||||
<translation>Dátum pridania</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OFFICIAL_RATING</source>
|
||||
<translation>Rodičovské hodnotenie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>PLAY_COUNT</source>
|
||||
<translation>Počet prehraní</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>RUNTIME</source>
|
||||
<translation>Dĺžka</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to sort library content</comment>
|
||||
<source>TAB_SORT</source>
|
||||
<translation>Zoradenie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<comment>Title of Tab for options to filter library content</comment>
|
||||
<source>TAB_FILTER</source>
|
||||
<translation>Filter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Born</source>
|
||||
<translation>Dátum narodenia</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Died</source>
|
||||
<translation>Dátum úmrtia</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Age</source>
|
||||
<translation>Vek</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>More Like This</source>
|
||||
<translation>Viac podobných</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>CRITIC_RATING</source>
|
||||
<translation>Hodnotenie kritikov</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Press 'OK' to Close</source>
|
||||
<translation>Zatvorte stlačením tlačidla 'OK'</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special Features</source>
|
||||
<translation>Špeciálne Funkcie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Additional Parts</source>
|
||||
<translation>Dodatočné Časti</translation>
|
||||
<extracomment>Additional parts of a video</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>yesterday</source>
|
||||
<translation>včera</translation>
|
||||
<extracomment>Previous day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sunday</source>
|
||||
<translation>nedeľa</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Monday</source>
|
||||
<translation>pondelok</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Wednesday</source>
|
||||
<translation>streda</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thursday</source>
|
||||
<translation>štvrtok</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Saturday</source>
|
||||
<translation>sobota</translation>
|
||||
<extracomment>Day of Week</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Presentation)</source>
|
||||
<translation>Filmy (prezentácia)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Grid)</source>
|
||||
<translation>Filmy (mriežka)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>TV Shows</source>
|
||||
<translation>TV Seriály</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>today</source>
|
||||
<translation>dnes</translation>
|
||||
<extracomment>Current day</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>tomorrow</source>
|
||||
<translation>zajtra</translation>
|
||||
<extracomment>Next day</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
240
package-lock.json
generated
240
package-lock.json
generated
|
@ -17,9 +17,9 @@
|
|||
"sob": "npm:slide-out-button@^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rokucommunity/bslint": "0.8.0",
|
||||
"brighterscript": "0.61.2",
|
||||
"ropm": "0.10.10"
|
||||
"@rokucommunity/bslint": "0.8.1",
|
||||
"brighterscript": "0.61.3",
|
||||
"ropm": "0.10.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
|
@ -60,9 +60,9 @@
|
|||
"integrity": "sha512-2ox6EUL+UTtccTbD4dbVjZK3QHa0PHCqpoKMF8lZz9ayzzEP3iVPF8KZR6hOi6bxsIcbGXVjqmtCVkpC4P9SrA=="
|
||||
},
|
||||
"node_modules/@rokucommunity/bslint": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.8.0.tgz",
|
||||
"integrity": "sha512-fqTiBMczZa6YmJix0b81cDm2n9c7GlumFirwLGJSHpCEvnxwNTf0tIo3efnd4N5aJq7kpg8Y3GEbWnKs58BhFg==",
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.8.1.tgz",
|
||||
"integrity": "sha512-G4XhWHMZzq6HJiNazd3Vpg8mstYkbOk6dQC4JD7vmBlSVqm0KkITmZRNlumn/x1v60E9dGONP89MkoBz3/DHaA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fs-extra": "^10.0.0",
|
||||
|
@ -371,9 +371,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/brighterscript": {
|
||||
"version": "0.61.2",
|
||||
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.61.2.tgz",
|
||||
"integrity": "sha512-QNawTRD9DHlyVc/lwD0pSJQIzFf1JsjL9CHu4jmv+41/o+/DzSAHAjQhgIa9dQWroIAwHPUVKMya+0jXlIrIvw==",
|
||||
"version": "0.61.3",
|
||||
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.61.3.tgz",
|
||||
"integrity": "sha512-8BDpOSCdmkS/QcTdPTUW/99nCBypuoa/Zz6PZHI6OiVylqTBidtrGI7lBZotqY6yvQ3KJl24thhLHK5XuIT/6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@rokucommunity/bslib": "^0.1.1",
|
||||
|
@ -391,15 +391,15 @@
|
|||
"fs-extra": "^8.0.0",
|
||||
"jsonc-parser": "^2.3.0",
|
||||
"long": "^3.2.0",
|
||||
"luxon": "^1.8.3",
|
||||
"luxon": "^2.5.2",
|
||||
"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.9.2",
|
||||
"roku-deploy": "^3.9.3",
|
||||
"serialize-error": "^7.0.1",
|
||||
"source-map": "^0.7.3",
|
||||
"source-map": "^0.7.4",
|
||||
"vscode-languageserver": "7.0.0",
|
||||
"vscode-languageserver-protocol": "3.16.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.1",
|
||||
|
@ -520,6 +520,15 @@
|
|||
"node": ">=6 <7 || >=8"
|
||||
}
|
||||
},
|
||||
"node_modules/brighterscript/node_modules/luxon": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-2.5.2.tgz",
|
||||
"integrity": "sha512-Yg7/RDp4nedqmLgyH0LwgGRvMEKVzKbUdkBYyCosbHgJ+kaOUx0qzSiSatVc3DFygnirTPYnMM2P5dg2uH1WvA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/brighterscript/node_modules/yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
|
@ -1398,9 +1407,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "1.17.2",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.17.2.tgz",
|
||||
"integrity": "sha512-qELKtIj3HD41N+MvgoxArk8DZGUb4Gpiijs91oi+ZmKJzRlxY6CoyTwNoUwnogCVs4p8HuxVJDik9JbnYgrCng==",
|
||||
"version": "1.28.1",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz",
|
||||
"integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
|
@ -1794,9 +1803,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/roku-deploy": {
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.9.2.tgz",
|
||||
"integrity": "sha512-2LZyR4EhaFrka1gVmcuJO/f42tqz4clGImboVLCNem1q/PcFV5cnXNZRfqDI+MZ/n8eJto71JlZkcc7TDLt/EQ==",
|
||||
"version": "3.9.3",
|
||||
"resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.9.3.tgz",
|
||||
"integrity": "sha512-cjTx5ffZNt07rQS+0s2sTBHkZKUk283y9f6UnbI77X03lQ60vYlCnqsKswWisFYMHPIdvsTLLSfKsshAPwKHEQ==",
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.2",
|
||||
"dateformat": "^3.0.3",
|
||||
|
@ -1831,20 +1840,20 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ropm": {
|
||||
"version": "0.10.10",
|
||||
"resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.10.tgz",
|
||||
"integrity": "sha512-tkPuDwP/Mva9IXIuTf4pnH1DC27WLeLfu8QJ70WwaX9tepNMZeDi4eEQdWQ7kalXxxlwXGnG4jUaaA1B4v8zWw==",
|
||||
"version": "0.10.11",
|
||||
"resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.11.tgz",
|
||||
"integrity": "sha512-D9Nfi2tuB32d9gJFdyBdUw6+sBo4lwG2trkL9S0jHJCKqAQWs6tgkmDRd+pp6ZrKEKSK1V2y0bi8xFOi2Rjr+Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@xml-tools/ast": "^5.0.5",
|
||||
"@xml-tools/parser": "1.0.10",
|
||||
"brighterscript": "^0.57.2",
|
||||
"brighterscript": "^0.61.3",
|
||||
"del": "6.0.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"glob-all": "3.2.1",
|
||||
"latinize": "0.5.0",
|
||||
"npm-packlist": "2.1.4",
|
||||
"roku-deploy": "^3.8.1",
|
||||
"roku-deploy": "^3.9.3",
|
||||
"semver": "7.3.4",
|
||||
"yargs": "16.2.0"
|
||||
},
|
||||
|
@ -1864,79 +1873,6 @@
|
|||
"chevrotain": "7.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ropm/node_modules/brighterscript": {
|
||||
"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",
|
||||
"@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/brighterscript/node_modules/jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"dev": true,
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ropm/node_modules/brighterscript/node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ropm/node_modules/chevrotain": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-7.1.1.tgz",
|
||||
|
@ -2438,9 +2374,9 @@
|
|||
"integrity": "sha512-2ox6EUL+UTtccTbD4dbVjZK3QHa0PHCqpoKMF8lZz9ayzzEP3iVPF8KZR6hOi6bxsIcbGXVjqmtCVkpC4P9SrA=="
|
||||
},
|
||||
"@rokucommunity/bslint": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.8.0.tgz",
|
||||
"integrity": "sha512-fqTiBMczZa6YmJix0b81cDm2n9c7GlumFirwLGJSHpCEvnxwNTf0tIo3efnd4N5aJq7kpg8Y3GEbWnKs58BhFg==",
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.8.1.tgz",
|
||||
"integrity": "sha512-G4XhWHMZzq6HJiNazd3Vpg8mstYkbOk6dQC4JD7vmBlSVqm0KkITmZRNlumn/x1v60E9dGONP89MkoBz3/DHaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs-extra": "^10.0.0",
|
||||
|
@ -2695,9 +2631,9 @@
|
|||
}
|
||||
},
|
||||
"brighterscript": {
|
||||
"version": "0.61.2",
|
||||
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.61.2.tgz",
|
||||
"integrity": "sha512-QNawTRD9DHlyVc/lwD0pSJQIzFf1JsjL9CHu4jmv+41/o+/DzSAHAjQhgIa9dQWroIAwHPUVKMya+0jXlIrIvw==",
|
||||
"version": "0.61.3",
|
||||
"resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.61.3.tgz",
|
||||
"integrity": "sha512-8BDpOSCdmkS/QcTdPTUW/99nCBypuoa/Zz6PZHI6OiVylqTBidtrGI7lBZotqY6yvQ3KJl24thhLHK5XuIT/6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rokucommunity/bslib": "^0.1.1",
|
||||
|
@ -2715,15 +2651,15 @@
|
|||
"fs-extra": "^8.0.0",
|
||||
"jsonc-parser": "^2.3.0",
|
||||
"long": "^3.2.0",
|
||||
"luxon": "^1.8.3",
|
||||
"luxon": "^2.5.2",
|
||||
"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.9.2",
|
||||
"roku-deploy": "^3.9.3",
|
||||
"serialize-error": "^7.0.1",
|
||||
"source-map": "^0.7.3",
|
||||
"source-map": "^0.7.4",
|
||||
"vscode-languageserver": "7.0.0",
|
||||
"vscode-languageserver-protocol": "3.16.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.1",
|
||||
|
@ -2743,6 +2679,12 @@
|
|||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"luxon": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-2.5.2.tgz",
|
||||
"integrity": "sha512-Yg7/RDp4nedqmLgyH0LwgGRvMEKVzKbUdkBYyCosbHgJ+kaOUx0qzSiSatVc3DFygnirTPYnMM2P5dg2uH1WvA==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
|
||||
|
@ -3533,9 +3475,9 @@
|
|||
}
|
||||
},
|
||||
"luxon": {
|
||||
"version": "1.17.2",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.17.2.tgz",
|
||||
"integrity": "sha512-qELKtIj3HD41N+MvgoxArk8DZGUb4Gpiijs91oi+ZmKJzRlxY6CoyTwNoUwnogCVs4p8HuxVJDik9JbnYgrCng=="
|
||||
"version": "1.28.1",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz",
|
||||
"integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw=="
|
||||
},
|
||||
"merge2": {
|
||||
"version": "1.4.1",
|
||||
|
@ -3813,9 +3755,9 @@
|
|||
}
|
||||
},
|
||||
"roku-deploy": {
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.9.2.tgz",
|
||||
"integrity": "sha512-2LZyR4EhaFrka1gVmcuJO/f42tqz4clGImboVLCNem1q/PcFV5cnXNZRfqDI+MZ/n8eJto71JlZkcc7TDLt/EQ==",
|
||||
"version": "3.9.3",
|
||||
"resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.9.3.tgz",
|
||||
"integrity": "sha512-cjTx5ffZNt07rQS+0s2sTBHkZKUk283y9f6UnbI77X03lQ60vYlCnqsKswWisFYMHPIdvsTLLSfKsshAPwKHEQ==",
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
"dateformat": "^3.0.3",
|
||||
|
@ -3846,20 +3788,20 @@
|
|||
}
|
||||
},
|
||||
"ropm": {
|
||||
"version": "0.10.10",
|
||||
"resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.10.tgz",
|
||||
"integrity": "sha512-tkPuDwP/Mva9IXIuTf4pnH1DC27WLeLfu8QJ70WwaX9tepNMZeDi4eEQdWQ7kalXxxlwXGnG4jUaaA1B4v8zWw==",
|
||||
"version": "0.10.11",
|
||||
"resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.11.tgz",
|
||||
"integrity": "sha512-D9Nfi2tuB32d9gJFdyBdUw6+sBo4lwG2trkL9S0jHJCKqAQWs6tgkmDRd+pp6ZrKEKSK1V2y0bi8xFOi2Rjr+Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@xml-tools/ast": "^5.0.5",
|
||||
"@xml-tools/parser": "1.0.10",
|
||||
"brighterscript": "^0.57.2",
|
||||
"brighterscript": "^0.61.3",
|
||||
"del": "6.0.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"glob-all": "3.2.1",
|
||||
"latinize": "0.5.0",
|
||||
"npm-packlist": "2.1.4",
|
||||
"roku-deploy": "^3.8.1",
|
||||
"roku-deploy": "^3.9.3",
|
||||
"semver": "7.3.4",
|
||||
"yargs": "16.2.0"
|
||||
},
|
||||
|
@ -3873,72 +3815,6 @@
|
|||
"chevrotain": "7.1.1"
|
||||
}
|
||||
},
|
||||
"brighterscript": {
|
||||
"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",
|
||||
"@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"
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"chevrotain": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-7.1.1.tgz",
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
"description": "Roku app for Jellyfin media server",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"@rokucommunity/bslint": "0.8.0",
|
||||
"brighterscript": "0.61.2",
|
||||
"ropm": "0.10.10"
|
||||
"@rokucommunity/bslint": "0.8.1",
|
||||
"brighterscript": "0.61.3",
|
||||
"ropm": "0.10.11"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "npx ropm copy",
|
||||
|
|
|
@ -4,39 +4,51 @@
|
|||
"description": "Settings relating to playback and supported codec and media types.",
|
||||
"children": [
|
||||
{
|
||||
"title": "AV1 Support",
|
||||
"description": "** EXPERIMENTAL** Support Direct Play of AV1 content if this Roku device supports it.",
|
||||
"settingName": "playback.av1",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
"title": "Codec Support",
|
||||
"description": "Enable or disable Direct Play support for certain codecs",
|
||||
"children": [
|
||||
{
|
||||
"title": "AV1",
|
||||
"description": "** EXPERIMENTAL** Support Direct Play of AV1 content if this Roku device supports it.",
|
||||
"settingName": "playback.av1",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"title": "MPEG-2",
|
||||
"description": "Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.",
|
||||
"settingName": "playback.mpeg2",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"title": "MPEG-4",
|
||||
"description": "Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.",
|
||||
"settingName": "playback.mpeg4",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "MPEG-2 Support",
|
||||
"description": "Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.",
|
||||
"settingName": "playback.mpeg2",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"title": "MPEG-4 Support",
|
||||
"description": "Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.",
|
||||
"settingName": "playback.mpeg4",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"title": "Direct Play H.264 Unsupported Profile Levels",
|
||||
"description": "Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.",
|
||||
"settingName": "playback.tryDirect.h264ProfileLevel",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"title": "Direct Play HEVC Unsupported Profile Levels",
|
||||
"description": "Attempt Direct Play for HEVC media with unsupported profile levels before falling back to trancoding if it fails.",
|
||||
"settingName": "playback.tryDirect.hevcProfileLevel",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
"title": "Profile Level Support",
|
||||
"description": "Attempt Direct Play of potentially unsupported profile levels",
|
||||
"children": [
|
||||
{
|
||||
"title": "H.264",
|
||||
"description": "Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.",
|
||||
"settingName": "playback.tryDirect.h264ProfileLevel",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"title": "HEVC",
|
||||
"description": "Attempt Direct Play for HEVC media with unsupported profile levels before falling back to trancoding if it fails.",
|
||||
"settingName": "playback.tryDirect.hevcProfileLevel",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Cinema Mode",
|
||||
|
@ -54,6 +66,13 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Show What's New Popup",
|
||||
"description": "Show What's New popup when Jellyfin is updated to a new version.",
|
||||
"settingName": "load.allowwhatsnew",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"title": "User Interface",
|
||||
"description": "Settings relating to how the application looks.",
|
||||
|
@ -68,6 +87,13 @@
|
|||
"settingName": "ui.details.maxdaysnextup",
|
||||
"type": "integer",
|
||||
"default": "365"
|
||||
},
|
||||
{
|
||||
"title": "Use Splashscreen as Home Background",
|
||||
"description": "Use generated splashscreen image as Jellyfin's home background. Jellyfin will need to be closed and reopened for change to take effect.",
|
||||
"settingName": "ui.home.splashBackground",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -94,6 +120,20 @@
|
|||
"settingName": "ui.tvshows.blurunwatched",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"title": "Skip Details for Single Seasons",
|
||||
"description": "If enabled, selecting a TV series with only one season will go straight to the episode list rather than the show details and season list.",
|
||||
"settingName": "ui.tvshows.goStraightToEpisodeListing",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"title":"Disable Community Rating for Episodes",
|
||||
"description": "If enabled, the star and community rating for episodes of a TV show will be removed. This is to prevent spoilers of an upcoming good/bad episode.",
|
||||
"settingName": "ui.tvshows.disableCommunityRating",
|
||||
"type":"bool",
|
||||
"default":"false"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -120,13 +160,6 @@
|
|||
"settingName": "ui.design.hideclock",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"title": "Use Splashscreen as Home Background",
|
||||
"description": "Use generated splashscreen image as Jellyfin's home background. Jellyfin will need to be closed and reopened for change to take effect.",
|
||||
"settingName": "ui.home.splashBackground",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -134,6 +167,44 @@
|
|||
"title": "Media Grid",
|
||||
"description": "Media Grid options.",
|
||||
"children": [
|
||||
{
|
||||
"title": "Movie Library Default View",
|
||||
"description": "Default view for Movie Libraries.",
|
||||
"settingName": "itemgrid.movieDefaultView",
|
||||
"type": "radio",
|
||||
"default": "movies",
|
||||
"options": [
|
||||
{
|
||||
"title": "Movies (Presentation)",
|
||||
"id": "Movies"
|
||||
},
|
||||
{
|
||||
"title": "Movies (Grid)",
|
||||
"id": "MoviesGrid"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Movie Library Grid Titles",
|
||||
"description": "Select when to show titles.",
|
||||
"settingName": "itemgrid.movieGridTitles",
|
||||
"type": "radio",
|
||||
"default": "showonhover",
|
||||
"options": [
|
||||
{
|
||||
"title": "Show On Hover",
|
||||
"id": "showonhover"
|
||||
},
|
||||
{
|
||||
"title": "Always Show",
|
||||
"id": "showalways"
|
||||
},
|
||||
{
|
||||
"title": "Always Hide",
|
||||
"id": "hidealways"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Item Count",
|
||||
"description": "Show item count in the library and index of selected item.",
|
||||
|
|
|
@ -41,6 +41,7 @@ sub Main (args as dynamic) as void
|
|||
sceneManager = CreateObject("roSGNode", "SceneManager")
|
||||
|
||||
m.global.addFields({ app_loaded: false, playstateTask: playstateTask, sceneManager: sceneManager })
|
||||
m.global.addFields({ queueManager: CreateObject("roSGNode", "QueueManager") })
|
||||
|
||||
app_start:
|
||||
' First thing to do is validate the ability to use the API
|
||||
|
@ -56,6 +57,17 @@ sub Main (args as dynamic) as void
|
|||
|
||||
m.scene.observeField("exit", m.port)
|
||||
|
||||
' Only show the Whats New popup the first time a user runs a new client version.
|
||||
if appInfo.GetVersion() <> get_setting("LastRunVersion")
|
||||
' Ensure the user hasn't disabled Whats New popups
|
||||
if get_user_setting("load.allowwhatsnew") = "true"
|
||||
set_setting("LastRunVersion", appInfo.GetVersion())
|
||||
dialog = createObject("roSGNode", "WhatsNewDialog")
|
||||
m.scene.dialog = dialog
|
||||
m.scene.dialog.observeField("buttonSelected", m.port)
|
||||
end if
|
||||
end if
|
||||
|
||||
' Handle input messages
|
||||
input = CreateObject("roInput")
|
||||
input.SetMessagePort(m.port)
|
||||
|
@ -66,10 +78,10 @@ sub Main (args as dynamic) as void
|
|||
m.device.EnableAppFocusEvent(false)
|
||||
|
||||
' Check if we were sent content to play with the startup command (Deep Link)
|
||||
if (args.mediaType <> invalid) and (args.contentId <> invalid)
|
||||
if isValidAndNotEmpty(args.mediaType) and isValidAndNotEmpty(args.contentId)
|
||||
video = CreateVideoPlayerGroup(args.contentId)
|
||||
|
||||
if video <> invalid and video.errorMsg <> "introaborted"
|
||||
if isValid(video) and video.errorMsg <> "introaborted"
|
||||
sceneManager.callFunc("pushScene", video)
|
||||
else
|
||||
dialog = createObject("roSGNode", "Dialog")
|
||||
|
@ -101,6 +113,7 @@ sub Main (args as dynamic) as void
|
|||
group.setFocus(true)
|
||||
end if
|
||||
else if isNodeEvent(msg, "quickPlayNode")
|
||||
group = sceneManager.callFunc("getActiveScene")
|
||||
reportingNode = msg.getRoSGNode()
|
||||
itemNode = reportingNode.quickPlayNode
|
||||
if itemNode = invalid or itemNode.id = "" then return
|
||||
|
@ -113,22 +126,40 @@ sub Main (args as dynamic) as void
|
|||
if video <> invalid and video.errorMsg <> "introaborted"
|
||||
sceneManager.callFunc("pushScene", video)
|
||||
end if
|
||||
|
||||
if LCase(group.subtype()) = "tvepisodes"
|
||||
if isValid(group.lastFocus)
|
||||
group.lastFocus.setFocus(true)
|
||||
end if
|
||||
end if
|
||||
|
||||
reportingNode.quickPlayNode.type = ""
|
||||
end if
|
||||
else if isNodeEvent(msg, "selectedItem")
|
||||
' If you select a library from ANYWHERE, follow this flow
|
||||
selectedItem = msg.getData()
|
||||
|
||||
m.selectedItemType = selectedItem.type
|
||||
'
|
||||
|
||||
if selectedItem.type = "CollectionFolder"
|
||||
if selectedItem.collectionType = "movies"
|
||||
group = CreateMovieLibraryView(selectedItem)
|
||||
else if selectedItem.collectionType = "music"
|
||||
group = CreateMusicLibraryView(selectedItem)
|
||||
else
|
||||
group = CreateItemGrid(selectedItem)
|
||||
end if
|
||||
sceneManager.callFunc("pushScene", group)
|
||||
else if selectedItem.type = "Folder" and selectedItem.json.type = "Genre"
|
||||
group = CreateMovieLibraryView(selectedItem)
|
||||
' User clicked on a genre folder
|
||||
if selectedItem.json.MovieCount > 0
|
||||
group = CreateMovieLibraryView(selectedItem)
|
||||
else
|
||||
group = CreateItemGrid(selectedItem)
|
||||
end if
|
||||
sceneManager.callFunc("pushScene", group)
|
||||
else if selectedItem.type = "Folder" and selectedItem.json.type = "MusicGenre"
|
||||
group = CreateMusicLibraryView(selectedItem)
|
||||
sceneManager.callFunc("pushScene", group)
|
||||
else if selectedItem.type = "UserView" or selectedItem.type = "Folder" or selectedItem.type = "Channel" or selectedItem.type = "Boxset"
|
||||
group = CreateItemGrid(selectedItem)
|
||||
|
@ -192,7 +223,9 @@ sub Main (args as dynamic) as void
|
|||
else if selectedItem.type = "MusicAlbum"
|
||||
group = CreateAlbumView(selectedItem.json)
|
||||
else if selectedItem.type = "Audio"
|
||||
group = CreateAudioPlayerGroup([selectedItem.json])
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", selectedItem.json)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else
|
||||
' TODO - switch on more node types
|
||||
message_dialog("This type is not yet supported: " + selectedItem.type + ".")
|
||||
|
@ -228,17 +261,27 @@ sub Main (args as dynamic) as void
|
|||
' User has selected audio they want us to play
|
||||
selectedIndex = msg.getData()
|
||||
screenContent = msg.getRoSGNode()
|
||||
group = CreateAudioPlayerGroup([screenContent.albumData.items[selectedIndex]])
|
||||
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", screenContent.albumData.items[selectedIndex])
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else if isNodeEvent(msg, "playAllSelected")
|
||||
' User has selected playlist of of audio they want us to play
|
||||
screenContent = msg.getRoSGNode()
|
||||
m.spinner = screenContent.findNode("spinner")
|
||||
m.spinner.visible = true
|
||||
group = CreateAudioPlayerGroup(screenContent.albumData.items)
|
||||
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("set", screenContent.albumData.items)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else if isNodeEvent(msg, "playArtistSelected")
|
||||
' User has selected playlist of of audio they want us to play
|
||||
screenContent = msg.getRoSGNode()
|
||||
group = CreateArtistMixGroup(screenContent.pageContent.id)
|
||||
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("set", CreateArtistMix(screenContent.pageContent.id).Items)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
|
||||
else if isNodeEvent(msg, "instantMixSelected")
|
||||
' User has selected instant mix
|
||||
' User has selected playlist of of audio they want us to play
|
||||
|
@ -248,20 +291,26 @@ sub Main (args as dynamic) as void
|
|||
m.spinner.visible = true
|
||||
end if
|
||||
|
||||
group = invalid
|
||||
viewHandled = false
|
||||
|
||||
' Create instant mix based on selected album
|
||||
if isValid(screenContent.albumData)
|
||||
if isValid(screenContent.albumData.items)
|
||||
if screenContent.albumData.items.count() > 0
|
||||
group = CreateInstantMixGroup(screenContent.albumData.items)
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("set", CreateInstantMix(screenContent.albumData.items[0].id).Items)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
|
||||
viewHandled = true
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
|
||||
' Create instant mix based on selected artist
|
||||
if not isValid(group)
|
||||
group = CreateInstantMixGroup([{ id: screenContent.pageContent.id }])
|
||||
if not viewHandled
|
||||
' Create instant mix based on selected artist
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("set", CreateInstantMix(screenContent.pageContent.id).Items)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
end if
|
||||
|
||||
else if isNodeEvent(msg, "episodeSelected")
|
||||
|
@ -306,7 +355,9 @@ sub Main (args as dynamic) as void
|
|||
else if node.type = "MusicAlbum"
|
||||
group = CreateAlbumView(node.json)
|
||||
else if node.type = "Audio"
|
||||
group = CreateAudioPlayerGroup([node.json])
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", node.json)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else if node.type = "Person"
|
||||
group = CreatePersonView(node)
|
||||
else if node.type = "TvChannel"
|
||||
|
@ -318,7 +369,9 @@ sub Main (args as dynamic) as void
|
|||
else if node.type = "Audio"
|
||||
selectedIndex = msg.getData()
|
||||
screenContent = msg.getRoSGNode()
|
||||
group = CreateAudioPlayerGroup([screenContent.albumData.items[node.id]])
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", screenContent.albumData.items[node.id])
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
else
|
||||
' TODO - switch on more node types
|
||||
message_dialog("This type is not yet supported: " + node.type + ".")
|
||||
|
|
|
@ -358,12 +358,18 @@ function CreateMovieDetailsGroup(movie)
|
|||
end function
|
||||
|
||||
function CreateSeriesDetailsGroup(series)
|
||||
' Get season data early in the function so we can check number of seasons.
|
||||
seasonData = TVSeasons(series.id)
|
||||
' Divert to season details if user setting goStraightToEpisodeListing is enabled and only one season exists.
|
||||
if get_user_setting("ui.tvshows.goStraightToEpisodeListing") = "true" and seasonData.Items.Count() = 1
|
||||
return CreateSeasonDetailsGroupByID(series.id, seasonData.Items[0].id)
|
||||
end if
|
||||
group = CreateObject("roSGNode", "TVShowDetails")
|
||||
group.optionsAvailable = false
|
||||
m.global.sceneManager.callFunc("pushScene", group)
|
||||
|
||||
group.itemContent = ItemMetaData(series.id)
|
||||
group.seasonData = TVSeasons(series.id)
|
||||
group.seasonData = seasonData ' Re-use variable from beginning of function
|
||||
|
||||
group.observeField("seasonSelected", m.port)
|
||||
|
||||
|
@ -483,6 +489,14 @@ function CreateMovieLibraryView(libraryItem)
|
|||
return group
|
||||
end function
|
||||
|
||||
function CreateMusicLibraryView(libraryItem)
|
||||
group = CreateObject("roSGNode", "MusicLibraryView")
|
||||
group.parentItem = libraryItem
|
||||
group.optionsAvailable = true
|
||||
group.observeField("selectedItem", m.port)
|
||||
return group
|
||||
end function
|
||||
|
||||
function CreateSearchPage()
|
||||
' Search + Results Page
|
||||
group = CreateObject("roSGNode", "searchResults")
|
||||
|
@ -512,72 +526,6 @@ function CreateVideoPlayerGroup(video_id, mediaSourceId = invalid, audio_stream_
|
|||
return video
|
||||
end function
|
||||
|
||||
' Play Audio
|
||||
function CreateAudioPlayerGroup(audiodata)
|
||||
|
||||
group = CreateObject("roSGNode", "NowPlaying")
|
||||
group.observeField("state", m.port)
|
||||
songIDArray = CreateObject("roArray", 0, true)
|
||||
|
||||
' All we need is an array of Song IDs the user selected to play.
|
||||
for each song in audiodata
|
||||
songIDArray.push(song.id)
|
||||
end for
|
||||
|
||||
group.pageContent = songIDArray
|
||||
group.musicArtistAlbumData = audiodata
|
||||
|
||||
m.global.sceneManager.callFunc("pushScene", group)
|
||||
|
||||
return group
|
||||
end function
|
||||
|
||||
' Play Instant Mix
|
||||
function CreateInstantMixGroup(audiodata)
|
||||
|
||||
songList = CreateInstantMix(audiodata[0].id)
|
||||
|
||||
group = CreateObject("roSGNode", "NowPlaying")
|
||||
group.observeField("state", m.port)
|
||||
songIDArray = CreateObject("roArray", 0, true)
|
||||
|
||||
' All we need is an array of Song IDs the user selected to play.
|
||||
for each song in songList.items
|
||||
songIDArray.push(song.id)
|
||||
end for
|
||||
|
||||
songIDArray.shift()
|
||||
|
||||
group.pageContent = songIDArray
|
||||
group.musicArtistAlbumData = songList.items
|
||||
|
||||
m.global.sceneManager.callFunc("pushScene", group)
|
||||
|
||||
return group
|
||||
end function
|
||||
|
||||
' Play Artist
|
||||
function CreateArtistMixGroup(artistID)
|
||||
|
||||
songList = CreateArtistMix(artistID)
|
||||
|
||||
group = CreateObject("roSGNode", "NowPlaying")
|
||||
group.observeField("state", m.port)
|
||||
songIDArray = CreateObject("roArray", 0, true)
|
||||
|
||||
' All we need is an array of Song IDs the user selected to play.
|
||||
for each song in songList.items
|
||||
songIDArray.push(song.id)
|
||||
end for
|
||||
|
||||
group.pageContent = songIDArray
|
||||
group.musicArtistAlbumData = songList.items
|
||||
|
||||
m.global.sceneManager.callFunc("pushScene", group)
|
||||
|
||||
return group
|
||||
end function
|
||||
|
||||
function CreatePersonView(personData as object) as object
|
||||
person = CreateObject("roSGNode", "PersonDetails")
|
||||
m.global.SceneManager.callFunc("pushScene", person)
|
||||
|
|
|
@ -22,15 +22,15 @@ end function
|
|||
sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -1, playbackPosition = -1, forceTranscoding = false, showIntro = true, allowResumeDialog = true)
|
||||
video.content = createObject("RoSGNode", "ContentNode")
|
||||
meta = ItemMetaData(video.id)
|
||||
m.videotype = meta.type
|
||||
if meta = invalid
|
||||
video.content = invalid
|
||||
return
|
||||
end if
|
||||
m.videotype = meta.type
|
||||
|
||||
' Special handling for "Programs" or "Vidoes" launched from "On Now" or elsewhere on the home screen...
|
||||
' basically anything that is a Live Channel.
|
||||
if meta.json.ChannelId <> invalid
|
||||
if isValid(meta?.json?.ChannelId)
|
||||
if meta.json.EpisodeTitle <> invalid
|
||||
meta.title = meta.json.EpisodeTitle
|
||||
else if meta.json.Name <> invalid
|
||||
|
@ -53,7 +53,7 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
|
|||
video.content.title = meta.title
|
||||
video.showID = meta.showID
|
||||
|
||||
if playbackPosition = -1
|
||||
if playbackPosition = -1 and isValid(meta.json)
|
||||
playbackPosition = meta.json.UserData.PlaybackPositionTicks
|
||||
if allowResumeDialog
|
||||
if playbackPosition > 0
|
||||
|
@ -175,7 +175,17 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
|
|||
if mediaSourceId = invalid
|
||||
mediaSourceId = video.id
|
||||
end if
|
||||
if meta.live then mediaSourceId = "" ' Don't send mediaSourceId for Live media
|
||||
|
||||
' Don't send mediaSourceId for Live Media
|
||||
' Note: Recordings in progress will have meta.live = invalid, but we still don't want to send mediaSourceId
|
||||
if not isValid(meta.live)
|
||||
meta.live = false
|
||||
mediaSourceId = ""
|
||||
else
|
||||
if meta.live
|
||||
mediaSourceId = ""
|
||||
end if
|
||||
end if
|
||||
|
||||
m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
|
||||
video.videoId = video.id
|
||||
|
@ -197,7 +207,7 @@ sub AddVideoContent(video, mediaSourceId, audio_stream_idx = 1, subtitle_idx = -
|
|||
|
||||
video.container = getContainerType(meta)
|
||||
|
||||
if m.playbackInfo.MediaSources[0] = invalid
|
||||
if not isValid(m.playbackInfo.MediaSources[0]) and isValid(meta.json)
|
||||
m.playbackInfo = meta.json
|
||||
end if
|
||||
|
||||
|
@ -395,7 +405,7 @@ end function
|
|||
|
||||
function getContainerType(meta as object) as string
|
||||
' Determine the file type of the video file source
|
||||
if meta.json.mediaSources = invalid then return ""
|
||||
if not IsValid(meta.json) or not isValid(meta.json.mediaSources) then return ""
|
||||
|
||||
container = meta.json.mediaSources[0].container
|
||||
if container = invalid
|
||||
|
|
|
@ -147,15 +147,19 @@ function ItemMetaData(id as string)
|
|||
tmp = CreateObject("roSGNode", "MusicSongData")
|
||||
|
||||
' Try using song's parent for poster image
|
||||
tmp.image = PosterImage(data.ParentId)
|
||||
tmp.image = PosterImage(data.ParentId, { "MaxWidth": 500, "MaxHeight": 500 })
|
||||
|
||||
' Song's parent poster image is no good, try using the song's poster image
|
||||
if tmp.image = invalid
|
||||
tmp.image = PosterImage(data.id)
|
||||
tmp.image = PosterImage(data.id, { "MaxWidth": 500, "MaxHeight": 500 })
|
||||
end if
|
||||
|
||||
tmp.json = data
|
||||
return tmp
|
||||
else if data.type = "Recording"
|
||||
' We know it's "Recording", but we don't do any special preprocessing
|
||||
' for this data type at the moment, so just return the json.
|
||||
return data
|
||||
else
|
||||
print "Items.brs::ItemMetaData processed unhandled type: " data.type
|
||||
' Return json if we don't know what it is
|
||||
|
@ -369,10 +373,6 @@ function TVSeasons(id as string)
|
|||
results = []
|
||||
for each item in data.Items
|
||||
imgParams = { "AddPlayedIndicator": item.UserData.Played }
|
||||
if item.UserData.UnplayedItemCount > 0
|
||||
param = { "UnplayedCount": item.UserData.UnplayedItemCount }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
tmp = CreateObject("roSGNode", "TVEpisodeData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
tmp.json = item
|
||||
|
@ -389,11 +389,7 @@ function TVEpisodes(show_id as string, season_id as string)
|
|||
data = getJson(resp)
|
||||
results = []
|
||||
for each item in data.Items
|
||||
imgParams = { "AddPlayedIndicator": item.UserData.Played, "maxWidth": 400, "maxheight": 250 }
|
||||
if item.UserData.PlayedPercentage <> invalid
|
||||
param = { "PercentPlayed": item.UserData.PlayedPercentage }
|
||||
imgParams.Append(param)
|
||||
end if
|
||||
imgParams = { "maxWidth": 400, "maxheight": 250 }
|
||||
tmp = CreateObject("roSGNode", "TVEpisodeData")
|
||||
tmp.image = PosterImage(item.id, imgParams)
|
||||
if tmp.image <> invalid
|
||||
|
|
98
source/static/whatsNew.json
Normal file
98
source/static/whatsNew.json
Normal file
|
@ -0,0 +1,98 @@
|
|||
[
|
||||
{
|
||||
"description": "Bug Fix: Don't crash when viewing in progress recording.",
|
||||
"author": "jimdogx"
|
||||
},
|
||||
{
|
||||
"description": "New Setting: Disable Community Rating for Episodes of TV shows.",
|
||||
"author": "dezchai"
|
||||
},
|
||||
{
|
||||
"description": "New View: Music Grid View",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "New Setting: Movie Grid Title Display",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Updated View: TV Library Genre View",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Bug Fix: Fix Crash When Options Button Pressed, but Options Are Not Available",
|
||||
"author": "ApexArray"
|
||||
},
|
||||
{
|
||||
"description": "Bug Fix: Fix Crash When Invalid Deep Link Arguments Are Passed",
|
||||
"author": "ApexArray"
|
||||
},
|
||||
{
|
||||
"description": "New Setting: Go straight to episode listing (if only 1 season available)",
|
||||
"author": "ApexArray"
|
||||
},
|
||||
{
|
||||
"description": "Feature: Create What's New Popup that tells users what's changed the first time they run a new version.",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Feature: Toggle video playback when OK button is pressed",
|
||||
"author": "ApexArray"
|
||||
},
|
||||
{
|
||||
"description": "Bug Fix: Fix TV Season List Crash when showing 27+ Episodes",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "New View: Create Movie Grid View and new default setting",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Core: Set Maximum Bitrates values based on Roku Guidelines",
|
||||
"author": "ApexArray"
|
||||
},
|
||||
{
|
||||
"description": "Core: Reorganize Roku Settings",
|
||||
"author": "sevenrats"
|
||||
},
|
||||
{
|
||||
"description": "Updated View: Add resumable filter to Movies",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Updated View: Filter Movies & Series by Played/Unplayed status",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "New Setting: Hide subtitles that might transcode",
|
||||
"author": "sevenrats"
|
||||
},
|
||||
{
|
||||
"description": "Bug Fix: Show Unwatched Episode Count",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "New View: Add Artist Presentation View & Genre View to Music Library",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Core: Draw Homepage Continue Watching percent complete bar instead of using image API",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Bug Fix: Fix TV Episode focus issues",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Bug Fix: Fix Movie Detail View lost focus bug when going back from resume dialog",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Core: Add new questions to GitHub bug template",
|
||||
"author": "1hitsong"
|
||||
},
|
||||
{
|
||||
"description": "Core: Create Queue Manager and use for Music",
|
||||
"author": "1hitsong"
|
||||
}
|
||||
]
|
|
@ -21,7 +21,7 @@ end function
|
|||
' returns the server-side track index for the appriate subtitle
|
||||
function defaultSubtitleTrackFromVid(video_id) as integer
|
||||
meta = ItemMetaData(video_id)
|
||||
if meta.json.mediaSources <> invalid
|
||||
if meta?.json?.mediaSources <> invalid
|
||||
subtitles = sortSubtitles(meta.id, meta.json.MediaSources[0].MediaStreams)
|
||||
default_text_subs = defaultSubtitleTrack(subtitles["all"], true) ' Find correct subtitle track (forced text)
|
||||
if default_text_subs <> -1
|
||||
|
|
|
@ -170,6 +170,13 @@ function getDeviceProfile() as object
|
|||
"Property": "VideoLevel",
|
||||
"Value": "41",
|
||||
"IsRequired": false
|
||||
},
|
||||
' Roku only supports h264 up to 10Mpbs
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoBitrate",
|
||||
"Value": "10000000",
|
||||
IsRequired: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -203,6 +210,13 @@ function getDeviceProfile() as object
|
|||
"Property": "VideoRangeType",
|
||||
"Value": av1VideoRangeTypes,
|
||||
"IsRequired": false
|
||||
},
|
||||
' Roku only supports AVI up to 40Mpbs
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoBitrate",
|
||||
"Value": "40000000",
|
||||
IsRequired: true
|
||||
}
|
||||
]
|
||||
})
|
||||
|
@ -229,6 +243,13 @@ function getDeviceProfile() as object
|
|||
"Property": "VideoLevel",
|
||||
"Value": (120 * 5.1).ToStr(),
|
||||
"IsRequired": false
|
||||
},
|
||||
' Roku only supports h265 up to 40Mpbs
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoBitrate",
|
||||
"Value": "40000000",
|
||||
IsRequired: true
|
||||
}
|
||||
]
|
||||
})
|
||||
|
@ -243,6 +264,13 @@ function getDeviceProfile() as object
|
|||
"Property": "VideoRangeType",
|
||||
"Value": vp9VideoRangeTypes,
|
||||
"IsRequired": false
|
||||
},
|
||||
' Roku only supports VP9 up to 40Mpbs
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoBitrate",
|
||||
"Value": "40000000",
|
||||
IsRequired: true
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
|
@ -91,6 +91,12 @@ function get_dialog_result(dialog, port)
|
|||
end function
|
||||
|
||||
function lastFocusedChild(obj as object) as object
|
||||
if LCase(obj.focusedChild.focusedChild.subType()) = "tvepisodes"
|
||||
if isValid(obj?.focusedChild?.focusedChild?.lastFocus)
|
||||
return obj.focusedChild.focusedChild.lastFocus
|
||||
end if
|
||||
end if
|
||||
|
||||
child = obj
|
||||
for i = 0 to obj.getChildCount()
|
||||
if obj.focusedChild <> invalid
|
||||
|
@ -189,6 +195,24 @@ function isValid(input) as boolean
|
|||
return input <> invalid
|
||||
end function
|
||||
|
||||
' Returns whether or not passed value is valid and not empty
|
||||
' Accepts a string, or any countable type (arrays and lists)
|
||||
function isValidAndNotEmpty(input) as boolean
|
||||
if not isValid(input) then return false
|
||||
' Use roAssociativeArray instead of list so we get access to the doesExist() method
|
||||
countableTypes = { "array": 1, "list": 1, "roarray": 1, "roassociativearray": 1, "rolist": 1 }
|
||||
inputType = LCase(type(input))
|
||||
if inputType = "string" or inputType = "rostring"
|
||||
trimmedInput = input.trim()
|
||||
return trimmedInput <> ""
|
||||
else if countableTypes.doesExist(inputType)
|
||||
return input.count() > 0
|
||||
else
|
||||
print "Called isValidAndNotEmpty() with invalid type: ", inputType
|
||||
return false
|
||||
end if
|
||||
end function
|
||||
|
||||
' Rounds number to nearest integer
|
||||
function roundNumber(f as float) as integer
|
||||
' BrightScript only has a "floor" round
|
||||
|
|
Loading…
Reference in New Issue
Block a user