Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Episode Next Up Behavior and OSD Remaining Time #1987

Merged
merged 9 commits into from
Oct 23, 2024
1 change: 1 addition & 0 deletions components/home/HomeRows.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions components/tvshows/TVEpisodeRow.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
3 changes: 3 additions & 0 deletions components/tvshows/TVEpisodeRow.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
6 changes: 6 additions & 0 deletions components/tvshows/TVEpisodeRowWithOptions.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions components/tvshows/TVEpisodeRowWithOptions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
8 changes: 8 additions & 0 deletions components/tvshows/TVEpisodes.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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)
Expand Down Expand Up @@ -73,13 +79,15 @@ end function
' OnScreenShown: Callback function when view is presented on screen
'
sub OnScreenShown()

if m.isFirstRun
m.isFirstRun = false
return
end if

m.tvEpisodeRow.setFocus(true)
m.top.refreshSeasonDetailsData = not m.top.refreshSeasonDetailsData

end sub

' Handle navigation input from the remote and act on it
Expand Down
3 changes: 3 additions & 0 deletions components/tvshows/TVEpisodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
12 changes: 11 additions & 1 deletion components/video/OSD.bs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,17 @@ 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 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

Expand Down
2 changes: 1 addition & 1 deletion components/video/OSD.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
47 changes: 46 additions & 1 deletion locale/en_US/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>What should clicking OK on a Next Up episode do.</source>
jimdogx marked this conversation as resolved.
Show resolved Hide resolved
<translation>What should clicking OK on a Next Up episode do.</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>
38 changes: 38 additions & 0 deletions settings/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,23 @@
}
]
},
{
"title": "Episode Next Up Behavior",
"description": "What should clicking OK on a Next Up episode do.",
"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.",
Expand All @@ -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.",
Expand Down
39 changes: 28 additions & 11 deletions source/Main.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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") })

Expand Down Expand Up @@ -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"))
Expand Down Expand Up @@ -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"))
Expand Down
10 changes: 8 additions & 2 deletions source/ShowScenes.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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
Expand Down
18 changes: 18 additions & 0 deletions source/utils/misc.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading