mirror of
https://github.com/jellyfin/jellyfin-roku.git
synced 2024-11-22 21:59:39 +00:00
Merge branch 'master' into 223-merge-conflicts
This commit is contained in:
commit
ff0c444920
4
.github/workflows/build-dev.yml
vendored
4
.github/workflows/build-dev.yml
vendored
@ -10,8 +10,8 @@ jobs:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "npm"
|
||||
|
2
.github/workflows/build-docs.yml
vendored
2
.github/workflows/build-docs.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
# Give the default GITHUB_TOKEN write permission to commit and push the changed files back to the repository.
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
|
4
.github/workflows/build-prod.yml
vendored
4
.github/workflows/build-prod.yml
vendored
@ -12,8 +12,8 @@ jobs:
|
||||
if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'release-prep') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "npm"
|
||||
|
12
.github/workflows/bump-version.yml
vendored
12
.github/workflows/bump-version.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
steps:
|
||||
# Setup
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- name: Install required packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
@ -50,7 +50,7 @@ jobs:
|
||||
run: echo "targetBranch=${{ env.bugfixBranch }}" >> $GITHUB_ENV
|
||||
- name: Checkout bugfix branch
|
||||
if: github.event.inputs.targetBranch == 'bugfix'
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
ref: ${{ env.targetBranch }}
|
||||
# Save old version again if needed
|
||||
@ -101,7 +101,7 @@ jobs:
|
||||
steps:
|
||||
# Setup
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- name: Install jq to update json
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
@ -125,7 +125,7 @@ jobs:
|
||||
run: echo "targetBranch=${{ env.bugfixBranch }}" >> $GITHUB_ENV
|
||||
- name: Checkout bugfix branch
|
||||
if: github.event.inputs.targetBranch == 'bugfix'
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
ref: ${{ env.targetBranch }}
|
||||
# Calculate new version
|
||||
@ -169,7 +169,7 @@ jobs:
|
||||
steps:
|
||||
# Setup
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- name: Install jq to update json
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
@ -193,7 +193,7 @@ jobs:
|
||||
run: echo "targetBranch=${{ env.bugfixBranch }}" >> $GITHUB_ENV
|
||||
- name: Checkout bugfix branch
|
||||
if: github.event.inputs.targetBranch == 'bugfix'
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
ref: ${{ env.targetBranch }}
|
||||
# Calculate new version
|
||||
|
2
.github/workflows/deploy-api-docs.yml
vendored
2
.github/workflows/deploy-api-docs.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
|
||||
- name: Upload artifact
|
||||
|
6
.github/workflows/roku-analysis.yml
vendored
6
.github/workflows/roku-analysis.yml
vendored
@ -12,8 +12,8 @@ jobs:
|
||||
if: github.repository == 'jellyfin/jellyfin-roku' && github.event_name != 'pull_request' || github.repository == 'jellyfin/jellyfin-roku' && github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4
|
||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
|
||||
with:
|
||||
node-version: "lts/*"
|
||||
cache: "npm"
|
||||
@ -28,7 +28,7 @@ jobs:
|
||||
if: env.BRANCH_NAME == 'master'
|
||||
run: npm run build-prod
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4
|
||||
uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "21"
|
||||
|
@ -115,6 +115,21 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
|
||||
end if
|
||||
end if
|
||||
|
||||
if videotype = "tvchannel" and isValid(meta.json) and isValid(meta.json.CurrentProgram)
|
||||
if isValid(meta.json.CurrentProgram.Name)
|
||||
meta.title = `${meta.title}: ${meta.json.CurrentProgram.Name}`
|
||||
end if
|
||||
if isValid(meta.json.CurrentProgram.ParentIndexNumber)
|
||||
video.seasonNumber = meta.json.CurrentProgram.ParentIndexNumber
|
||||
end if
|
||||
if isValid(meta.json.CurrentProgram.IndexNumber)
|
||||
video.episodeNumber = meta.json.CurrentProgram.IndexNumber
|
||||
end if
|
||||
if isValid(meta.json.CurrentProgram.IndexNumberEnd)
|
||||
video.episodeNumberEnd = meta.json.CurrentProgram.IndexNumberEnd
|
||||
end if
|
||||
end if
|
||||
|
||||
video.chapters = meta.json.Chapters
|
||||
video.content.title = meta.title
|
||||
video.showID = meta.showID
|
||||
|
@ -5,7 +5,11 @@ import "pkg:/source/utils/config.bs"
|
||||
sub setFields()
|
||||
json = m.top.json
|
||||
m.top.id = json.id
|
||||
m.top.title = json.name
|
||||
if isValid(json.number)
|
||||
m.top.title = `${tr("CH")} ${json.number} ${json.name}`
|
||||
else
|
||||
m.top.title = json.name
|
||||
end if
|
||||
m.top.live = true
|
||||
m.top.Type = "TvChannel"
|
||||
setPoster()
|
||||
|
@ -35,6 +35,7 @@ sub updateSize()
|
||||
end sub
|
||||
|
||||
sub loadParts(data as object)
|
||||
m.extrasGrp = m.top.getParent().findNode("extrasGrp")
|
||||
m.top.parentId = data.id
|
||||
m.people = data.People
|
||||
m.LoadAdditionalPartsTask.itemId = m.top.parentId
|
||||
@ -42,6 +43,7 @@ sub loadParts(data as object)
|
||||
end sub
|
||||
|
||||
sub loadPersonVideos(personId)
|
||||
m.extrasGrp = m.top.getParent().findNode("extrasGrp")
|
||||
m.personId = personId
|
||||
m.LoadMoviesTask.itemId = m.personId
|
||||
m.LoadMoviesTask.observeField("content", "onMoviesLoaded")
|
||||
@ -114,7 +116,7 @@ sub onLikeThisLoaded()
|
||||
m.SpecialFeaturesTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
function onSpecialFeaturesLoaded()
|
||||
sub onSpecialFeaturesLoaded()
|
||||
data = m.SpecialFeaturesTask.content
|
||||
m.SpecialFeaturesTask.unobserveField("content")
|
||||
if data <> invalid and data.count() > 0
|
||||
@ -132,8 +134,8 @@ function onSpecialFeaturesLoaded()
|
||||
addRowSize([462, 372])
|
||||
end if
|
||||
|
||||
return m.top.content
|
||||
end function
|
||||
showOrHideMe()
|
||||
end sub
|
||||
|
||||
sub onMoviesLoaded()
|
||||
data = m.LoadMoviesTask.content
|
||||
@ -179,6 +181,8 @@ sub onSeriesLoaded()
|
||||
m.top.content.appendChild(row)
|
||||
end if
|
||||
m.top.visible = true
|
||||
|
||||
showOrHideMe()
|
||||
end sub
|
||||
|
||||
function buildRow(rowTitle as string, items, imgWdth = 0)
|
||||
@ -218,6 +222,17 @@ sub addRowSize(newRow)
|
||||
m.top.rowItemSize = newSizeArray
|
||||
end sub
|
||||
|
||||
' don't show popup panel if there is nothing to show
|
||||
sub showOrHideMe()
|
||||
if isValid(m.top.content)
|
||||
if m.top.content.getChildCount() = 0
|
||||
m.extrasGrp.visible = false
|
||||
else
|
||||
m.extrasGrp.visible = true
|
||||
end if
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub onRowItemSelected()
|
||||
m.top.selectedItem = m.top.content.getChild(m.top.rowItemSelected[0]).getChild(m.top.rowItemSelected[1])
|
||||
end sub
|
||||
|
@ -51,20 +51,30 @@ sub loadLibraries()
|
||||
end sub
|
||||
|
||||
sub updateSize()
|
||||
m.top.translation = [111, 180]
|
||||
itemHeight = 330
|
||||
uiRowLayout = m.global.session.user.settings["ui.row.layout"]
|
||||
|
||||
'Set width of Rows to cut off at edge of Safe Zone
|
||||
m.top.itemSize = [1703, itemHeight]
|
||||
if isValid(uiRowLayout)
|
||||
if uiRowLayout = "fullwidth"
|
||||
m.top.translation = [0, 180]
|
||||
' rows take up full width of the screen
|
||||
m.top.itemSize = [1920, 330]
|
||||
' align with edge of "action" safe zone
|
||||
m.top.focusXOffset = [96]
|
||||
m.top.rowLabelOffset = [96, 20]
|
||||
else
|
||||
' original layout
|
||||
m.top.translation = [111, 180]
|
||||
m.top.itemSize = [1703, 330]
|
||||
' reset to defaults
|
||||
m.top.focusXOffset = []
|
||||
m.top.rowLabelOffset = [0, 20]
|
||||
end if
|
||||
end if
|
||||
|
||||
' spacing between rows
|
||||
m.top.itemSpacing = [0, 105]
|
||||
|
||||
' spacing between items in a row
|
||||
m.top.rowItemSpacing = [20, 0]
|
||||
|
||||
' Default size to wide poster, the most used size
|
||||
m.top.rowItemSize = homeRowItemSizes.WIDE_POSTER
|
||||
m.top.rowItemSpacing = [21, 0]
|
||||
|
||||
m.top.visible = true
|
||||
end sub
|
||||
@ -455,6 +465,8 @@ end sub
|
||||
sub updateHomeRows()
|
||||
' Hide the row counter to prevent flicker. We'll show it once loading timer fires
|
||||
m.top.showRowCounter = [false]
|
||||
m.top.visible = false
|
||||
updateSize()
|
||||
processUserSections()
|
||||
end sub
|
||||
|
||||
@ -687,6 +699,7 @@ end sub
|
||||
sub itemSelected()
|
||||
m.selectedRowItem = m.top.rowItemSelected
|
||||
|
||||
m.global.launchSource = "home"
|
||||
m.top.selectedItem = m.top.content.getChild(m.top.rowItemSelected[0]).getChild(m.top.rowItemSelected[1])
|
||||
|
||||
'Prevent the selected item event from double firing
|
||||
|
@ -403,7 +403,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
m.options.setFocus(true)
|
||||
end if
|
||||
|
||||
if key = "down" and m.buttonGrp.isInFocusChain()
|
||||
if key = "down" and m.buttonGrp.isInFocusChain() and m.extrasGrp.visible = true
|
||||
m.top.lastFocus = m.extrasGrid
|
||||
m.extrasGrid.setFocus(true)
|
||||
m.top.findNode("VertSlider").reverse = false
|
||||
|
@ -6,11 +6,28 @@ sub init()
|
||||
|
||||
m.top.showRowLabel = [false]
|
||||
|
||||
m.top.observeField("selectItemId", "onItemSelected")
|
||||
|
||||
updateSize()
|
||||
|
||||
m.top.setFocus(true)
|
||||
end sub
|
||||
|
||||
sub onItemSelected()
|
||||
Id = m.top.selectItemId
|
||||
|
||||
if Id <> invalid and Id <> "" and m.top.objects <> invalid
|
||||
for i = 0 to m.top.objects.items.count() - 1
|
||||
item = m.top.objects.items[i]
|
||||
if item.id = Id
|
||||
m.top.jumpToItem = i
|
||||
return
|
||||
end if
|
||||
end for
|
||||
end if
|
||||
end sub
|
||||
|
||||
|
||||
sub updateSize()
|
||||
m.top.translation = [450, 180]
|
||||
|
||||
|
@ -4,5 +4,8 @@
|
||||
<field id="objects" type="assocarray" onChange="setupRows" />
|
||||
<field id="escapeButton" type="string" alwaysNotify="true" />
|
||||
<field id="doneLoading" type="boolean" />
|
||||
<field id="selectItemId" type="string" />
|
||||
|
||||
<function name="onItemSelected" />
|
||||
</interface>
|
||||
</component>
|
@ -4,9 +4,15 @@ sub init()
|
||||
m.rows = m.top.findNode("tvEpisodeRow")
|
||||
m.tvListOptions = m.top.findNode("tvListOptions")
|
||||
|
||||
m.top.observeField("selectItemId", "onItemSelected")
|
||||
m.rows.observeField("doneLoading", "rowsDoneLoading")
|
||||
end sub
|
||||
|
||||
sub onItemSelected()
|
||||
Id = m.top.selectItemId
|
||||
m.rows.selectItemId = Id
|
||||
end sub
|
||||
|
||||
sub setupRows()
|
||||
objects = m.top.objects
|
||||
m.rows.objects = objects
|
||||
|
@ -8,5 +8,8 @@
|
||||
<field id="objects" type="assocarray" onChange="setupRows" />
|
||||
<field id="itemSelected" type="integer" />
|
||||
<field id="doneLoading" type="boolean" />
|
||||
<field id="selectItemId" type="string" />
|
||||
|
||||
<function name="onItemSelected" />
|
||||
</interface>
|
||||
</component>
|
@ -6,6 +6,7 @@ import "pkg:/source/api/sdk.bs"
|
||||
|
||||
sub init()
|
||||
m.top.optionsAvailable = false
|
||||
m.top.observeField("selectItemId", "onItemSelected")
|
||||
|
||||
m.rows = m.top.findNode("picker")
|
||||
m.poster = m.top.findNode("seasonPoster")
|
||||
@ -25,6 +26,11 @@ sub setSeasonLoading()
|
||||
m.top.overhangTitle = tr("Loading...")
|
||||
end sub
|
||||
|
||||
sub onItemSelected()
|
||||
Id = m.top.selectItemId
|
||||
m.rows.selectItemId = Id
|
||||
end sub
|
||||
|
||||
' Updates the visibility of the Extras button based on if this season has any extra features
|
||||
sub setExtraButtonVisibility()
|
||||
if isValid(m.top.extrasObjects) and isValidAndNotEmpty(m.top.extrasObjects.items)
|
||||
@ -73,6 +79,7 @@ end function
|
||||
' OnScreenShown: Callback function when view is presented on screen
|
||||
'
|
||||
sub OnScreenShown()
|
||||
|
||||
if m.isFirstRun
|
||||
m.isFirstRun = false
|
||||
return
|
||||
@ -80,6 +87,7 @@ sub OnScreenShown()
|
||||
|
||||
m.tvEpisodeRow.setFocus(true)
|
||||
m.top.refreshSeasonDetailsData = not m.top.refreshSeasonDetailsData
|
||||
|
||||
end sub
|
||||
|
||||
' Handle navigation input from the remote and act on it
|
||||
|
@ -19,6 +19,9 @@
|
||||
<field id="objects" alias="picker.objects" />
|
||||
<field id="episodeObjects" type="assocarray" />
|
||||
<field id="extrasObjects" type="assocarray" onChange="setExtraButtonVisibility" />
|
||||
<field id="selectItemId" type="string" />
|
||||
|
||||
<function name="updateSeason" />
|
||||
<function name="onItemSelected" />
|
||||
</interface>
|
||||
</component>
|
@ -41,7 +41,21 @@ end sub
|
||||
'
|
||||
sub onProgressPercentageChanged()
|
||||
m.videoPositionTime.text = secondsToHuman(m.top.positionTime, true)
|
||||
m.videoRemainingTime.text = secondsToHuman(m.top.remainingPositionTime, true)
|
||||
|
||||
osdmode = m.global.session.user.settings["ui.general.osdremainingtime"]
|
||||
if m.global.session.user.settings["ui.design.hideclock"]
|
||||
' in order to honor the hide clocks setting
|
||||
osdmode = "remaining"
|
||||
end if
|
||||
|
||||
if osdmode = "remaining"
|
||||
m.videoRemainingTime.text = secondsToHuman(m.top.remainingPositionTime, true)
|
||||
else if osdmode = "timeofday"
|
||||
m.videoRemainingTime.text = secondsToEndTime(m.top.remainingPositionTime)
|
||||
else if osdmode = "both"
|
||||
m.videoRemainingTime.text = secondsToHuman(m.top.remainingPositionTime, true) + " | " + secondsToEndTime(m.top.remainingPositionTime)
|
||||
end if
|
||||
|
||||
m.progressBar.width = m.progressBarBackground.width * m.top.progressPercentage
|
||||
end sub
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
</Rectangle>
|
||||
|
||||
<Label id="videoPositionTime" font="font:MediumSystemFont" color="0xffffffFF" translation="[103,985]" />
|
||||
<Label id="videoRemainingTime" font="font:MediumSystemFont" color="0xffffffFF" horizAlign="right" width="200" translation="[1617,985]" />
|
||||
<Label id="videoRemainingTime" font="font:MediumSystemFont" color="0xffffffFF" horizAlign="right" width="400" translation="[1450,985]" />
|
||||
|
||||
<Timer id="inactivityTimer" duration="1" repeat="true" />
|
||||
</children>
|
||||
|
@ -412,7 +412,7 @@ sub onVideoContentLoaded()
|
||||
m.osd.itemTitleText = m.top.content.title
|
||||
|
||||
' If video is an episode, attempt to add season and episode numbers to OSD
|
||||
if m.top.content.contenttype = 4
|
||||
if m.top.content.contenttype = 4 or m.top.content.live
|
||||
if isValid(videoContent[0].seasonNumber)
|
||||
m.osd.seasonNumber = videoContent[0].seasonNumber
|
||||
end if
|
||||
|
@ -304,7 +304,7 @@ sub setTvShowsOptions(options)
|
||||
{ "Title": tr("TITLE"), "Name": "SortName" },
|
||||
{ "Title": tr("IMDB_RATING"), "Name": "CommunityRating" },
|
||||
{ "Title": tr("DATE_ADDED"), "Name": "DateCreated" },
|
||||
{ "Title": tr("DATE_PLAYED"), "Name": "DatePlayed" },
|
||||
{ "Title": tr("DATE_PLAYED"), "Name": "SeriesDatePlayed" },
|
||||
{ "Title": tr("OFFICIAL_RATING"), "Name": "OfficialRating" },
|
||||
{ "Title": tr("RELEASE_DATE"), "Name": "PremiereDate" },
|
||||
{ "Title": tr("Random"), "Name": "Random" },
|
||||
|
@ -117,6 +117,21 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
|
||||
end if
|
||||
end if
|
||||
|
||||
if videotype = "tvchannel" and isValid(meta.json) and isValid(meta.json.CurrentProgram)
|
||||
if isValid(meta.json.CurrentProgram.Name)
|
||||
meta.title = `${meta.title}: ${meta.json.CurrentProgram.Name}`
|
||||
end if
|
||||
if isValid(meta.json.CurrentProgram.ParentIndexNumber)
|
||||
video.seasonNumber = meta.json.CurrentProgram.ParentIndexNumber
|
||||
end if
|
||||
if isValid(meta.json.CurrentProgram.IndexNumber)
|
||||
video.episodeNumber = meta.json.CurrentProgram.IndexNumber
|
||||
end if
|
||||
if isValid(meta.json.CurrentProgram.IndexNumberEnd)
|
||||
video.episodeNumberEnd = meta.json.CurrentProgram.IndexNumberEnd
|
||||
end if
|
||||
end if
|
||||
|
||||
video.chapters = meta.json.Chapters
|
||||
video.content.title = meta.title
|
||||
video.showID = meta.showID
|
||||
|
@ -7,7 +7,11 @@ import "pkg:/source/utils/config.bs"
|
||||
sub setFields()
|
||||
json = m.top.json
|
||||
m.top.id = json.id
|
||||
m.top.title = json.name
|
||||
if isValid(json.number)
|
||||
m.top.title = `${tr("CH")} ${json.number} ${json.name}`
|
||||
else
|
||||
m.top.title = json.name
|
||||
end if
|
||||
m.top.live = true
|
||||
m.top.Type = "TvChannel"
|
||||
setPoster()
|
||||
|
@ -37,6 +37,7 @@ sub updateSize()
|
||||
end sub
|
||||
|
||||
sub loadParts(data as object)
|
||||
m.extrasGrp = m.top.getParent().findNode("extrasGrp")
|
||||
m.top.parentId = data.id
|
||||
m.people = data.People
|
||||
m.LoadAdditionalPartsTask.itemId = m.top.parentId
|
||||
@ -44,6 +45,7 @@ sub loadParts(data as object)
|
||||
end sub
|
||||
|
||||
sub loadPersonVideos(personId)
|
||||
m.extrasGrp = m.top.getParent().findNode("extrasGrp")
|
||||
m.personId = personId
|
||||
m.LoadMoviesTask.itemId = m.personId
|
||||
m.LoadMoviesTask.observeField("content", "onMoviesLoaded")
|
||||
@ -116,7 +118,7 @@ sub onLikeThisLoaded()
|
||||
m.SpecialFeaturesTask.control = "RUN"
|
||||
end sub
|
||||
|
||||
function onSpecialFeaturesLoaded()
|
||||
sub onSpecialFeaturesLoaded()
|
||||
data = m.SpecialFeaturesTask.content
|
||||
m.SpecialFeaturesTask.unobserveField("content")
|
||||
if data <> invalid and data.count() > 0
|
||||
@ -134,8 +136,8 @@ function onSpecialFeaturesLoaded()
|
||||
addRowSize([462, 372])
|
||||
end if
|
||||
|
||||
return m.top.content
|
||||
end function
|
||||
showOrHideMe()
|
||||
end sub
|
||||
|
||||
sub onMoviesLoaded()
|
||||
data = m.LoadMoviesTask.content
|
||||
@ -181,6 +183,8 @@ sub onSeriesLoaded()
|
||||
m.top.content.appendChild(row)
|
||||
end if
|
||||
m.top.visible = true
|
||||
|
||||
showOrHideMe()
|
||||
end sub
|
||||
|
||||
function buildRow(rowTitle as string, items, imgWdth = 0)
|
||||
@ -220,6 +224,17 @@ sub addRowSize(newRow)
|
||||
m.top.rowItemSize = newSizeArray
|
||||
end sub
|
||||
|
||||
' don't show popup panel if there is nothing to show
|
||||
sub showOrHideMe()
|
||||
if isValid(m.top.content)
|
||||
if m.top.content.getChildCount() = 0
|
||||
m.extrasGrp.visible = false
|
||||
else
|
||||
m.extrasGrp.visible = true
|
||||
end if
|
||||
end if
|
||||
end sub
|
||||
|
||||
sub onRowItemSelected()
|
||||
m.top.selectedItem = m.top.content.getChild(m.top.rowItemSelected[0]).getChild(m.top.rowItemSelected[1])
|
||||
end sub
|
||||
|
@ -53,20 +53,30 @@ sub loadLibraries()
|
||||
end sub
|
||||
|
||||
sub updateSize()
|
||||
m.top.translation = [111, 180]
|
||||
itemHeight = 330
|
||||
uiRowLayout = m.global.session.user.settings["ui.row.layout"]
|
||||
|
||||
'Set width of Rows to cut off at edge of Safe Zone
|
||||
m.top.itemSize = [1703, itemHeight]
|
||||
if isValid(uiRowLayout)
|
||||
if uiRowLayout = "fullwidth"
|
||||
m.top.translation = [0, 180]
|
||||
' rows take up full width of the screen
|
||||
m.top.itemSize = [1920, 330]
|
||||
' align with edge of "action" safe zone
|
||||
m.top.focusXOffset = [96]
|
||||
m.top.rowLabelOffset = [96, 20]
|
||||
else
|
||||
' original layout
|
||||
m.top.translation = [111, 180]
|
||||
m.top.itemSize = [1703, 330]
|
||||
' reset to defaults
|
||||
m.top.focusXOffset = []
|
||||
m.top.rowLabelOffset = [0, 20]
|
||||
end if
|
||||
end if
|
||||
|
||||
' spacing between rows
|
||||
m.top.itemSpacing = [0, 105]
|
||||
|
||||
' spacing between items in a row
|
||||
m.top.rowItemSpacing = [20, 0]
|
||||
|
||||
' Default size to wide poster, the most used size
|
||||
m.top.rowItemSize = homeRowItemSizes.WIDE_POSTER
|
||||
m.top.rowItemSpacing = [21, 0]
|
||||
|
||||
m.top.visible = true
|
||||
end sub
|
||||
@ -457,6 +467,8 @@ end sub
|
||||
sub updateHomeRows()
|
||||
' Hide the row counter to prevent flicker. We'll show it once loading timer fires
|
||||
m.top.showRowCounter = [false]
|
||||
m.top.visible = false
|
||||
updateSize()
|
||||
processUserSections()
|
||||
end sub
|
||||
|
||||
@ -689,6 +701,7 @@ end sub
|
||||
sub itemSelected()
|
||||
m.selectedRowItem = m.top.rowItemSelected
|
||||
|
||||
m.global.launchSource = "home"
|
||||
m.top.selectedItem = m.top.content.getChild(m.top.rowItemSelected[0]).getChild(m.top.rowItemSelected[1])
|
||||
|
||||
'Prevent the selected item event from double firing
|
||||
|
@ -75,6 +75,7 @@ sub loadItems()
|
||||
params["DisableFirstEpisode"] = false
|
||||
params["limit"] = 24
|
||||
params["EnableTotalRecordCount"] = false
|
||||
params["EnableResumable"] = false
|
||||
|
||||
maxDaysInNextUp = userSettings["ui.details.maxdaysnextup"].ToInt()
|
||||
if isValid(maxDaysInNextUp)
|
||||
|
@ -405,7 +405,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
||||
m.options.setFocus(true)
|
||||
end if
|
||||
|
||||
if key = "down" and m.buttonGrp.isInFocusChain()
|
||||
if key = "down" and m.buttonGrp.isInFocusChain() and m.extrasGrp.visible = true
|
||||
m.top.lastFocus = m.extrasGrid
|
||||
m.extrasGrid.setFocus(true)
|
||||
m.top.findNode("VertSlider").reverse = false
|
||||
|
@ -8,11 +8,28 @@
|
||||
|
||||
m.top.showRowLabel = [false]
|
||||
|
||||
m.top.observeField("selectItemId", "onItemSelected")
|
||||
|
||||
updateSize()
|
||||
|
||||
m.top.setFocus(true)
|
||||
end sub
|
||||
|
||||
sub onItemSelected()
|
||||
Id = m.top.selectItemId
|
||||
|
||||
if Id <> invalid and Id <> "" and m.top.objects <> invalid
|
||||
for i = 0 to m.top.objects.items.count() - 1
|
||||
item = m.top.objects.items[i]
|
||||
if item.id = Id
|
||||
m.top.jumpToItem = i
|
||||
return
|
||||
end if
|
||||
end for
|
||||
end if
|
||||
end sub
|
||||
|
||||
|
||||
sub updateSize()
|
||||
m.top.translation = [450, 180]
|
||||
|
||||
|
@ -6,9 +6,15 @@ sub init()
|
||||
m.rows = m.top.findNode("tvEpisodeRow")
|
||||
m.tvListOptions = m.top.findNode("tvListOptions")
|
||||
|
||||
m.top.observeField("selectItemId", "onItemSelected")
|
||||
m.rows.observeField("doneLoading", "rowsDoneLoading")
|
||||
end sub
|
||||
|
||||
sub onItemSelected()
|
||||
Id = m.top.selectItemId
|
||||
m.rows.selectItemId = Id
|
||||
end sub
|
||||
|
||||
sub setupRows()
|
||||
objects = m.top.objects
|
||||
m.rows.objects = objects
|
||||
|
@ -8,6 +8,7 @@ import "pkg:/source/api/sdk.bs"
|
||||
|
||||
sub init()
|
||||
m.top.optionsAvailable = false
|
||||
m.top.observeField("selectItemId", "onItemSelected")
|
||||
|
||||
m.rows = m.top.findNode("picker")
|
||||
m.poster = m.top.findNode("seasonPoster")
|
||||
@ -27,6 +28,11 @@ sub setSeasonLoading()
|
||||
m.top.overhangTitle = tr("Loading...")
|
||||
end sub
|
||||
|
||||
sub onItemSelected()
|
||||
Id = m.top.selectItemId
|
||||
m.rows.selectItemId = Id
|
||||
end sub
|
||||
|
||||
' Updates the visibility of the Extras button based on if this season has any extra features
|
||||
sub setExtraButtonVisibility()
|
||||
if isValid(m.top.extrasObjects) and isValidAndNotEmpty(m.top.extrasObjects.items)
|
||||
@ -75,6 +81,7 @@ end function
|
||||
' OnScreenShown: Callback function when view is presented on screen
|
||||
'
|
||||
sub OnScreenShown()
|
||||
|
||||
if m.isFirstRun
|
||||
m.isFirstRun = false
|
||||
return
|
||||
@ -82,6 +89,7 @@ sub OnScreenShown()
|
||||
|
||||
m.tvEpisodeRow.setFocus(true)
|
||||
m.top.refreshSeasonDetailsData = not m.top.refreshSeasonDetailsData
|
||||
|
||||
end sub
|
||||
|
||||
' Handle navigation input from the remote and act on it
|
||||
|
@ -43,7 +43,21 @@ end sub
|
||||
'
|
||||
sub onProgressPercentageChanged()
|
||||
m.videoPositionTime.text = secondsToHuman(m.top.positionTime, true)
|
||||
m.videoRemainingTime.text = secondsToHuman(m.top.remainingPositionTime, true)
|
||||
|
||||
osdmode = m.global.session.user.settings["ui.general.osdremainingtime"]
|
||||
if m.global.session.user.settings["ui.design.hideclock"]
|
||||
' in order to honor the hide clocks setting
|
||||
osdmode = "remaining"
|
||||
end if
|
||||
|
||||
if osdmode = "remaining"
|
||||
m.videoRemainingTime.text = secondsToHuman(m.top.remainingPositionTime, true)
|
||||
else if osdmode = "timeofday"
|
||||
m.videoRemainingTime.text = secondsToEndTime(m.top.remainingPositionTime)
|
||||
else if osdmode = "both"
|
||||
m.videoRemainingTime.text = secondsToHuman(m.top.remainingPositionTime, true) + " | " + secondsToEndTime(m.top.remainingPositionTime)
|
||||
end if
|
||||
|
||||
m.progressBar.width = m.progressBarBackground.width * m.top.progressPercentage
|
||||
end sub
|
||||
|
||||
|
@ -11,6 +11,7 @@ sub init()
|
||||
m.top.getScene().findNode("overhang").visible = false
|
||||
userSettings = m.global.session.user.settings
|
||||
m.currentItem = m.global.queueManager.callFunc("getCurrentItem")
|
||||
m.originalClosedCaptionState = invalid
|
||||
|
||||
m.top.id = m.currentItem.id
|
||||
m.top.seekMode = "accurate"
|
||||
@ -413,7 +414,7 @@ sub onVideoContentLoaded()
|
||||
m.osd.itemTitleText = m.top.content.title
|
||||
|
||||
' If video is an episode, attempt to add season and episode numbers to OSD
|
||||
if m.top.content.contenttype = 4
|
||||
if m.top.content.contenttype = 4 or m.top.content.live
|
||||
if isValid(videoContent[0].seasonNumber)
|
||||
m.osd.seasonNumber = videoContent[0].seasonNumber
|
||||
end if
|
||||
@ -463,7 +464,12 @@ sub onVideoContentLoaded()
|
||||
availableSubtitleTrackIndex = availSubtitleTrackIdx(selectedSubtitle.Track.TrackName)
|
||||
if availableSubtitleTrackIndex <> -1
|
||||
if not selectedSubtitle.IsEncoded
|
||||
m.top.globalCaptionMode = "On"
|
||||
if selectedSubtitle.IsForced
|
||||
' If IsForced, make sure to remember the Roku global setting so we
|
||||
' can set it back when the video is done playing.
|
||||
m.originalClosedCaptionState = m.top.globalCaptionMode
|
||||
m.top.globalCaptionMode = "On"
|
||||
end if
|
||||
m.top.subtitleTrack = m.top.availableSubtitleTracks[availableSubtitleTrackIndex].TrackName
|
||||
end if
|
||||
end if
|
||||
@ -699,6 +705,12 @@ sub ReportPlayback(state = "update" as string)
|
||||
m.bufferCheckTimer.duration = 30
|
||||
end if
|
||||
|
||||
if (state = "stop" or state = "finished") and m.originalClosedCaptionState <> invalid
|
||||
m.log.debug("ReportPlayback() setting", m.top.globalCaptionMode, "back to", m.originalClosedCaptionState)
|
||||
m.top.globalCaptionMode = m.originalClosedCaptionState
|
||||
m.originalClosedCaptionState = invalid
|
||||
end if
|
||||
|
||||
' Report playstate via worker task
|
||||
playstateTask = m.global.playstateTask
|
||||
playstateTask.setFields({ status: state, params: params })
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -38,7 +38,7 @@
|
||||
sceneManager = CreateObject("roSGNode", "SceneManager")
|
||||
sceneManager.observeField("dataReturned", m.port)
|
||||
|
||||
m.global.addFields({ app_loaded: false, playstateTask: playstateTask, sceneManager: sceneManager })
|
||||
m.global.addFields({ app_loaded: false, playstateTask: playstateTask, sceneManager: sceneManager, launchSource: "" })
|
||||
m.global.addFields({ queueManager: CreateObject("roSGNode", "QueueManager") })
|
||||
m.global.addFields({ audioPlayer: CreateObject("roSGNode", "AudioPlayer") })
|
||||
|
||||
@ -309,22 +309,39 @@
|
||||
audio_stream_idx = selectedItem.selectedAudioStreamIndex
|
||||
end if
|
||||
|
||||
launchSource = m.global.launchSource
|
||||
m.global.launchSource = ""
|
||||
|
||||
selectedItem.selectedAudioStreamIndex = audio_stream_idx
|
||||
' Display playback options dialog
|
||||
if selectedItem.json.userdata.PlaybackPositionTicks > 0
|
||||
m.global.queueManager.callFunc("hold", selectedItem)
|
||||
playbackOptionDialog(selectedItem.json.userdata.PlaybackPositionTicks, selectedItem.json)
|
||||
else
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", selectedItem)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
|
||||
localGlobal = m.global
|
||||
|
||||
' we only use this special steering logic from the home screen
|
||||
if launchSource <> "home"
|
||||
' Display playback options dialog
|
||||
if selectedItem.json.userdata.PlaybackPositionTicks > 0
|
||||
m.global.queueManager.callFunc("hold", selectedItem)
|
||||
playbackOptionDialog(selectedItem.json.userdata.PlaybackPositionTicks, selectedItem.json)
|
||||
else
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", selectedItem)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
end if
|
||||
|
||||
else if localGlobal.session.user.settings["ui.general.episodenextupbehavior"] = "episode"
|
||||
group = CreateMovieDetailsGroup(selectedItem)
|
||||
|
||||
else if localGlobal.session.user.settings["ui.general.episodenextupbehavior"] = "season"
|
||||
group = CreateSeasonDetailsGroupByID(selectedItem.json.SeriesId, selectedItem.json.SeasonId, selectedItem.id)
|
||||
|
||||
end if
|
||||
|
||||
|
||||
else if selectedItemType = "Series"
|
||||
group = CreateSeriesDetailsGroup(selectedItem.json.id)
|
||||
else if selectedItemType = "Season"
|
||||
if isValid(selectedItem.json) and isValid(selectedItem.json.SeriesId) and isValid(selectedItem.id)
|
||||
group = CreateSeasonDetailsGroupByID(selectedItem.json.SeriesId, selectedItem.id)
|
||||
group = CreateSeasonDetailsGroupByID(selectedItem.json.SeriesId, selectedItem.id, "")
|
||||
else
|
||||
stopLoadingSpinner()
|
||||
message_dialog(tr("Error loading Season"))
|
||||
@ -865,7 +882,7 @@
|
||||
else if popupNode.returnData.indexselected = 3
|
||||
' User chose Go to season
|
||||
if isValid(selectedItem[0].json) and isValid(selectedItem[0].json.SeriesId) and isValid(selectedItem[0].json.seasonID)
|
||||
CreateSeasonDetailsGroupByID(selectedItem[0].json.SeriesId, selectedItem[0].json.seasonID)
|
||||
CreateSeasonDetailsGroupByID(selectedItem[0].json.SeriesId, selectedItem[0].json.seasonID, "")
|
||||
else
|
||||
stopLoadingSpinner()
|
||||
message_dialog(tr("Error loading Season"))
|
||||
|
@ -648,7 +648,7 @@ function CreateSeriesDetailsGroup(seriesID as string) as dynamic
|
||||
' Divert to season details if user setting goStraightToEpisodeListing is enabled and only one season exists.
|
||||
if seasonData <> invalid and m.global.session.user.settings["ui.tvshows.goStraightToEpisodeListing"] and seasonData.Items.Count() = 1
|
||||
stopLoadingSpinner()
|
||||
return CreateSeasonDetailsGroupByID(seriesID, seasonData.Items[0].id)
|
||||
return CreateSeasonDetailsGroupByID(seriesID, seasonData.Items[0].id, "")
|
||||
end if
|
||||
' start building SeriesDetails view
|
||||
group = CreateObject("roSGNode", "TVShowDetails")
|
||||
@ -795,7 +795,7 @@ function CreateSeasonDetailsGroup(series as object, season as object) as dynamic
|
||||
return group
|
||||
end function
|
||||
|
||||
function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string) as dynamic
|
||||
function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string, selectId as string) as dynamic
|
||||
' validate parameters
|
||||
if seriesID = "" or seasonID = "" then return invalid
|
||||
|
||||
@ -807,9 +807,11 @@ function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string) as
|
||||
stopLoadingSpinner()
|
||||
return invalid
|
||||
end if
|
||||
|
||||
' start building SeasonDetails view
|
||||
group = CreateObject("roSGNode", "TVEpisodes")
|
||||
group.optionsAvailable = false
|
||||
|
||||
' push scene asap (to prevent extra button presses when retriving series/movie info)
|
||||
group.seasonData = seasonMetaData.json
|
||||
group.objects = TVEpisodes(seriesID, seasonID)
|
||||
@ -823,6 +825,10 @@ function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string) as
|
||||
' check for specials/extras for this season
|
||||
group.extrasObjects = TVSeasonExtras(seasonID)
|
||||
|
||||
group.observeField("sceneReady", "onSceneReady")
|
||||
|
||||
group.selectItemId = selectId
|
||||
|
||||
' finished building SeasonDetails view
|
||||
return group
|
||||
end function
|
||||
|
@ -49,8 +49,8 @@ function ItemPostPlaybackInfo(id as string, mediaSourceId = "" as string, audioT
|
||||
|
||||
' force the server to transcode AAC profiles we don't support to MP3 instead of the usual AAC
|
||||
' TODO: Remove this after server adds support for transcoding AAC from one profile to another
|
||||
if LCase(selectedAudioStream.Codec) = "aac"
|
||||
if LCase(selectedAudioStream.Profile) = "main" or LCase(selectedAudioStream.Profile) = "he-aac"
|
||||
if selectedAudioStream.Codec <> invalid and LCase(selectedAudioStream.Codec) = "aac"
|
||||
if selectedAudioStream.Profile <> invalid and LCase(selectedAudioStream.Profile) = "main" or LCase(selectedAudioStream.Profile) = "he-aac"
|
||||
for each rule in deviceProfile.TranscodingProfiles
|
||||
if rule.Container = "ts" or rule.Container = "mp4"
|
||||
if rule.AudioCodec = "aac"
|
||||
|
@ -303,21 +303,11 @@ function getTranscodingProfiles() as object
|
||||
end if
|
||||
|
||||
' AV1
|
||||
for each container in transcodingContainers
|
||||
if di.CanDecodeVideo({ Codec: "av1", Container: container }).Result
|
||||
if container = "mp4"
|
||||
' check for codec string before adding it
|
||||
if mp4VideoCodecs.Instr(0, ",av1") = -1
|
||||
mp4VideoCodecs = mp4VideoCodecs + ",av1"
|
||||
end if
|
||||
else if container = "ts"
|
||||
' check for codec string before adding it
|
||||
if tsVideoCodecs.Instr(0, ",av1") = -1
|
||||
tsVideoCodecs = tsVideoCodecs + ",av1"
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
end for
|
||||
' CanDecodeVideo() returns false for AV1 when the container is provided
|
||||
' Manually add AV1 to the mp4VideoCodecs list if support is detected
|
||||
if di.CanDecodeVideo({ Codec: "av1" }).Result
|
||||
mp4VideoCodecs = mp4VideoCodecs + ",av1"
|
||||
end if
|
||||
|
||||
' AUDIO CODECS
|
||||
for each container in transcodingContainers
|
||||
|
@ -72,6 +72,24 @@ function secondsToHuman(totalSeconds as integer, addLeadingMinuteZero as boolean
|
||||
return humanTime
|
||||
end function
|
||||
|
||||
function secondsToEndTime(totalSeconds) as string
|
||||
' Get the current time in seconds since midnight UTC (Unix Epoch Time)
|
||||
currentUTCTime = CreateObject("roDateTime").AsSeconds()
|
||||
|
||||
' Calculate the target time in seconds by adding the number of seconds
|
||||
targetTimeInSeconds = currentUTCTime + totalSeconds
|
||||
|
||||
' Create a new roDateTime object for the target time
|
||||
targetDateTime = CreateObject("roDateTime")
|
||||
targetDateTime.FromSeconds(targetTimeInSeconds)
|
||||
targetDateTime.ToLocalTime()
|
||||
|
||||
formattedTime = formatTime(targetDateTime)
|
||||
|
||||
return formattedTime
|
||||
end function
|
||||
|
||||
|
||||
' Format time as 12 or 24 hour format based on system clock setting
|
||||
function formatTime(time) as string
|
||||
hours = time.getHours()
|
||||
|
@ -1338,6 +1338,54 @@
|
||||
<translation>Serienbild verwenden</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Play Next Episode Automatically</source>
|
||||
<translation>Nächste Episode automatisch abspielen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>When finished playing a single episode, play the next one automatically.</source>
|
||||
<translation>Wenn die Wiedergabe einer einzelnen Episode beendet ist, wird automatisch die nächste Episode abgespielt.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special</source>
|
||||
<translation>Special</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Episode Next Up Behavior</source>
|
||||
<translation>Nächste Episode Verhalten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This controls what clicking OK on a Next Up episode does.</source>
|
||||
<translation>Damit wird gesteuert, was ein Klick auf OK bei einer Folge von Next Up bewirkt.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Episode Details</source>
|
||||
<translation>Details zur Episode ansehen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Season Details</source>
|
||||
<translation>Staffel Details ansehen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OSD Remaining Time</source>
|
||||
<translation>verbleibende OSD Zeit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>How is remaining time represented. Remaining time shows hours, minutes, and seconds remaining. Time of day shows what time the video will complete.</source>
|
||||
<translation>Wie wird die verbleibende Zeit dargestellt? Die verbleibende Zeit zeigt die verbleibenden Stunden, Minuten und Sekunden an. Die Tageszeit zeigt an, wann das Video beendet sein wird.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remaining Time</source>
|
||||
<translation>verbleibende Zeit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Time of Day</source>
|
||||
<translation>Tageszeit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Both</source>
|
||||
<translation>Beide</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name></name>
|
||||
|
@ -1322,6 +1322,46 @@
|
||||
<source>When finished playing a single episode, play the next one automatically.</source>
|
||||
<translation>When finished playing a single episode, play the next one automatically.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special</source>
|
||||
<translation>Special</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>How is remaining time represented. Remaining time shows hours, minutes, and seconds remaining. Time of day shows what time the video will complete.</source>
|
||||
<translation>How is remaining time represented. Remaining time shows hours, minutes, and seconds remaining. Time of day shows what time the video will complete.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Episode Next Up Behavior</source>
|
||||
<translation>Episode Next Up Behaviour</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This controls what clicking OK on a Next Up episode does.</source>
|
||||
<translation>This controls what clicking OK on a Next Up episode does.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Episode Details</source>
|
||||
<translation>View Episode Details</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Season Details</source>
|
||||
<translation>View Season Details</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OSD Remaining Time</source>
|
||||
<translation>OSD Remaining Time</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remaining Time</source>
|
||||
<translation>Remaining Time</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Time of Day</source>
|
||||
<translation>Time of Day</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Both</source>
|
||||
<translation>Both</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name></name>
|
||||
|
@ -1321,6 +1321,71 @@
|
||||
<translation>Use Show Image</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Row Layout</source>
|
||||
<translation>Row Layout</translation>
|
||||
<extracomment>User Setting - Setting title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Choose how rows are displayed on the home screen.</source>
|
||||
<translation>Choose how rows are displayed on the home screen.</translation>
|
||||
<extracomment>User Setting - Setting description</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Original</source>
|
||||
<translation>Original</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Full Width</source>
|
||||
<translation>Full Width</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Episode Next Up Behavior</source>
|
||||
<translation>Episode Next Up Behavior</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>This controls what clicking OK on a Next Up episode does.</source>
|
||||
<translation>This controls what clicking OK on a Next Up episode does.</translation>
|
||||
<extracomment>User Setting - Setting description</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Episode Details</source>
|
||||
<translation>View Episode Details</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Season Details</source>
|
||||
<translation>View Season Details</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>OSD Remaining Time</source>
|
||||
<translation>OSD Remaining Time</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>How is remaining time represented. Remaining time shows hours, minutes, and seconds remaining. Time of day shows what time the video will complete.</source>
|
||||
<translation>How is remaining time represented. Remaining time shows hours, minutes, and seconds remaining. Time of day shows what time the video will complete.</translation>
|
||||
<extracomment>User Setting - Setting description</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remaining Time</source>
|
||||
<translation>Remaining Time</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Time of Day</source>
|
||||
<translation>Time of Day</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Both</source>
|
||||
<translation>Both</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special</source>
|
||||
<translation>Special</translation>
|
||||
@ -1331,5 +1396,10 @@
|
||||
<translation>N/A</translation>
|
||||
<extracomment>Abbreviation for not available</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>CH</source>
|
||||
<translation>CH</translation>
|
||||
<extracomment>Abbreviation for Channel</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
@ -1330,6 +1330,54 @@
|
||||
<translation>Images d'épisodes à suivre</translation>
|
||||
<extracomment>User Setting - Setting title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Play Next Episode Automatically</source>
|
||||
<translation>Lancer automatiquement le nouvel épisode</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special</source>
|
||||
<translation>Spécial</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>When finished playing a single episode, play the next one automatically.</source>
|
||||
<translation>Lorsque la lecture d'un épisode est terminée, lancer automatiquement le suivant.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>How is remaining time represented. Remaining time shows hours, minutes, and seconds remaining. Time of day shows what time the video will complete.</source>
|
||||
<translation>Comment le temps restant est-il représenté ? Le temps restant indique les heures, les minutes et les secondes restantes. L'heure du jour indique l'heure à laquelle la vidéo se terminera.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Episode Next Up Behavior</source>
|
||||
<translation type="unfinished">Comportement du prochain épisode</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This controls what clicking OK on a Next Up episode does.</source>
|
||||
<translation>Cela contrôle ce que fait un clic sur OK sur un épisode suivant.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Episode Details</source>
|
||||
<translation>Voir les détails de l'épisode</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Season Details</source>
|
||||
<translation>Voir les détails de la saison</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OSD Remaining Time</source>
|
||||
<translation type="unfinished">Affichage à l'écran du temps restant</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remaining Time</source>
|
||||
<translation>Temps restant</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Time of Day</source>
|
||||
<translation>Heure de la journée</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Both</source>
|
||||
<translation>Les deux</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name></name>
|
||||
|
@ -1353,6 +1353,54 @@
|
||||
<translation>Usar imagem da série</translation>
|
||||
<extracomment>User Setting - Setting option title</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Play Next Episode Automatically</source>
|
||||
<translation>Reproduzir o próximo episódio automaticamente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>When finished playing a single episode, play the next one automatically.</source>
|
||||
<translation>Quando terminar de reproduzir um único episodio, reproduz o próximo de forma automática.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Special</source>
|
||||
<translation>Especial</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>How is remaining time represented. Remaining time shows hours, minutes, and seconds remaining. Time of day shows what time the video will complete.</source>
|
||||
<translation>Como o tempo restante é representado. O tempo exibe as horas, minutos e segundos restantes. A hora do dia mostra o horário em que o vídeo será concluído.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Episode Next Up Behavior</source>
|
||||
<translation>Comportamento do A Seguir</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Episode Details</source>
|
||||
<translation>Ver Detalhes do Episódio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This controls what clicking OK on a Next Up episode does.</source>
|
||||
<translation>Isso controla o comportamento ao clicar OK no A Seguir.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>View Season Details</source>
|
||||
<translation>Ver Detalhes da Temporada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remaining Time</source>
|
||||
<translation>Tempo Restante</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>OSD Remaining Time</source>
|
||||
<translation>Tempo Restante de Exibição na Tela</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Time of Day</source>
|
||||
<translation>Hora do Dia</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Both</source>
|
||||
<translation>Ambos</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name></name>
|
||||
|
@ -581,6 +581,310 @@
|
||||
<translation>Necunoscut</translation>
|
||||
<extracomment>Title for a cast member for which we have no information for</extracomment>
|
||||
</message>
|
||||
<message>
|
||||
<source>Bring the theater experience straight to your living room with the ability to play custom intros before the main feature.</source>
|
||||
<translation>Adu experiența cinematografică direct în sufrageria ta, cu posibilitatea de a reda introduceri personalizate înainte de filmul principal.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Paused</source>
|
||||
<translation>Slideshow Paused</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Random Off</source>
|
||||
<translation>Aleatoriu Oprit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Splashscreen as Home Background</source>
|
||||
<translation>Folosește Ecranul de Start ca Fundal pentru Pagina Principală</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Audio Codec</source>
|
||||
<translation>Codec Audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You can search for Titles, People, Live TV Channels and more</source>
|
||||
<translation>Poți căuta Titluri, Persoane, Canale TV live și altele</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for HEVC media with unsupported profile levels before falling back to transcoding if it fails.</source>
|
||||
<translation>Încearcă redarea directă pentru media HEVC cu niveluri de profil nesuportate, înainte de a recurge la transcodare dacă aceasta eșuează.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reason</source>
|
||||
<translation>Motiv</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Video Codec</source>
|
||||
<translation>Codec Video</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Size</source>
|
||||
<translation>Dimensiune</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use voice remote to search</source>
|
||||
<translation>Folosește căutare vocală</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>H.264</source>
|
||||
<translation>H.264</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settings relating to how the application looks.</source>
|
||||
<translation>Setări referitoare la aspectul aplicației.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go directly to the episode list if a TV series has only one season.</source>
|
||||
<translation>Mergi direct la lista de episoade dacă un serial are un singur sezon.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Blur images of unwatched episodes.</source>
|
||||
<translation>Estompează imaginile episoadelor nevizionate.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide Clock</source>
|
||||
<translation>Ascunde Ceasul</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide all clocks in Jellyfin. Jellyfin will need to be closed and reopened for changes to take effect.</source>
|
||||
<translation>Ascunde ceasul peste tot in Jellyfin. Va fi necesar să închizi și să redeschizi Jellyfin pentru ca modificările să aibă efect.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.</source>
|
||||
<translation>Încearcă redarea directă pentru media H.264 cu niveluri de profil nesuportate, înainte de a recurge la transcodare dacă aceasta eșuează.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom Subtitles</source>
|
||||
<translation>Subtitrări Personalizate</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Replace Roku's default subtitle functions with custom functions that support CJK fonts. Fallback fonts must be configured and enabled on the server for CJK rendering to work.</source>
|
||||
<translation>Înlocuiește funcțiile implicite de subtitrare ale Roku cu funcții personalizate care suportă fonturi CJK (chineză, japoneză, coreeană). Fonturile de rezervă trebuie configurate și activate pe server pentru ca redarea CJK să funcționeze.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>all</source>
|
||||
<translation>toate</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Off</source>
|
||||
<translation>Slideshow Off</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow On</source>
|
||||
<translation>Slideshow On</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>MPEG-4 Support</source>
|
||||
<translation>Suport MPEG-4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Slideshow Resumed</source>
|
||||
<translation>Slideshow Resumed</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Parental Ratings</source>
|
||||
<translation>Evaluări Parentale</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Years</source>
|
||||
<translation>Ani</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show What's New Popup</source>
|
||||
<translation>Afișează Popup-ul „Ce e Nou”</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show What's New popup when Jellyfin is updated to a new version.</source>
|
||||
<translation>Afișează popup-ul „Ce e Nou” când Jellyfin este actualizat la o versiune nouă.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Next episode</source>
|
||||
<translation>Următorul episod</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Random On</source>
|
||||
<translation>Aleatoriu Pornit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Presentation)</source>
|
||||
<translation>Filme (Prezentare)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Movies (Grid)</source>
|
||||
<translation>Filme (Grilă)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show item count in the library and index of selected item.</source>
|
||||
<translation>Afișează numărul de elemente din bibliotecă și indexul elementului selectat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Here is your Quick Connect code:</source>
|
||||
<translation>Iată codul tău de Conectare Rapidă:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hide Taglines</source>
|
||||
<translation>Ascunde sloganurile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Design Elements</source>
|
||||
<translation>Elemente de Design</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Play Trailer</source>
|
||||
<translation>Redă Trailer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Transcoding Information</source>
|
||||
<translation>Informații de transcodare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Audio Channels</source>
|
||||
<translation>Canale Audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Level</source>
|
||||
<translation>Nivel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Video range type</source>
|
||||
<translation>Tipul intervalului video</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set Favorite</source>
|
||||
<translation>Setează că favorit</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set Watched</source>
|
||||
<translation>Marcheaza ca vizionat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Go to series</source>
|
||||
<translation>Mergi la sezoane</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search now</source>
|
||||
<translation>Caută acum</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(Dialog will close automatically)</source>
|
||||
<translation>(Dialogul se va închide automat)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Return to Top</source>
|
||||
<translation>Revino la început</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Studios</source>
|
||||
<translation>Studiouri</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>Folosește butonul de reluare pentru a anima încet până la primul element din folder. (Dacă este dezactivat, folderul va reveni imediat la primul element).</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hides tagline text on details pages.</source>
|
||||
<translation>Ascunde textul de slogan pe paginile de detalii.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options for TV Shows.</source>
|
||||
<translation>Opțiuni pentru seriale TV.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Blur Unwatched Episodes</source>
|
||||
<translation>Estompează episoadele nevizionate</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Skip Details for Single Seasons</source>
|
||||
<translation>Omite detaliile pentru sezoanele individuale</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Options that alter the design of Jellyfin.</source>
|
||||
<translation>Opțiuni ce modifică designul Jellyfin.</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>Folosește imaginea generată a ecranului de start ca fundal pentru pagina principală a Jellyfin. Va fi necesar să închizi și să redeschizi Jellyfin pentru ca schimbarea să aibă efect.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Cinema Mode</source>
|
||||
<translation>Mod Cinema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HEVC</source>
|
||||
<translation>HEVC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settings relating to playback and supported codec and media types.</source>
|
||||
<translation>Setări referitoare la redare și la codec-urile și tipurile de media acceptate.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Max Days Next Up</source>
|
||||
<translation>Zile Maxime la Următorul Episod</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Playback Information</source>
|
||||
<translation>Informații de redare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.</source>
|
||||
<translation>Setează numărul maxim de zile în care un serial ar trebui să rămână în lista „Următorul Episod” fără a fi vizionat.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>direct</source>
|
||||
<translation>direct</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Total Bitrate</source>
|
||||
<translation>Bitrate Total</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Stream Information</source>
|
||||
<translation>Informații Stream</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec</source>
|
||||
<translation>Codec</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Codec Tag</source>
|
||||
<translation>Tag Codec</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Bit Rate</source>
|
||||
<translation>Bit Rate</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Container</source>
|
||||
<translation>Container</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pixel format</source>
|
||||
<translation>Format Pixel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>WxH</source>
|
||||
<translation>Lățime x Înălțime (LxÎ)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unable to find any albums or songs belonging to this artist</source>
|
||||
<translation>Nu s-au găsit albume sau melodii aparținând acestui artist</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Text Subtitles Only</source>
|
||||
<translation>Doar Subtitrări Text</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Aired</source>
|
||||
<translation>Difuzat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only display text subtitles to minimize transcoding.</source>
|
||||
<translation>Afișează doar subtitrările text pentru a minimiza transcodarea.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unplayed</source>
|
||||
<translation>Nevizionat</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name></name>
|
||||
|
1367
package-lock.json
generated
1367
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@ -5,23 +5,23 @@
|
||||
"description": "Roku app for Jellyfin media server",
|
||||
"dependencies": {
|
||||
"@rokucommunity/bslib": "0.1.1",
|
||||
"brighterscript-formatter": "1.7.5",
|
||||
"brighterscript-formatter": "1.7.6",
|
||||
"log": "npm:roku-log@0.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rokucommunity/bslint": "0.8.25",
|
||||
"brighterscript": "0.67.7",
|
||||
"@rokucommunity/bslint": "0.8.26",
|
||||
"brighterscript": "0.67.8",
|
||||
"brighterscript-jsdocs-plugin": "0.7.3",
|
||||
"clean-jsdoc-theme": "4.3.0",
|
||||
"jsdoc": "4.0.3",
|
||||
"jsdoc": "4.0.4",
|
||||
"jshint": "2.13.6",
|
||||
"markdownlint-cli2": "0.14.0",
|
||||
"rimraf": "6.0.1",
|
||||
"roku-deploy": "3.12.1",
|
||||
"roku-deploy": "3.12.2",
|
||||
"roku-log-bsc-plugin": "0.8.1",
|
||||
"rooibos-roku": "5.14.0",
|
||||
"ropm": "0.10.26",
|
||||
"spellchecker-cli": "6.2.0",
|
||||
"ropm": "0.10.27",
|
||||
"spellchecker-cli": "7.0.0",
|
||||
"undent": "0.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -262,6 +262,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Episode Next Up Behavior",
|
||||
"description": "This controls what clicking OK on a Next Up episode does.",
|
||||
"settingName": "ui.general.episodenextupbehavior",
|
||||
"type": "radio",
|
||||
"default": "episode",
|
||||
"options": [
|
||||
{
|
||||
"title": "View Episode Details",
|
||||
"id": "episode"
|
||||
},
|
||||
{
|
||||
"title": "View Season Details",
|
||||
"id": "season"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Hide Clock",
|
||||
"description": "Hide all clocks in Jellyfin. Jellyfin will need to be closed and reopened for changes to take effect.",
|
||||
@ -276,6 +293,27 @@
|
||||
"type": "integer",
|
||||
"default": "365"
|
||||
},
|
||||
{
|
||||
"title": "OSD Remaining Time",
|
||||
"description": "How is remaining time represented. Remaining time shows hours, minutes, and seconds remaining. Time of day shows what time the video will complete.",
|
||||
"settingName": "ui.general.osdremainingtime",
|
||||
"type": "radio",
|
||||
"default": "remaining",
|
||||
"options": [
|
||||
{
|
||||
"title": "Remaining Time",
|
||||
"id": "remaining"
|
||||
},
|
||||
{
|
||||
"title": "Time of Day",
|
||||
"id": "timeofday"
|
||||
},
|
||||
{
|
||||
"title": "Both",
|
||||
"id": "both"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Rewatching Next Up",
|
||||
"description": "Show already watched episodes in 'Next Up' sections.",
|
||||
@ -283,6 +321,23 @@
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"title": "Row Layout",
|
||||
"description": "Choose how rows are displayed on the home screen.",
|
||||
"settingName": "ui.row.layout",
|
||||
"type": "radio",
|
||||
"default": "fullwidth",
|
||||
"options": [
|
||||
{
|
||||
"title": "Original",
|
||||
"id": "original"
|
||||
},
|
||||
{
|
||||
"title": "Full Width",
|
||||
"id": "fullwidth"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Show What's New Popup",
|
||||
"description": "Show What's New popup when Jellyfin is updated to a new version.",
|
||||
|
@ -36,7 +36,7 @@ sub Main (args as dynamic) as void
|
||||
sceneManager = CreateObject("roSGNode", "SceneManager")
|
||||
sceneManager.observeField("dataReturned", m.port)
|
||||
|
||||
m.global.addFields({ app_loaded: false, playstateTask: playstateTask, sceneManager: sceneManager })
|
||||
m.global.addFields({ app_loaded: false, playstateTask: playstateTask, sceneManager: sceneManager, launchSource: "" })
|
||||
m.global.addFields({ queueManager: CreateObject("roSGNode", "QueueManager") })
|
||||
m.global.addFields({ audioPlayer: CreateObject("roSGNode", "AudioPlayer") })
|
||||
|
||||
@ -307,22 +307,39 @@ sub Main (args as dynamic) as void
|
||||
audio_stream_idx = selectedItem.selectedAudioStreamIndex
|
||||
end if
|
||||
|
||||
launchSource = m.global.launchSource
|
||||
m.global.launchSource = ""
|
||||
|
||||
selectedItem.selectedAudioStreamIndex = audio_stream_idx
|
||||
' Display playback options dialog
|
||||
if selectedItem.json.userdata.PlaybackPositionTicks > 0
|
||||
m.global.queueManager.callFunc("hold", selectedItem)
|
||||
playbackOptionDialog(selectedItem.json.userdata.PlaybackPositionTicks, selectedItem.json)
|
||||
else
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", selectedItem)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
|
||||
localGlobal = m.global
|
||||
|
||||
' we only use this special steering logic from the home screen
|
||||
if launchSource <> "home"
|
||||
' Display playback options dialog
|
||||
if selectedItem.json.userdata.PlaybackPositionTicks > 0
|
||||
m.global.queueManager.callFunc("hold", selectedItem)
|
||||
playbackOptionDialog(selectedItem.json.userdata.PlaybackPositionTicks, selectedItem.json)
|
||||
else
|
||||
m.global.queueManager.callFunc("clear")
|
||||
m.global.queueManager.callFunc("push", selectedItem)
|
||||
m.global.queueManager.callFunc("playQueue")
|
||||
end if
|
||||
|
||||
else if localGlobal.session.user.settings["ui.general.episodenextupbehavior"] = "episode"
|
||||
group = CreateMovieDetailsGroup(selectedItem)
|
||||
|
||||
else if localGlobal.session.user.settings["ui.general.episodenextupbehavior"] = "season"
|
||||
group = CreateSeasonDetailsGroupByID(selectedItem.json.SeriesId, selectedItem.json.SeasonId, selectedItem.id)
|
||||
|
||||
end if
|
||||
|
||||
|
||||
else if selectedItemType = "Series"
|
||||
group = CreateSeriesDetailsGroup(selectedItem.json.id)
|
||||
else if selectedItemType = "Season"
|
||||
if isValid(selectedItem.json) and isValid(selectedItem.json.SeriesId) and isValid(selectedItem.id)
|
||||
group = CreateSeasonDetailsGroupByID(selectedItem.json.SeriesId, selectedItem.id)
|
||||
group = CreateSeasonDetailsGroupByID(selectedItem.json.SeriesId, selectedItem.id, "")
|
||||
else
|
||||
stopLoadingSpinner()
|
||||
message_dialog(tr("Error loading Season"))
|
||||
@ -863,7 +880,7 @@ sub Main (args as dynamic) as void
|
||||
else if popupNode.returnData.indexselected = 3
|
||||
' User chose Go to season
|
||||
if isValid(selectedItem[0].json) and isValid(selectedItem[0].json.SeriesId) and isValid(selectedItem[0].json.seasonID)
|
||||
CreateSeasonDetailsGroupByID(selectedItem[0].json.SeriesId, selectedItem[0].json.seasonID)
|
||||
CreateSeasonDetailsGroupByID(selectedItem[0].json.SeriesId, selectedItem[0].json.seasonID, "")
|
||||
else
|
||||
stopLoadingSpinner()
|
||||
message_dialog(tr("Error loading Season"))
|
||||
|
@ -646,7 +646,7 @@ function CreateSeriesDetailsGroup(seriesID as string) as dynamic
|
||||
' Divert to season details if user setting goStraightToEpisodeListing is enabled and only one season exists.
|
||||
if seasonData <> invalid and m.global.session.user.settings["ui.tvshows.goStraightToEpisodeListing"] and seasonData.Items.Count() = 1
|
||||
stopLoadingSpinner()
|
||||
return CreateSeasonDetailsGroupByID(seriesID, seasonData.Items[0].id)
|
||||
return CreateSeasonDetailsGroupByID(seriesID, seasonData.Items[0].id, "")
|
||||
end if
|
||||
' start building SeriesDetails view
|
||||
group = CreateObject("roSGNode", "TVShowDetails")
|
||||
@ -793,7 +793,7 @@ function CreateSeasonDetailsGroup(series as object, season as object) as dynamic
|
||||
return group
|
||||
end function
|
||||
|
||||
function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string) as dynamic
|
||||
function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string, selectId as string) as dynamic
|
||||
' validate parameters
|
||||
if seriesID = "" or seasonID = "" then return invalid
|
||||
|
||||
@ -805,9 +805,11 @@ function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string) as
|
||||
stopLoadingSpinner()
|
||||
return invalid
|
||||
end if
|
||||
|
||||
' start building SeasonDetails view
|
||||
group = CreateObject("roSGNode", "TVEpisodes")
|
||||
group.optionsAvailable = false
|
||||
|
||||
' push scene asap (to prevent extra button presses when retriving series/movie info)
|
||||
group.seasonData = seasonMetaData.json
|
||||
group.objects = TVEpisodes(seriesID, seasonID)
|
||||
@ -821,6 +823,10 @@ function CreateSeasonDetailsGroupByID(seriesID as string, seasonID as string) as
|
||||
' check for specials/extras for this season
|
||||
group.extrasObjects = TVSeasonExtras(seasonID)
|
||||
|
||||
group.observeField("sceneReady", "onSceneReady")
|
||||
|
||||
group.selectItemId = selectId
|
||||
|
||||
' finished building SeasonDetails view
|
||||
return group
|
||||
end function
|
||||
|
@ -70,6 +70,24 @@ function secondsToHuman(totalSeconds as integer, addLeadingMinuteZero as boolean
|
||||
return humanTime
|
||||
end function
|
||||
|
||||
function secondsToEndTime(totalSeconds) as string
|
||||
' Get the current time in seconds since midnight UTC (Unix Epoch Time)
|
||||
currentUTCTime = CreateObject("roDateTime").AsSeconds()
|
||||
|
||||
' Calculate the target time in seconds by adding the number of seconds
|
||||
targetTimeInSeconds = currentUTCTime + totalSeconds
|
||||
|
||||
' Create a new roDateTime object for the target time
|
||||
targetDateTime = CreateObject("roDateTime")
|
||||
targetDateTime.FromSeconds(targetTimeInSeconds)
|
||||
targetDateTime.ToLocalTime()
|
||||
|
||||
formattedTime = formatTime(targetDateTime)
|
||||
|
||||
return formattedTime
|
||||
end function
|
||||
|
||||
|
||||
' Format time as 12 or 24 hour format based on system clock setting
|
||||
function formatTime(time) as string
|
||||
hours = time.getHours()
|
||||
|
Loading…
Reference in New Issue
Block a user