mirror of
https://github.com/jellyfin/jellyfin-roku.git
synced 2025-02-19 22:41:47 +00:00
Merge pull request #1987 from Renbo2024/next-up-behavior-configuration
New Features: OK button on next up customization and OSD time left customization.
This commit is contained in:
commit
f3941710dc
@ -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
|
||||
|
@ -687,6 +687,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>
|
||||
|
@ -1321,10 +1321,55 @@
|
||||
<translation>Use Show Image</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>
|
||||
<extracomment>Special episode of a TV Show</extracomment>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
</TS>
|
@ -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.",
|
||||
|
@ -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…
x
Reference in New Issue
Block a user