From 34b0978a7d8756e7860af43295a2c72442ed0458 Mon Sep 17 00:00:00 2001 From: Reynolds Date: Fri, 11 Oct 2024 10:28:06 -0400 Subject: [PATCH 1/6] Initial Changes to Support Next-Up Behavior --- components/home/HomeRows.bs | 1 + components/tvshows/TVEpisodeRow.bs | 17 ++++++++ components/tvshows/TVEpisodeRow.xml | 3 ++ components/tvshows/TVEpisodeRowWithOptions.bs | 6 +++ .../tvshows/TVEpisodeRowWithOptions.xml | 3 ++ components/tvshows/TVEpisodes.bs | 8 ++++ components/tvshows/TVEpisodes.xml | 3 ++ locale/en_US/translations.ts | 27 ++++++++++++- settings/settings.json | 21 ++++++++++ source/Main.bs | 39 +++++++++++++------ source/ShowScenes.bs | 10 ++++- 11 files changed, 124 insertions(+), 14 deletions(-) diff --git a/components/home/HomeRows.bs b/components/home/HomeRows.bs index 598a08961..90522eb8d 100644 --- a/components/home/HomeRows.bs +++ b/components/home/HomeRows.bs @@ -686,6 +686,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 diff --git a/components/tvshows/TVEpisodeRow.bs b/components/tvshows/TVEpisodeRow.bs index 5782723f9..62cfb143e 100644 --- a/components/tvshows/TVEpisodeRow.bs +++ b/components/tvshows/TVEpisodeRow.bs @@ -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] diff --git a/components/tvshows/TVEpisodeRow.xml b/components/tvshows/TVEpisodeRow.xml index 0e14bc3a8..9885529c9 100644 --- a/components/tvshows/TVEpisodeRow.xml +++ b/components/tvshows/TVEpisodeRow.xml @@ -4,5 +4,8 @@ + + + \ No newline at end of file diff --git a/components/tvshows/TVEpisodeRowWithOptions.bs b/components/tvshows/TVEpisodeRowWithOptions.bs index d982d82c7..6c68fde79 100644 --- a/components/tvshows/TVEpisodeRowWithOptions.bs +++ b/components/tvshows/TVEpisodeRowWithOptions.bs @@ -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 diff --git a/components/tvshows/TVEpisodeRowWithOptions.xml b/components/tvshows/TVEpisodeRowWithOptions.xml index 008720972..6b6c904d4 100644 --- a/components/tvshows/TVEpisodeRowWithOptions.xml +++ b/components/tvshows/TVEpisodeRowWithOptions.xml @@ -8,5 +8,8 @@ + + + \ No newline at end of file diff --git a/components/tvshows/TVEpisodes.bs b/components/tvshows/TVEpisodes.bs index 95b98f02e..916a1fca6 100644 --- a/components/tvshows/TVEpisodes.bs +++ b/components/tvshows/TVEpisodes.bs @@ -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 diff --git a/components/tvshows/TVEpisodes.xml b/components/tvshows/TVEpisodes.xml index 1c0cfefc8..2213b49dc 100644 --- a/components/tvshows/TVEpisodes.xml +++ b/components/tvshows/TVEpisodes.xml @@ -19,6 +19,9 @@ + + + \ No newline at end of file diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index 410ea54e3..99a329625 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1321,5 +1321,30 @@ Use Show Image User Setting - Setting option title + + Episode Next Up Behavior + Episode Next Up Behavior + User Setting - Setting option title + + + What should clicking a Next Up episode do. + What should clicking a Next Up episode do. + User Setting - Setting description + + + Play Episode Immediately + Play Episode Immediately + User Setting - Setting option title + + + View Episode Details + View Episode Details + User Setting - Setting option title + + + View Season Details + View Season Details + User Setting - Setting option title + - + \ No newline at end of file diff --git a/settings/settings.json b/settings/settings.json index e3b01eee9..de50d62eb 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -262,6 +262,27 @@ } ] }, + { + "title": "Episode Next Up Behavior", + "description": "What should clicking a Next Up episode do.", + "settingName": "ui.general.episodenextupbehavior", + "type": "radio", + "default": "play", + "options": [ + { + "title": "Play Episode Immediately", + "id": "play" + }, + { + "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.", diff --git a/source/Main.bs b/source/Main.bs index 232532e2b..3ee6aba1a 100644 --- a/source/Main.bs +++ b/source/Main.bs @@ -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") }) @@ -304,22 +304,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 localGlobal.session.user.settings["ui.general.episodenextupbehavior"] = "play" or 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")) @@ -860,7 +877,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")) diff --git a/source/ShowScenes.bs b/source/ShowScenes.bs index 4aa14f8e6..1829e928b 100644 --- a/source/ShowScenes.bs +++ b/source/ShowScenes.bs @@ -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 From 97961b315bad7045f741895b4a223651166a3191 Mon Sep 17 00:00:00 2001 From: Reynolds Date: Fri, 11 Oct 2024 12:56:41 -0400 Subject: [PATCH 2/6] Added Settings for Remaining Time Display This adds a setting with controls the type of data displayed for remaining time: remaining time, completion time of day, or both. --- components/video/OSD.bs | 12 +++++++++++- components/video/OSD.xml | 2 +- locale/en_US/translations.ts | 25 +++++++++++++++++++++++++ settings/settings.json | 21 +++++++++++++++++++++ source/utils/misc.bs | 18 ++++++++++++++++++ 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/components/video/OSD.bs b/components/video/OSD.bs index 4f687c7a4..446d3b65b 100644 --- a/components/video/OSD.bs +++ b/components/video/OSD.bs @@ -42,7 +42,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 diff --git a/components/video/OSD.xml b/components/video/OSD.xml index 4093701d0..e80eb64e6 100644 --- a/components/video/OSD.xml +++ b/components/video/OSD.xml @@ -34,7 +34,7 @@