From abae59d2b79c254593db4adc6266fbdd0d2c1898 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 18:45:59 +0000 Subject: [PATCH 01/27] Update dependency brighterscript to v0.69.7 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f531fcb..45019064 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@rokucommunity/bslint": "0.8.28", - "brighterscript": "0.69.6", + "brighterscript": "0.69.7", "jshint": "2.13.6", "rimraf": "6.0.1", "roku-deploy": "3.12.4", @@ -645,9 +645,9 @@ } }, "node_modules/brighterscript": { - "version": "0.69.6", - "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.69.6.tgz", - "integrity": "sha512-bS7EmxlAMCfPXNUqC19a1vmVFFm6Occ50DRACGOtiO801MXQxKq3KWlB9y6pefHWLIKkZLZVmfl5Ss203T6y3Q==", + "version": "0.69.7", + "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.69.7.tgz", + "integrity": "sha512-9VwFoCy1lKT6xYKT3nTbHPTROz+G5n39v2oZiT6MiLXxjxMVHtyM24aCYaSVdEBADBG4LcKzBkY7mrgDnPUerA==", "license": "MIT", "dependencies": { "@rokucommunity/bslib": "^0.1.1", diff --git a/package.json b/package.json index ef369f74..c03ce5e3 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "devDependencies": { "@rokucommunity/bslint": "0.8.28", - "brighterscript": "0.69.6", + "brighterscript": "0.69.7", "jshint": "2.13.6", "rimraf": "6.0.1", "roku-deploy": "3.12.4", From 93e68759315d847135f300f99388511e42299a7d Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Wed, 23 Apr 2025 15:05:33 -0400 Subject: [PATCH 02/27] Dev images --- manifest | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest b/manifest index b62a1b69..ca24ab09 100644 --- a/manifest +++ b/manifest @@ -7,9 +7,9 @@ build_version=2 ### Main Menu Icons / Channel Poster Artwork -mm_icon_focus_fhd=pkg:/images/channel-poster_fhd.png -mm_icon_focus_hd=pkg:/images/channel-poster_hd.png -mm_icon_focus_sd=pkg:/images/channel-poster_sd.png +mm_icon_focus_fhd=pkg:/images/channel-poster_fhd_dev.png +mm_icon_focus_hd=pkg:/images/channel-poster_hd_dev.png +mm_icon_focus_sd=pkg:/images/channel-poster_sd_dev.png ### Splash Screen + Loading Screen Artwork From c139d3ce9bf672fe4126dc4bc383f0dc1e2310d7 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:27:08 -0400 Subject: [PATCH 03/27] Add setting for cursor color --- components/AudioMiniPlayer.bs | 2 +- components/Buttons/JFButtons.bs | 4 ++-- components/ItemGrid/Alpha.bs | 3 ++- components/ItemGrid/ItemGrid.bs | 2 +- components/ItemGrid/ItemGridOptions.bs | 8 ++++---- components/ItemGrid/LibraryFilterDialog.bs | 6 +++--- components/JFMessageDialog.bs | 3 ++- components/JFOverhang.bs | 2 +- components/Libraries/AudioBookLibraryView.bs | 2 +- components/Libraries/MusicLibraryView.bs | 14 +++++++------- components/Libraries/VisualLibraryScene.bs | 14 +++++++------- components/MovieDetailButton.bs | 3 ++- components/WhatsNewDialog.bs | 5 +++-- components/data/SceneManager.bs | 10 +++++----- components/extras/ExtrasRowList.bs | 2 +- components/home/Home.bs | 9 ++------- components/home/Home.xml | 4 ---- components/liveTv/LiveTVLibraryView.bs | 2 +- components/liveTv/ProgramDetails.bs | 10 +++++----- components/liveTv/schedule.bs | 2 +- components/login/UserRow.bs | 3 ++- components/movies/MovieDetails.bs | 2 +- components/movies/MovieOptions.bs | 8 ++++---- components/music/AlbumView.bs | 6 +++--- components/music/ArtistView.bs | 10 +++++----- components/music/AudioPlayerView.bs | 10 +++++----- components/music/PlaylistView.bs | 2 +- components/settings/settings.bs | 6 +++--- components/subtitle/SubtitleSearchView.bs | 6 +++--- components/tvshows/TVEpisodes.bs | 2 +- components/tvshows/TVListOptions.bs | 4 ++-- components/tvshows/TVSeasonRow.bs | 3 ++- components/video/OSD.bs | 6 +++--- components/video/VideoPlayerView.bs | 4 ++-- settings/settings.json | 7 +++++++ source/ShowScenes.bs | 2 +- 36 files changed, 96 insertions(+), 92 deletions(-) diff --git a/components/AudioMiniPlayer.bs b/components/AudioMiniPlayer.bs index 8d786c59..0f235d18 100644 --- a/components/AudioMiniPlayer.bs +++ b/components/AudioMiniPlayer.bs @@ -58,7 +58,7 @@ sub onButtonSelectedChange() ' Change selected button image to selected image selectedButton = m.buttons.getChild(m.selectedButtonIndex) if isValid(selectedButton) - selectedButton.blendColor = ColorPalette.HIGHLIGHT + selectedButton.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) selectedButton.opacity = 1 end if end sub diff --git a/components/Buttons/JFButtons.bs b/components/Buttons/JFButtons.bs index 403cfee5..41ab3d40 100644 --- a/components/Buttons/JFButtons.bs +++ b/components/Buttons/JFButtons.bs @@ -18,7 +18,7 @@ sub init() m.focusAnimHeight = m.top.findNode("focusHeight") ' Set button color to global - m.focusRing.color = ColorPalette.HIGHLIGHT + m.focusRing.color = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.buttonCount = 0 m.selectedFocusedIndex = 0 @@ -98,7 +98,7 @@ end sub ' Change opacity of the highlighted menu item based on focus sub focusChanged() if m.top.isInFocusChain() - m.focusRing.color = ColorPalette.HIGHLIGHT + m.focusRing.color = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) setButtonColor(m.selectedFocusedIndex, ColorPalette.WHITE) else m.focusRing.color = ColorPalette.LIGHTBLUE diff --git a/components/ItemGrid/Alpha.bs b/components/ItemGrid/Alpha.bs index 41c94e57..5bf5b0ef 100644 --- a/components/ItemGrid/Alpha.bs +++ b/components/ItemGrid/Alpha.bs @@ -1,5 +1,6 @@ import "pkg:/source/enums/ColorPalette.bs" import "pkg:/source/enums/KeyCode.bs" +import "pkg:/source/utils/misc.bs" sub init() m.top.setFocus(false) @@ -7,7 +8,7 @@ sub init() m.alphaText = m.top.findNode("alphaText") m.alphaMenu = m.top.findNode("alphaMenu") - m.alphaMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.alphaMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.alphaMenu.focusFootprintBlendColor = ColorPalette.TRANSPARENT m.alphaMenu.setFocus(false) diff --git a/components/ItemGrid/ItemGrid.bs b/components/ItemGrid/ItemGrid.bs index 59722255..49bab308 100644 --- a/components/ItemGrid/ItemGrid.bs +++ b/components/ItemGrid/ItemGrid.bs @@ -98,7 +98,7 @@ sub createItemGrid() m.itemGrid.itemSize = "[230, 345]" m.itemGrid.itemSpacing = "[20, 20]" m.itemGrid.drawFocusFeedback = "true" - m.itemGrid.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.itemGrid.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.appendChild(m.itemGrid) m.itemGrid.observeField("itemFocused", "onItemFocused") m.itemGrid.observeField("itemSelected", "onItemSelected") diff --git a/components/ItemGrid/ItemGridOptions.bs b/components/ItemGrid/ItemGridOptions.bs index 2a2f73d8..a4016f5e 100644 --- a/components/ItemGrid/ItemGridOptions.bs +++ b/components/ItemGrid/ItemGridOptions.bs @@ -24,17 +24,17 @@ sub init() m.selectedItem = MenuType.SORT viewMenu = m.top.findNode("viewMenu") - viewMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + viewMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) viewMenu.focusFootprintBlendColor = ColorPalette.LIGHTHIGHLIGHT viewMenu.focusedColor = ColorPalette.WHITE sortMenu = m.top.findNode("sortMenu") - sortMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + sortMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) sortMenu.focusFootprintBlendColor = ColorPalette.LIGHTHIGHLIGHT sortMenu.focusedColor = ColorPalette.WHITE m.filterMenu = m.top.findNode("filterMenu") - m.filterMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.filterMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.filterMenu.focusFootprintBlendColor = ColorPalette.LIGHTHIGHLIGHT m.filterMenu.focusedColor = ColorPalette.WHITE @@ -47,7 +47,7 @@ sub init() m.menus.push(m.filterMenu) m.filterOptions = m.top.findNode("filterOptions") - m.filterOptions.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.filterOptions.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.filterOptions.focusFootprintBlendColor = ColorPalette.LIGHTHIGHLIGHT m.filterOptions.focusedColor = ColorPalette.WHITE diff --git a/components/ItemGrid/LibraryFilterDialog.bs b/components/ItemGrid/LibraryFilterDialog.bs index 2ce603a7..682836a9 100644 --- a/components/ItemGrid/LibraryFilterDialog.bs +++ b/components/ItemGrid/LibraryFilterDialog.bs @@ -11,7 +11,7 @@ sub init() m.submitButton = m.top.findNode("submitButton") m.submitButton.background = ColorPalette.LIGHTBLUE m.submitButton.color = ColorPalette.DARKGREY - m.submitButton.focusBackground = ColorPalette.HIGHLIGHT + m.submitButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.submitButton.focusColor = ColorPalette.WHITE overlay = m.top.findNode("overlay") @@ -27,7 +27,7 @@ sub init() headerBorder.color = ColorPalette.LIGHTBLUE m.filterMenu = m.top.findNode("filterMenu") - m.filterMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.filterMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.filterMenu.focusFootprintBlendColor = ColorPalette.LIGHTHIGHLIGHT m.filterMenu.focusedColor = ColorPalette.WHITE @@ -35,7 +35,7 @@ sub init() filterOptionsBackdrop.color = ColorPalette.LIGHTGREY m.filterOptionsMenu = m.top.findNode("filterOptionsMenu") - m.filterOptionsMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.filterOptionsMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.filterOptionsMenu.focusFootprintBlendColor = ColorPalette.LIGHTHIGHLIGHT m.filterOptionsMenu.focusedColor = ColorPalette.WHITE diff --git a/components/JFMessageDialog.bs b/components/JFMessageDialog.bs index 1a215639..e82c8e0d 100644 --- a/components/JFMessageDialog.bs +++ b/components/JFMessageDialog.bs @@ -1,8 +1,9 @@ import "pkg:/source/enums/ColorPalette.bs" +import "pkg:/source/utils/misc.bs" sub init() options = m.top.findNode("optionList") - options.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + options.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) options.color = "0xffffff" options.focusedColor = "0xffffff" options.setFocus(true) diff --git a/components/JFOverhang.bs b/components/JFOverhang.bs index df61d575..eba1927d 100644 --- a/components/JFOverhang.bs +++ b/components/JFOverhang.bs @@ -20,7 +20,7 @@ sub init() m.overlayCurrentUserSelection = m.top.findNode("overlayCurrentUserSelection") if isValid(m.overlayCurrentUserSelection) - m.overlayCurrentUserSelection.blendColor = ColorPalette.HIGHLIGHT + m.overlayCurrentUserSelection.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end if end sub diff --git a/components/Libraries/AudioBookLibraryView.bs b/components/Libraries/AudioBookLibraryView.bs index 6912fbc4..a52d2425 100644 --- a/components/Libraries/AudioBookLibraryView.bs +++ b/components/Libraries/AudioBookLibraryView.bs @@ -19,7 +19,7 @@ sub init() m.newBackdrop = m.top.findNode("backdropTransition") m.emptyText = m.top.findNode("emptyText") - m.itemGrid.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.itemGrid.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.genreList = m.top.findNode("genrelist") m.genreList.observeField("itemSelected", "onGenreItemSelected") diff --git a/components/Libraries/MusicLibraryView.bs b/components/Libraries/MusicLibraryView.bs index c6143269..abace9ea 100644 --- a/components/Libraries/MusicLibraryView.bs +++ b/components/Libraries/MusicLibraryView.bs @@ -32,39 +32,39 @@ sub setupNodes() m.searchButton.textColor = ColorPalette.DARKGREY m.searchButton.focusTextColor = ColorPalette.WHITE m.searchButton.background = ColorPalette.WHITE - m.searchButton.focusBackground = ColorPalette.HIGHLIGHT + m.searchButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.sortButton = m.top.findNode("sortButton") m.sortButton.textColor = ColorPalette.DARKGREY m.sortButton.focusTextColor = ColorPalette.WHITE m.sortButton.background = ColorPalette.WHITE - m.sortButton.focusBackground = ColorPalette.HIGHLIGHT + m.sortButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.sortOrderButton = m.top.findNode("sortOrderButton") m.sortOrderButton.textColor = ColorPalette.DARKGREY m.sortOrderButton.focusTextColor = ColorPalette.WHITE m.sortOrderButton.background = ColorPalette.WHITE - m.sortOrderButton.focusBackground = ColorPalette.HIGHLIGHT + m.sortOrderButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.filterButton = m.top.findNode("filterButton") m.filterButton.textColor = ColorPalette.DARKGREY m.filterButton.focusTextColor = ColorPalette.WHITE m.filterButton.background = ColorPalette.WHITE - m.filterButton.focusBackground = ColorPalette.HIGHLIGHT + m.filterButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.viewButton = m.top.findNode("viewButton") m.viewButton.textColor = ColorPalette.DARKGREY m.viewButton.focusTextColor = ColorPalette.WHITE m.viewButton.background = ColorPalette.WHITE - m.viewButton.focusBackground = ColorPalette.HIGHLIGHT + m.viewButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end sub sub init() setupNodes() m.bypassSearchEvent = false - m.itemGrid.focusBitmapBlendColor = ColorPalette.HIGHLIGHT - m.genrelist.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.itemGrid.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) + m.genrelist.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.findNode("VoiceBoxCover").color = chainLookupReturn(m.global.session, "user.settings.colorBackground", ColorPalette.VIEWBACKGROUND) m.overhang.isVisible = false diff --git a/components/Libraries/VisualLibraryScene.bs b/components/Libraries/VisualLibraryScene.bs index 43a659a1..d4450aa8 100644 --- a/components/Libraries/VisualLibraryScene.bs +++ b/components/Libraries/VisualLibraryScene.bs @@ -39,31 +39,31 @@ sub setupNodes() m.searchButton.textColor = ColorPalette.DARKGREY m.searchButton.focusTextColor = ColorPalette.WHITE m.searchButton.background = ColorPalette.WHITE - m.searchButton.focusBackground = ColorPalette.HIGHLIGHT + m.searchButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.sortButton = m.top.findNode("sortButton") m.sortButton.textColor = ColorPalette.DARKGREY m.sortButton.focusTextColor = ColorPalette.WHITE m.sortButton.background = ColorPalette.WHITE - m.sortButton.focusBackground = ColorPalette.HIGHLIGHT + m.sortButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.sortOrderButton = m.top.findNode("sortOrderButton") m.sortOrderButton.textColor = ColorPalette.DARKGREY m.sortOrderButton.focusTextColor = ColorPalette.WHITE m.sortOrderButton.background = ColorPalette.WHITE - m.sortOrderButton.focusBackground = ColorPalette.HIGHLIGHT + m.sortOrderButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.filterButton = m.top.findNode("filterButton") m.filterButton.textColor = ColorPalette.DARKGREY m.filterButton.focusTextColor = ColorPalette.WHITE m.filterButton.background = ColorPalette.WHITE - m.filterButton.focusBackground = ColorPalette.HIGHLIGHT + m.filterButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.viewButton = m.top.findNode("viewButton") m.viewButton.textColor = ColorPalette.DARKGREY m.viewButton.focusTextColor = ColorPalette.WHITE m.viewButton.background = ColorPalette.WHITE - m.viewButton.focusBackground = ColorPalette.HIGHLIGHT + m.viewButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end sub sub init() @@ -71,8 +71,8 @@ sub init() setupNodes() m.bypassSearchEvent = false - m.itemGrid.focusBitmapBlendColor = ColorPalette.HIGHLIGHT - m.genreList.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.itemGrid.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) + m.genreList.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.findNode("VoiceBoxCover").color = chainLookupReturn(m.global.session, "user.settings.colorBackground", ColorPalette.VIEWBACKGROUND) m.overhang.isVisible = false diff --git a/components/MovieDetailButton.bs b/components/MovieDetailButton.bs index 509577aa..28dedf19 100644 --- a/components/MovieDetailButton.bs +++ b/components/MovieDetailButton.bs @@ -1,4 +1,5 @@ import "pkg:/source/enums/ColorPalette.bs" +import "pkg:/source/utils/misc.bs" sub init() m.buttonBackground = m.top.findNode("buttonBackground") @@ -20,7 +21,7 @@ sub itemContentChanged() m.focusTextColor = ColorPalette.WHITE m.textColor = ColorPalette.WHITE - m.focusBackground = ColorPalette.HIGHLIGHT + m.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.focusIcon = itemData.focusIcon m.icon = itemData.icon m.background = ColorPalette.TRANSPARENT diff --git a/components/WhatsNewDialog.bs b/components/WhatsNewDialog.bs index 7323124c..f30c139e 100644 --- a/components/WhatsNewDialog.bs +++ b/components/WhatsNewDialog.bs @@ -1,4 +1,5 @@ import "pkg:/source/enums/ColorPalette.bs" +import "pkg:/source/utils/misc.bs" sub init() m.content = m.top.findNode("content") @@ -24,7 +25,7 @@ sub init() "author": { "fontSize": 27, "fontUri": "font:SystemFontFile", - "color": ColorPalette.HIGHLIGHT + "color": chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) } } @@ -44,7 +45,7 @@ sub setPalette() DialogFocusColor: "0xcececeFF", DialogFocusItemColor: "0x202020FF", DialogSecondaryTextColor: "0xf8f8f8ff", - DialogSecondaryItemColor: ColorPalette.HIGHLIGHT, + DialogSecondaryItemColor: chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT), DialogTextColor: "0xeeeeeeFF" } diff --git a/components/data/SceneManager.bs b/components/data/SceneManager.bs index ccd819fb..9abfba0c 100644 --- a/components/data/SceneManager.bs +++ b/components/data/SceneManager.bs @@ -283,7 +283,7 @@ sub standardDialog(title, message) dlgPalette = createObject("roSGNode", "RSGPalette") dlgPalette.colors = { DialogBackgroundColor: ColorPalette.ELEMENTBACKGROUND, - DialogFocusColor: ColorPalette.HIGHLIGHT, + DialogFocusColor: chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT), DialogFocusItemColor: ColorPalette.WHITE, DialogSecondaryTextColor: ColorPalette.WHITE, DialogSecondaryItemColor: ColorPalette.LIGHTBLUE, @@ -316,7 +316,7 @@ sub radioDialog(title, message) dlgPalette = createObject("roSGNode", "RSGPalette") dlgPalette.colors = { DialogBackgroundColor: ColorPalette.ELEMENTBACKGROUND, - DialogFocusColor: ColorPalette.HIGHLIGHT, + DialogFocusColor: chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT), DialogFocusItemColor: ColorPalette.WHITE, DialogSecondaryTextColor: ColorPalette.WHITE, DialogSecondaryItemColor: ColorPalette.LIGHTBLUE, @@ -344,7 +344,7 @@ sub remoteSubtitleDialog(title, message) dlgPalette = createObject("roSGNode", "RSGPalette") dlgPalette.colors = { DialogBackgroundColor: ColorPalette.ELEMENTBACKGROUND, - DialogFocusColor: ColorPalette.HIGHLIGHT, + DialogFocusColor: chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT), DialogFocusItemColor: ColorPalette.WHITE, DialogSecondaryTextColor: ColorPalette.WHITE, DialogSecondaryItemColor: ColorPalette.LIGHTBLUE, @@ -371,7 +371,7 @@ sub keyboardDialog(id, title, message, buttons, hintText, itemID) dlgPalette = createObject("roSGNode", "RSGPalette") dlgPalette.colors = { DialogBackgroundColor: ColorPalette.ELEMENTBACKGROUND, - DialogFocusColor: ColorPalette.HIGHLIGHT, + DialogFocusColor: chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT), DialogFocusItemColor: ColorPalette.WHITE, DialogSecondaryTextColor: ColorPalette.WHITE, DialogSecondaryItemColor: ColorPalette.LIGHTBLUE, @@ -411,7 +411,7 @@ sub optionDialog(id, title, message, buttons, params) dlgPalette = createObject("roSGNode", "RSGPalette") dlgPalette.colors = { DialogBackgroundColor: ColorPalette.ELEMENTBACKGROUND, - DialogFocusColor: ColorPalette.HIGHLIGHT, + DialogFocusColor: chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT), DialogFocusItemColor: ColorPalette.WHITE, DialogSecondaryTextColor: ColorPalette.WHITE, DialogSecondaryItemColor: ColorPalette.LIGHTBLUE, diff --git a/components/extras/ExtrasRowList.bs b/components/extras/ExtrasRowList.bs index ca3b62df..7db9d55e 100644 --- a/components/extras/ExtrasRowList.bs +++ b/components/extras/ExtrasRowList.bs @@ -8,7 +8,7 @@ import "pkg:/source/utils/misc.bs" sub init() m.top.visible = true - m.top.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.top.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) updateSize() m.top.observeField("rowItemSelected", "onRowItemSelected") diff --git a/components/home/Home.bs b/components/home/Home.bs index 79969625..d1da8c34 100644 --- a/components/home/Home.bs +++ b/components/home/Home.bs @@ -19,21 +19,16 @@ sub init() m.loadItemsTask1.itemsToLoad = "isInMyList" m.homeRows = m.top.findNode("homeRows") - animationInterpolator = m.top.findNode("fadeInFocusBitmapInterpolator") - animationInterpolator.keyValue = `[${ColorPalette.TRANSPARENT}, ${ColorPalette.HIGHLIGHT}]` - - m.fadeInFocusBitmap = m.top.findNode("fadeInFocusBitmap") + m.homeRows.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end sub sub refresh() - m.homeRows.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.homeRows.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.homeRows.callFunc("updateHomeRows") end sub sub loadLibraries() - m.homeRows.focusBitmapBlendColor = ColorPalette.TRANSPARENT m.homeRows.callFunc("loadLibraries") - m.fadeInFocusBitmap.control = "start" end sub ' JFScreen hook called when the screen is displayed by the screen manager diff --git a/components/home/Home.xml b/components/home/Home.xml index 8c680e57..682ca96b 100644 --- a/components/home/Home.xml +++ b/components/home/Home.xml @@ -11,10 +11,6 @@ numRows="2" rowLabelOffset="[[0,20]]" /> - - - - diff --git a/components/liveTv/LiveTVLibraryView.bs b/components/liveTv/LiveTVLibraryView.bs index bc4d3f68..0dcdf56e 100644 --- a/components/liveTv/LiveTVLibraryView.bs +++ b/components/liveTv/LiveTVLibraryView.bs @@ -30,7 +30,7 @@ sub init() m.data = CreateObject("roSGNode", "ContentNode") m.itemGrid = m.top.findNode("itemGrid") - m.itemGrid.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.itemGrid.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.itemGrid.observeField("itemFocused", "onItemFocused") m.itemGrid.observeField("itemSelected", "onItemSelected") m.itemGrid.content = m.data diff --git a/components/liveTv/ProgramDetails.bs b/components/liveTv/ProgramDetails.bs index 3d006544..e206ff15 100644 --- a/components/liveTv/ProgramDetails.bs +++ b/components/liveTv/ProgramDetails.bs @@ -299,7 +299,7 @@ sub focusChanged() m.viewChannelOutline.visible = true m.recordOutline.visible = false m.recordSeriesOutline.visible = false - m.viewChannelButtonBackground.blendColor = ColorPalette.HIGHLIGHT + m.viewChannelButtonBackground.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.recordButtonBackground.blendColor = "#000000" m.recordSeriesButtonBackground.blendColor = "#000000" else @@ -343,7 +343,7 @@ function onKeyEvent(key as string, press as boolean) as boolean m.viewChannelOutline.visible = false m.recordOutline.visible = true m.viewChannelButtonBackground.blendColor = "#000000" - m.recordButtonBackground.blendColor = ColorPalette.HIGHLIGHT + m.recordButtonBackground.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.recordSeriesButtonBackground.blendColor = "#000000" return true else if key = "right" and m.recordButton.hasFocus() @@ -354,7 +354,7 @@ function onKeyEvent(key as string, press as boolean) as boolean m.recordSeriesOutline.visible = true m.viewChannelButtonBackground.blendColor = "#000000" m.recordButtonBackground.blendColor = "#000000" - m.recordSeriesButtonBackground.blendColor = ColorPalette.HIGHLIGHT + m.recordSeriesButtonBackground.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) return true else if key = "left" and m.recordSeriesButton.hasFocus() m.recordButton.setFocus(true) @@ -363,7 +363,7 @@ function onKeyEvent(key as string, press as boolean) as boolean m.recordOutline.visible = true m.recordSeriesOutline.visible = false m.viewChannelButtonBackground.blendColor = "#000000" - m.recordButtonBackground.blendColor = ColorPalette.HIGHLIGHT + m.recordButtonBackground.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.recordSeriesButtonBackground.blendColor = "#000000" return true else if key = "left" and m.recordButton.hasFocus() @@ -372,7 +372,7 @@ function onKeyEvent(key as string, press as boolean) as boolean group.lastFocus = m.viewChannelButton m.viewChannelOutline.visible = true m.recordOutline.visible = false - m.viewChannelButtonBackground.blendColor = ColorPalette.HIGHLIGHT + m.viewChannelButtonBackground.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.recordButtonBackground.blendColor = "#000000" m.recordSeriesButtonBackground.blendColor = "#000000" return true diff --git a/components/liveTv/schedule.bs b/components/liveTv/schedule.bs index fae6686b..9bf16334 100644 --- a/components/liveTv/schedule.bs +++ b/components/liveTv/schedule.bs @@ -29,7 +29,7 @@ sub init() m.scheduleGrid.observeField("leftEdgeTargetTime", "onGridScrolled") m.scheduleGrid.channelInfoWidth = 350 - m.scheduleGrid.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.scheduleGrid.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.scheduleGrid.programTitleFocusedColor = ColorPalette.WHITE m.scheduleGrid.timeLabelColor = ColorPalette.LIGHTGREY m.scheduleGrid.timeLabelFont.size = 30 diff --git a/components/login/UserRow.bs b/components/login/UserRow.bs index c066c7d3..58ba98e4 100644 --- a/components/login/UserRow.bs +++ b/components/login/UserRow.bs @@ -1,7 +1,8 @@ import "pkg:/source/enums/ColorPalette.bs" +import "pkg:/source/utils/misc.bs" sub init() - m.top.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.top.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.itemComponentName = "UserItem" m.top.content = SetData() m.top.observeField("itemSelected", "SetUser") diff --git a/components/movies/MovieDetails.bs b/components/movies/MovieDetails.bs index 1b6a2878..ab6f248a 100644 --- a/components/movies/MovieDetails.bs +++ b/components/movies/MovieDetails.bs @@ -100,7 +100,7 @@ end sub sub setButtonColors() m.buttonGrp.focusBitmapUri = "pkg:/images/white.9.png" - m.buttonGrp.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.buttonGrp.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end sub ' OnScreenShown: Callback function when view is presented on screen diff --git a/components/movies/MovieOptions.bs b/components/movies/MovieOptions.bs index 84d62b20..90d2d0e4 100644 --- a/components/movies/MovieOptions.bs +++ b/components/movies/MovieOptions.bs @@ -17,7 +17,7 @@ sub init() m.selectedSubtitleIndex = -1 m.optionMenu = m.top.findNode("optionMenu") - m.optionMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.optionMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.optionMenu.focusedColor = ColorPalette.WHITE m.menus = [m.top.findNode("subtitleMenu"), m.top.findNode("audioMenu"), m.top.findNode("videoMenu"), m.optionMenu] @@ -25,13 +25,13 @@ sub init() m.videoNames = [] m.audioNames = [] - m.top.findNode("videoMenu").focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.top.findNode("videoMenu").focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.findNode("videoMenu").focusedColor = ColorPalette.WHITE - m.top.findNode("audioMenu").focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.top.findNode("audioMenu").focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.findNode("audioMenu").focusedColor = ColorPalette.WHITE - m.top.findNode("subtitleMenu").focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.top.findNode("subtitleMenu").focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.findNode("subtitleMenu").focusedColor = ColorPalette.WHITE ' Animation diff --git a/components/music/AlbumView.bs b/components/music/AlbumView.bs index 132fbe1f..ef6ac3e1 100644 --- a/components/music/AlbumView.bs +++ b/components/music/AlbumView.bs @@ -11,7 +11,7 @@ sub init() m.instantMix = m.top.findNode("instantMix") m.instantMix.background = ColorPalette.LIGHTBLUE m.instantMix.color = ColorPalette.DARKGREY - m.instantMix.focusBackground = ColorPalette.HIGHLIGHT + m.instantMix.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.instantMix.focusColor = ColorPalette.WHITE m.albumCover = m.top.findNode("albumCover") @@ -21,7 +21,7 @@ sub init() m.top.lastFocus = m.songList m.songList.focusFootprintBitmapUri = string.EMPTY - m.songList.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.songList.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.songList.focusFootprintBlendColor = ColorPalette.LIGHTHIGHLIGHT m.songListRect.color = ColorPalette.ELEMENTBACKGROUND @@ -152,7 +152,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if m.songList.hasFocus() if m.dscr.isTextEllipsized m.dscr.setFocus(true) - m.dscr.color = ColorPalette.HIGHLIGHT + m.dscr.color = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) return true end if end if diff --git a/components/music/ArtistView.bs b/components/music/ArtistView.bs index b82e02b4..690d7639 100644 --- a/components/music/ArtistView.bs +++ b/components/music/ArtistView.bs @@ -17,7 +17,7 @@ sub init() m.appearsOnHeader.text = tr("AppearsOn") m.similarArtist = m.top.findNode("similarArtist") - m.similarArtist.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.similarArtist.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.similarArtist.observeField("escape", "onSimilarArtistEscape") m.LoadSimilarArtistTask = CreateObject("roSGNode", "LoadItemsTask") @@ -25,12 +25,12 @@ sub init() m.LoadSimilarArtistTask.observeField("content", "onSimilarArtistLoaded") m.appearsOn = m.top.findNode("appearsOn") - m.appearsOn.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.appearsOn.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.appearsOn.observeField("escape", "onAppearsOnEscape") m.appearsOn.observeField("MusicArtistAlbumData", "onAppearsOnData") m.albums = m.top.findNode("albums") - m.albums.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.albums.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.albums.observeField("escape", "onAlbumsEscape") m.albums.observeField("MusicArtistAlbumData", "onAlbumsData") @@ -161,7 +161,7 @@ sub setupButtons() m.buttonCount = m.buttonGrp.getChildCount() m.playButton = m.top.findNode("play") - m.playButton.focusBackground = ColorPalette.HIGHLIGHT + m.playButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.previouslySelectedButtonIndex = -1 buttons = ["instantMix", "albumsLink", "appearsOnLink", "similarArtistLink", "detailsLink"] @@ -169,7 +169,7 @@ sub setupButtons() for each button in buttons thisButton = m.top.findNode(button) if isValid(thisButton) - thisButton.focusBackground = ColorPalette.HIGHLIGHT + thisButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end if end for diff --git a/components/music/AudioPlayerView.bs b/components/music/AudioPlayerView.bs index 3616e7d1..61a33209 100644 --- a/components/music/AudioPlayerView.bs +++ b/components/music/AudioPlayerView.bs @@ -169,7 +169,7 @@ sub onButtonSelectedChange() ' Change selected button image to selected image selectedButton = m.buttons.getChild(m.top.selectedButtonIndex) - selectedButton.blendColor = ColorPalette.HIGHLIGHT + selectedButton.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end sub sub setupInfoNodes() @@ -648,7 +648,7 @@ end sub ' @param {string} newState - state to replace {oldState} with in icon url sub setSelectedButtonState(newState as string) selectedButton = m.buttons.getChild(m.top.selectedButtonIndex) - selectedButton.blendColor = newState = ButtonState.SELECTED ? ColorPalette.HIGHLIGHT : ColorPalette.WHITE + selectedButton.blendColor = newState = ButtonState.SELECTED ? chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) : ColorPalette.WHITE end sub ' processScrubAction: Handles +/- seeking for the audio trickplay bar @@ -819,7 +819,7 @@ function onKeyEvent(key as string, press as boolean) as boolean m.song.color = ColorPalette.WHITE m.artist.setFocus(true) - m.artist.color = ColorPalette.HIGHLIGHT + m.artist.color = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) return true end if @@ -880,7 +880,7 @@ function onKeyEvent(key as string, press as boolean) as boolean m.artist.color = ColorPalette.WHITE m.song.setFocus(true) - m.song.color = ColorPalette.HIGHLIGHT + m.song.color = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) return true end if @@ -938,7 +938,7 @@ function onKeyEvent(key as string, press as boolean) as boolean end if if key = KeyCode.UP - m.artist.color = ColorPalette.HIGHLIGHT + m.artist.color = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) if m.thumb.visible exitScrubMode(m.artist) end if diff --git a/components/music/PlaylistView.bs b/components/music/PlaylistView.bs index b12e19a0..8d402a69 100644 --- a/components/music/PlaylistView.bs +++ b/components/music/PlaylistView.bs @@ -13,7 +13,7 @@ sub init() m.songListRect = m.top.FindNode("songListRect") m.songListRect.color = ColorPalette.ELEMENTBACKGROUND - m.playlist.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.playlist.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.playlist.observeField("doneLoading", "onDoneLoading") diff --git a/components/settings/settings.bs b/components/settings/settings.bs index 36a1428d..ea5d7420 100644 --- a/components/settings/settings.bs +++ b/components/settings/settings.bs @@ -15,7 +15,7 @@ sub init() m.userLocation = [] m.settingsMenu = m.top.findNode("settingsMenu") - m.settingsMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.settingsMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.settingsMenu.focusFootprintBlendColor = ColorPalette.TRANSPARENT m.settingsMenu.focusedColor = ColorPalette.WHITE @@ -35,7 +35,7 @@ sub init() "b": { "fontSize": 27, "fontUri": "font:SystemFontFile", - "color": ColorPalette.HIGHLIGHT + "color": chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) } } @@ -49,7 +49,7 @@ sub init() m.colorgrid = m.top.findNode("colorgrid") m.radioSetting = m.top.findNode("radioSetting") - m.radioSetting.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.radioSetting.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.radioSetting.focusFootprintBlendColor = ColorPalette.TRANSPARENT m.radioSetting.focusedColor = ColorPalette.WHITE diff --git a/components/subtitle/SubtitleSearchView.bs b/components/subtitle/SubtitleSearchView.bs index f8754845..cbe102a1 100644 --- a/components/subtitle/SubtitleSearchView.bs +++ b/components/subtitle/SubtitleSearchView.bs @@ -15,14 +15,14 @@ sub init() m.languageButton.textColor = ColorPalette.DARKGREY m.languageButton.focusTextColor = ColorPalette.WHITE m.languageButton.background = ColorPalette.WHITE - m.languageButton.focusBackground = ColorPalette.HIGHLIGHT + m.languageButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.searchButton.background = ColorPalette.LIGHTBLUE m.searchButton.color = ColorPalette.DARKGREY - m.searchButton.focusBackground = ColorPalette.HIGHLIGHT + m.searchButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.searchButton.focusColor = ColorPalette.WHITE - m.mySubtitleList.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.mySubtitleList.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.mySubtitleList.focusFootprintBlendColor = ColorPalette.WHITE headerText = m.top.findNode("headerText") diff --git a/components/tvshows/TVEpisodes.bs b/components/tvshows/TVEpisodes.bs index 20c8a4ba..9b24cae0 100644 --- a/components/tvshows/TVEpisodes.bs +++ b/components/tvshows/TVEpisodes.bs @@ -14,7 +14,7 @@ sub init() m.poster = m.top.findNode("seasonPoster") m.shuffle = m.top.findNode("shuffle") m.extras = m.top.findNode("extras") - m.rows.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.rows.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.playedIndicator = m.top.findNode("playedIndicator") diff --git a/components/tvshows/TVListOptions.bs b/components/tvshows/TVListOptions.bs index bd1d2f2b..8aa5550c 100644 --- a/components/tvshows/TVListOptions.bs +++ b/components/tvshows/TVListOptions.bs @@ -19,10 +19,10 @@ sub init() m.videoNames = [] m.audioNames = [] - m.top.findNode("videoMenu").focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.top.findNode("videoMenu").focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.findNode("videoMenu").focusedColor = ColorPalette.WHITE - m.top.findNode("audioMenu").focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.top.findNode("audioMenu").focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.findNode("audioMenu").focusedColor = ColorPalette.WHITE ' Animation diff --git a/components/tvshows/TVSeasonRow.bs b/components/tvshows/TVSeasonRow.bs index cf3296fd..3600973b 100644 --- a/components/tvshows/TVSeasonRow.bs +++ b/components/tvshows/TVSeasonRow.bs @@ -1,11 +1,12 @@ import "pkg:/source/enums/ColorPalette.bs" +import "pkg:/source/utils/misc.bs" sub init() m.top.itemComponentName = "ListPoster" m.top.content = getData() m.top.rowFocusAnimationStyle = "fixedFocusWrap" - m.top.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.top.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.top.showRowLabel = [false] m.top.showRowCounter = [true] diff --git a/components/video/OSD.bs b/components/video/OSD.bs index 2aab634c..9605a978 100644 --- a/components/video/OSD.bs +++ b/components/video/OSD.bs @@ -1,5 +1,5 @@ -import "pkg:/source/utils/misc.bs" import "pkg:/source/enums/ColorPalette.bs" +import "pkg:/source/utils/misc.bs" const LOGO_RIGHT_PADDING = 30 const OPTIONCONTROLS_TOP_PADDING = 50 @@ -51,13 +51,13 @@ sub init() optionButtons = m.optionControls.getChildren(-1, 0) for each button in optionButtons button.background = ColorPalette.DARKGREY - button.focusBackground = ColorPalette.HIGHLIGHT + button.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end for videoButtons = m.videoControls.getChildren(-1, 0) for each button in videoButtons button.background = ColorPalette.DARKGREY - button.focusBackground = ColorPalette.HIGHLIGHT + button.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) end for end sub diff --git a/components/video/VideoPlayerView.bs b/components/video/VideoPlayerView.bs index e71eeb47..c2c5324c 100644 --- a/components/video/VideoPlayerView.bs +++ b/components/video/VideoPlayerView.bs @@ -45,7 +45,7 @@ sub init() m.chapterList = m.top.findNode("chapterList") m.chapterMenu = m.top.findNode("chapterMenu") - m.chapterMenu.focusBitmapBlendColor = ColorPalette.HIGHLIGHT + m.chapterMenu.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.chapterMenu.color = ColorPalette.WHITE m.chapterMenu.focusedColor = ColorPalette.WHITE m.chapterContent = m.top.findNode("chapterContent") @@ -78,7 +78,7 @@ sub init() m.skipSegmentButton = m.top.findNode("skipSegment") m.skipSegmentButton.background = ColorPalette.LIGHTBLUE m.skipSegmentButton.color = ColorPalette.DARKGREY - m.skipSegmentButton.focusBackground = ColorPalette.HIGHLIGHT + m.skipSegmentButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.skipSegmentButton.focusColor = ColorPalette.WHITE 'Play Next Episode button diff --git a/settings/settings.json b/settings/settings.json index f2ef0fb5..9b832747 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -61,6 +61,13 @@ "type": "colorgrid", "default": "#777777FF" }, + { + "title": "Cursor Color", + "description": "The highlight color for the cursor showing where the user's focus is.", + "settingName": "colorCursor", + "type": "colorgrid", + "default": "#FF6666" + }, { "title": "Watched Check Mark & Unplayed Count", "description": "Watched Check Mark & Unplayed Count color settings", diff --git a/source/ShowScenes.bs b/source/ShowScenes.bs index a7cb1a69..c2470a24 100644 --- a/source/ShowScenes.bs +++ b/source/ShowScenes.bs @@ -541,7 +541,7 @@ function CreateSigninGroup(user = "", profileImageUri = "") dlgPalette = createObject("roSGNode", "RSGPalette") dlgPalette.colors = { DialogBackgroundColor: ColorPalette.ELEMENTBACKGROUND, - DialogFocusColor: ColorPalette.HIGHLIGHT, + DialogFocusColor: chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT), DialogFocusItemColor: ColorPalette.WHITE, DialogSecondaryTextColor: ColorPalette.WHITE, DialogSecondaryItemColor: ColorPalette.LIGHTBLUE, From df3ec70fe0869988136238f7934ca42fef2e8a98 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 19:01:31 +0000 Subject: [PATCH 04/27] Update dependency @rokucommunity/bslint to v0.8.29 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 45019064..cedfd750 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "brighterscript-formatter": "1.7.12" }, "devDependencies": { - "@rokucommunity/bslint": "0.8.28", + "@rokucommunity/bslint": "0.8.29", "brighterscript": "0.69.7", "jshint": "2.13.6", "rimraf": "6.0.1", @@ -241,9 +241,9 @@ "license": "MIT" }, "node_modules/@rokucommunity/bslint": { - "version": "0.8.28", - "resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.8.28.tgz", - "integrity": "sha512-hLrkcwQHkSVXiRHWl9Y3+knVnClSrE6G/aeVNxEPFbHzNh85CFub0BfnzKbTfOSN0CjfAxo9/LoHt1axPHGQOg==", + "version": "0.8.29", + "resolved": "https://registry.npmjs.org/@rokucommunity/bslint/-/bslint-0.8.29.tgz", + "integrity": "sha512-HH2Q+TlcBtUnLFjf3tBSnFA3zJuqTAZYBtWf+5ocbKbqYq/XKCexreKt/kAC8vVDzVl7d27O9ZSOSM/G1fUPyA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c03ce5e3..a29c111d 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "brighterscript-formatter": "1.7.12" }, "devDependencies": { - "@rokucommunity/bslint": "0.8.28", + "@rokucommunity/bslint": "0.8.29", "brighterscript": "0.69.7", "jshint": "2.13.6", "rimraf": "6.0.1", From e5a187590d981276fec655a2ea18709bdeb4bf45 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 26 Apr 2025 09:09:23 -0400 Subject: [PATCH 05/27] Move What's New Author to own setting --- components/WhatsNewDialog.bs | 2 +- settings/settings.json | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/components/WhatsNewDialog.bs b/components/WhatsNewDialog.bs index f30c139e..8b5b6fc7 100644 --- a/components/WhatsNewDialog.bs +++ b/components/WhatsNewDialog.bs @@ -25,7 +25,7 @@ sub init() "author": { "fontSize": 27, "fontUri": "font:SystemFontFile", - "color": chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) + "color": chainLookupReturn(m.global.session, "user.settings.colorWhatsNewAuthor", ColorPalette.HIGHLIGHT) } } diff --git a/settings/settings.json b/settings/settings.json index 9b832747..04ee4dba 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -48,26 +48,33 @@ "default": "#000018" }, { - "title": "Primary Text Color", + "title": "Primary Text", "description": "The primary text color for all screens in Jellyfin.", "settingName": "colorText", "type": "colorgrid", "default": "#FFFFFF" }, { - "title": "Secondary Text Color", + "title": "Secondary Text", "description": "The secondary text color for all screens in Jellyfin.", "settingName": "colorSubText", "type": "colorgrid", "default": "#777777FF" }, { - "title": "Cursor Color", + "title": "Cursor", "description": "The highlight color for the cursor showing where the user's focus is.", "settingName": "colorCursor", "type": "colorgrid", "default": "#FF6666" }, + { + "title": "What's New Author", + "description": "The text color for the author in the What's New popup.", + "settingName": "colorWhatsNewAuthor", + "type": "colorgrid", + "default": "#FF6666" + }, { "title": "Watched Check Mark & Unplayed Count", "description": "Watched Check Mark & Unplayed Count color settings", From 180fddc39cec455151fb1d952edb66d8b8989dff Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 26 Apr 2025 09:30:23 -0400 Subject: [PATCH 06/27] Apply colors to loaded views --- components/settings/settings.bs | 17 +++++++++++++++++ source/Main.bs | 29 +++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/components/settings/settings.bs b/components/settings/settings.bs index ea5d7420..3f64fed6 100644 --- a/components/settings/settings.bs +++ b/components/settings/settings.bs @@ -92,10 +92,27 @@ sub onColorSelected() selectedSetting = m.userLocation.peek().children[m.settingsMenu.itemFocused] set_user_setting(selectedSetting.settingName, selectedColor ?? ColorPalette.VIEWBACKGROUND) + ' Apply settings to items already loaded so user doesn't need to close the channel to see the changes + + ' Apply global background color if isStringEqual(selectedSetting.settingName, "colorBackground") scene = m.top.getScene() scene.backgroundColor = selectedColor ?? ColorPalette.VIEWBACKGROUND end if + + ' Apply cursor color + if isStringEqual(selectedSetting.settingName, "colorCursor") + ' Update cursor around user avatar + scene = m.top.getScene() + overhang = scene.findNode("overhang") + if isValid(overhang) + overlayCurrentUserSelection = overhang.findNode("overlayCurrentUserSelection") + overlayCurrentUserSelection.blendColor = selectedColor ?? ColorPalette.HIGHLIGHT + end if + + ' Update setting menu colors + m.settingsMenu.focusBitmapBlendColor = selectedColor ?? ColorPalette.HIGHLIGHT + end if end sub sub onKeyGridSubmit() diff --git a/source/Main.bs b/source/Main.bs index 4ba0b4b0..8b7ebd4b 100644 --- a/source/Main.bs +++ b/source/Main.bs @@ -51,8 +51,8 @@ sub Main (args as dynamic) as void ' First thing to do is validate the ability to use the API if not LoginFlow() then return - ' Set user's chosen background color - setUserBackground() + ' Set user's chosen colors + setUserColors() ' remove login scenes from the stack sceneManager.callFunc("clearScenes") @@ -185,18 +185,18 @@ sub Main (args as dynamic) as void unset_setting("server") session.server.Delete() SignOut(false) - setUserBackground() + setUserColors() sceneManager.callFunc("clearScenes") goto app_start else if isStringEqual(button.id, "change_user") SignOut(false) ' Reset colors to defaults - setUserBackground() + setUserColors() sceneManager.callFunc("clearScenes") goto app_start else if isStringEqual(button.id, "sign_out") SignOut() - setUserBackground() + setUserColors() sceneManager.callFunc("clearScenes") goto app_start else if isStringEqual(button.id, "settings") @@ -285,8 +285,17 @@ sub displayWhatsNewPopup(args) end if end sub -sub setUserBackground() +sub setUserColors() + setBackgroundColor() + setBackgroundImage() + setCursorColor() +end sub + +sub setBackgroundColor() m.scene.backgroundColor = chainLookupReturn(m.global.session, "user.settings.colorBackground", ColorPalette.VIEWBACKGROUND) +end sub + +sub setBackgroundImage() selectedBackgroundImage = chainLookupReturn(m.global.session, "user.settings.imageBackground", string.EMPTY) if isStringEqual(selectedBackgroundImage, "splash") @@ -303,3 +312,11 @@ sub setUserBackground() m.scene.callFunc("setBackgroundImage", selectedBackgroundImage) end sub + +sub setCursorColor() + overhang = m.scene.findNode("overhang") + if isValid(overhang) + overlayCurrentUserSelection = overhang.findNode("overlayCurrentUserSelection") + overlayCurrentUserSelection.blendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) + end if +end sub From ebcf15940944b6a9580a484945ac9dfa2cd4c386 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 1 May 2025 08:06:18 -0400 Subject: [PATCH 07/27] Bump version --- manifest | 2 +- package-lock.json | 4 ++-- package.json | 2 +- source/static/whatsNew/3.0.2.json | 34 ------------------------------- source/static/whatsNew/3.0.3.json | 1 + 5 files changed, 5 insertions(+), 38 deletions(-) delete mode 100644 source/static/whatsNew/3.0.2.json create mode 100644 source/static/whatsNew/3.0.3.json diff --git a/manifest b/manifest index ca24ab09..a631df60 100644 --- a/manifest +++ b/manifest @@ -3,7 +3,7 @@ title=jellyfin major_version=3 minor_version=0 -build_version=2 +build_version=3 ### Main Menu Icons / Channel Poster Artwork diff --git a/package-lock.json b/package-lock.json index cedfd750..ced71111 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jellyfin", - "version": "3.0.2", + "version": "3.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jellyfin", - "version": "3.0.2", + "version": "3.0.3", "hasInstallScript": true, "license": "GPL-2.0", "dependencies": { diff --git a/package.json b/package.json index a29c111d..64732af2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jellyfin", "type": "module", - "version": "3.0.2", + "version": "3.0.3", "description": "Official Roku app for Jellyfin media server", "dependencies": { "@rokucommunity/bslib": "0.1.1", diff --git a/source/static/whatsNew/3.0.2.json b/source/static/whatsNew/3.0.2.json deleted file mode 100644 index 425e7ce3..00000000 --- a/source/static/whatsNew/3.0.2.json +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - "description": "Add unplayed episode count and refresh number in real time", - "author": "1hitsong" - }, - { - "description": "Fix clicking skip outro can result in video stuck in buffering state", - "author": "1hitsong" - }, - { - "description": "Fix voice search in visual and music libraries", - "author": "1hitsong" - }, - { - "description": "Fix search in collections library. Due to API restrictions, all sub-folders are included in search results and is disabled when inside a collection.", - "author": "1hitsong" - }, - { - "description": "Add folder support to music video library", - "author": "1hitsong" - }, - { - "description": "Fix NFO not updating when media finishes playing", - "author": "1hitsong" - }, - { - "description": "Fix default audio track not always selected correctly", - "author": "jimdogx" - }, - { - "description": "Fix possible video player error when using quickplay on list of episodes in TV season screen", - "author": "1hitsong" - } -] diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/source/static/whatsNew/3.0.3.json @@ -0,0 +1 @@ +[] From f4758dd02dce2c2d17ae89cdc58bc7934ca31ba2 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 1 May 2025 08:04:06 -0400 Subject: [PATCH 08/27] Fix crash when watching a show while recording Fixes #312 --- components/movies/MovieDetails.bs | 6 ++++-- source/static/whatsNew/3.0.3.json | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/components/movies/MovieDetails.bs b/components/movies/MovieDetails.bs index 1b6a2878..164d9f66 100644 --- a/components/movies/MovieDetails.bs +++ b/components/movies/MovieDetails.bs @@ -603,8 +603,10 @@ sub SetDefaultAudioTrack(itemData) end for ' If we got here, then nothing matched. First track it is... - m.top.selectedAudioStreamIndex = firstAudioTrack - setFieldText("audio_codec", tr("Audio") + ": " + itemData.mediaStreams[firstAudioTrack].displayTitle) + if firstAudioTrack > -1 + m.top.selectedAudioStreamIndex = firstAudioTrack + setFieldText("audio_codec", tr("Audio") + ": " + itemData.mediaStreams[firstAudioTrack].displayTitle) + end if end sub sub setFieldText(field, value) diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json index fe51488c..5c7a9745 100644 --- a/source/static/whatsNew/3.0.3.json +++ b/source/static/whatsNew/3.0.3.json @@ -1 +1,6 @@ -[] +[ + { + "description": "Fix crash when watching a recording in progress", + "author": "1hitsong" + } +] From d9b3363f1111a618b8199c1967383d4ba24eff37 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 25 Apr 2025 09:03:58 -0400 Subject: [PATCH 09/27] Request logos in PNG format Fixes #301 --- components/ItemGrid/LoadItemsTask2.bs | 5 +++-- components/ItemGrid/LoadVideoContentTask.bs | 5 +++-- components/movies/MovieDetails.bs | 1 + source/ShowScenes.bs | 5 +++-- source/static/whatsNew/3.0.3.json | 4 ++++ 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/components/ItemGrid/LoadItemsTask2.bs b/components/ItemGrid/LoadItemsTask2.bs index a2d7a581..35b22420 100644 --- a/components/ItemGrid/LoadItemsTask2.bs +++ b/components/ItemGrid/LoadItemsTask2.bs @@ -2,6 +2,7 @@ import "pkg:/source/api/baserequest.bs" import "pkg:/source/api/Image.bs" import "pkg:/source/api/Items.bs" import "pkg:/source/api/sdk.bs" +import "pkg:/source/enums/ImageType.bs" import "pkg:/source/utils/config.bs" import "pkg:/source/utils/deviceCapabilities.bs" import "pkg:/source/utils/misc.bs" @@ -29,9 +30,9 @@ sub loadItems() end if if m.top.ItemType = "LogoImage" - logoImageExists = api.items.HeadImageURLByName(m.top.itemId, "logo") + logoImageExists = api.items.HeadImageURLByName(m.top.itemId, ImageType.LOGO) if logoImageExists - m.top.content = [api.items.GetImageURL(m.top.itemId, "logo", 0, { "maxHeight": 500, "maxWidth": 500, "quality": "90" })] + m.top.content = [api.items.GetImageURL(m.top.itemId, ImageType.LOGO, 0, { "format": "Png", "maxHeight": 500, "maxWidth": 500, "quality": "90" })] else m.top.content = [] end if diff --git a/components/ItemGrid/LoadVideoContentTask.bs b/components/ItemGrid/LoadVideoContentTask.bs index 790298b3..6f220a9a 100644 --- a/components/ItemGrid/LoadVideoContentTask.bs +++ b/components/ItemGrid/LoadVideoContentTask.bs @@ -2,6 +2,7 @@ import "pkg:/source/api/baserequest.bs" import "pkg:/source/api/Image.bs" import "pkg:/source/api/Items.bs" import "pkg:/source/api/userauth.bs" +import "pkg:/source/enums/ImageType.bs" import "pkg:/source/enums/MediaSegmentType.bs" import "pkg:/source/utils/config.bs" import "pkg:/source/utils/deviceCapabilities.bs" @@ -169,9 +170,9 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s end if end if - logoImageExists = api.items.HeadImageURLByName(logoLookupID, "logo") + logoImageExists = api.items.HeadImageURLByName(logoLookupID, ImageType.LOGO) if logoImageExists - video.logoImage = api.items.GetImageURL(logoLookupID, "logo", 0, { "maxHeight": 65, "maxWidth": 300, "quality": "90" }) + video.logoImage = api.items.GetImageURL(logoLookupID, ImageType.LOGO, 0, { "format": "Png", "maxHeight": 65, "maxWidth": 300, "quality": "90" }) end if additionalParts = api.videos.GetAdditionalParts(video.id) diff --git a/components/movies/MovieDetails.bs b/components/movies/MovieDetails.bs index 0a9a6768..db555163 100644 --- a/components/movies/MovieDetails.bs +++ b/components/movies/MovieDetails.bs @@ -258,6 +258,7 @@ sub setLogoImage(itemData as object) movieLogo.width = "500" movieLogo.height = "250" movieLogo.uri = ImageURL(m.top.id, ImageType.LOGO, { + "format": "Png", "maxHeight": 300, "maxWidth": 600, "Tag": itemData.ImageTags.Logo diff --git a/source/ShowScenes.bs b/source/ShowScenes.bs index c2470a24..de818fa5 100644 --- a/source/ShowScenes.bs +++ b/source/ShowScenes.bs @@ -1,3 +1,4 @@ +import "pkg:/source/enums/ImageType.bs" import "pkg:/source/utils/misc.bs" function LoginFlow() @@ -645,9 +646,9 @@ function CreateMovieDetailsGroup(movie as object) as dynamic if LCase(movieMetaData.json.type) = "episode" or LCase(movieMetaData.json.type) = "series" if isValid(movieMetaData.json.SeriesID) - logoImageExists = api.items.HeadImageURLByName(movieMetaData.json.SeriesID, "logo") + logoImageExists = api.items.HeadImageURLByName(movieMetaData.json.SeriesID, ImageType.LOGO) if logoImageExists - group.logoImageURI = api.items.GetImageURL(movieMetaData.json.SeriesID, "logo", 0, { "maxHeight": 250, "maxWidth": 500, "quality": "90" }) + group.logoImageURI = api.items.GetImageURL(movieMetaData.json.SeriesID, ImageType.LOGO, 0, { "format": "Png", "maxHeight": 250, "maxWidth": 500, "quality": "90" }) end if end if end if diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json index 5c7a9745..9222b28c 100644 --- a/source/static/whatsNew/3.0.3.json +++ b/source/static/whatsNew/3.0.3.json @@ -2,5 +2,9 @@ { "description": "Fix crash when watching a recording in progress", "author": "1hitsong" + }, + { + "description": "Fix transparent backgrounds when using SVG logos", + "author": "1hitsong" } ] From 5c247219638a3823db89f9157d8ef796a620ee48 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:08:02 -0400 Subject: [PATCH 10/27] Apply item title setting to "other" library views Fixes #262 Applies item title setting to libraries not covered by audiobook, music, and visual library scenes. --- components/ItemGrid/ItemGrid.bs | 15 ++------------- source/static/whatsNew/3.0.3.json | 4 ++++ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/components/ItemGrid/ItemGrid.bs b/components/ItemGrid/ItemGrid.bs index 49bab308..fa669532 100644 --- a/components/ItemGrid/ItemGrid.bs +++ b/components/ItemGrid/ItemGrid.bs @@ -120,6 +120,8 @@ sub loadInitialItems() m.itemGrid.numColumns = "7" m.itemGrid.numRows = "3" + m.top.showItemTitles = m.global.session.user.settings["itemgrid.gridTitles"] + if isStringEqual(m.top.showItemTitles, "hidealways") m.itemGrid.itemSize = "[230, 345]" m.itemGrid.rowHeights = "[345]" @@ -213,8 +215,6 @@ sub loadInitialItems() else if isStringEqual(getCollectionType(), "tvshows") m.loadItemsTask.itemType = "Series" m.loadItemsTask.itemId = m.top.parentItem.Id - - m.top.showItemTitles = m.global.session.user.settings["itemgrid.gridTitles"] else if isStringEqual(getCollectionType(), "music") ' Default Settings m.loadItemsTask.recursive = true @@ -236,17 +236,6 @@ sub loadInitialItems() m.loadItemsTask.recursive = false end if - if isStringEqual(getCollectionType(), "boxsets") - m.top.showItemTitles = m.global.session.user.settings["itemgrid.gridTitles"] - if isStringEqual(m.top.showItemTitles, "hidealways") - m.itemGrid.itemSize = "[230, 345]" - m.itemGrid.rowHeights = "[345]" - else - m.itemGrid.itemSize = "[230, 390]" - m.itemGrid.rowHeights = "[390]" - end if - end if - if isStringEqual(getCollectionType(), "nextup") m.loadItemsTask.itemType = "NextUp" m.itemGrid.itemComponentName = "GridItemMedium" diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json index 9222b28c..3a65d85d 100644 --- a/source/static/whatsNew/3.0.3.json +++ b/source/static/whatsNew/3.0.3.json @@ -6,5 +6,9 @@ { "description": "Fix transparent backgrounds when using SVG logos", "author": "1hitsong" + }, + { + "description": "Honor item title showing setting in \"other\" library views", + "author": "1hitsong" } ] From 84f3c5195300bbdb6550f4028dddb7e2f97c8ff4 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Wed, 30 Apr 2025 08:06:13 -0400 Subject: [PATCH 11/27] Prevent users from getting stuck on actor detail screen Fixes #309 --- components/extras/ExtrasRowList.bs | 1 + source/static/whatsNew/3.0.3.json | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/components/extras/ExtrasRowList.bs b/components/extras/ExtrasRowList.bs index 7db9d55e..52206e52 100644 --- a/components/extras/ExtrasRowList.bs +++ b/components/extras/ExtrasRowList.bs @@ -281,6 +281,7 @@ end sub sub onRowItemSelected() m.top.selectedItem = m.top.content.getChild(m.top.rowItemSelected[0]).getChild(m.top.rowItemSelected[1]) + m.top.selectedItem = invalid end sub sub onRowItemFocused() diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json index 3a65d85d..d96092e3 100644 --- a/source/static/whatsNew/3.0.3.json +++ b/source/static/whatsNew/3.0.3.json @@ -10,5 +10,9 @@ { "description": "Honor item title showing setting in \"other\" library views", "author": "1hitsong" + }, + { + "description": "Fix bug that caused users to get stuck on actor detail screen", + "author": "1hitsong" } ] From 25123b98d8607e8e7cabebdf806d1e44fab1fdf8 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 1 May 2025 20:48:11 -0400 Subject: [PATCH 12/27] Fix bug that caused video playback failure if audio was paused first Fixes #311 --- components/music/AudioPlayerView.bs | 8 +++++++- source/static/whatsNew/3.0.3.json | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/components/music/AudioPlayerView.bs b/components/music/AudioPlayerView.bs index 61a33209..bb968f28 100644 --- a/components/music/AudioPlayerView.bs +++ b/components/music/AudioPlayerView.bs @@ -3,6 +3,7 @@ import "pkg:/source/api/Image.bs" import "pkg:/source/enums/ColorPalette.bs" import "pkg:/source/enums/ImageType.bs" import "pkg:/source/enums/KeyCode.bs" +import "pkg:/source/enums/MediaPlaybackState.bs" import "pkg:/source/enums/String.bs" import "pkg:/source/enums/TaskControl.bs" import "pkg:/source/enums/ViewLoadStatus.bs" @@ -299,7 +300,12 @@ sub endScreenSaver() end sub sub displayMiniPlayer() - if not m.global.audioPlayer.state = "playing" then return + if not isStringEqual(m.global.audioPlayer.state, MediaPlaybackState.PLAYING) + m.global.queueManager.callFunc("clear") + m.global.audioPlayer.control = "stop" + return + end if + if m.playlistTypeCount > 1 then return scene = m.top.getScene() diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json index d96092e3..58a0ef3a 100644 --- a/source/static/whatsNew/3.0.3.json +++ b/source/static/whatsNew/3.0.3.json @@ -14,5 +14,9 @@ { "description": "Fix bug that caused users to get stuck on actor detail screen", "author": "1hitsong" + }, + { + "description": "Fix bug that caused video playback failure if audio was paused first", + "author": "1hitsong" } ] From 7de3c19605cc40984a155138dc5d230834a971ad Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 1 May 2025 20:22:52 -0400 Subject: [PATCH 13/27] If seeking to end of video, use itemNext action to skip to next in queue --- components/video/VideoPlayerView.bs | 9 +++++++-- source/static/whatsNew/3.0.3.json | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/components/video/VideoPlayerView.bs b/components/video/VideoPlayerView.bs index c2c5324c..0278ea7f 100644 --- a/components/video/VideoPlayerView.bs +++ b/components/video/VideoPlayerView.bs @@ -944,8 +944,13 @@ end function sub skipCurrentSegment() seekPosition = m.currentSegmentEndTicks / 10000000# - if seekPosition > m.top.duration - seekPosition = m.top.duration + if seekPosition > (m.top.duration - 2) + if m.global.queueManager.callFunc("getPosition") < m.global.queueManager.callFunc("getCount") - 1 + handleItemSkipAction("itemnext") + return + else + seekPosition = m.top.duration - 1 + end if end if m.top.seek = seekPosition diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json index 58a0ef3a..5dfbfa1d 100644 --- a/source/static/whatsNew/3.0.3.json +++ b/source/static/whatsNew/3.0.3.json @@ -18,5 +18,9 @@ { "description": "Fix bug that caused video playback failure if audio was paused first", "author": "1hitsong" + }, + { + "description": "Improve fix for skip outro can result in video stuck in buffering state", + "author": "1hitsong" } ] From 8eef0b3d6ba446845a405b32810e8721e2ac95bc Mon Sep 17 00:00:00 2001 From: David Briglio <3886546+DavidBriglio@users.noreply.github.com> Date: Fri, 2 May 2025 12:07:59 -0400 Subject: [PATCH 14/27] Autoplay Episode Limit implementation --- components/ItemGrid/LoadVideoContentTask.bs | 14 +- locale/en_US/translations.ts | 8 + settings/settings.json | 1205 ++++++++++--------- 3 files changed, 625 insertions(+), 602 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.bs b/components/ItemGrid/LoadVideoContentTask.bs index 6f220a9a..1c2db6c5 100644 --- a/components/ItemGrid/LoadVideoContentTask.bs +++ b/components/ItemGrid/LoadVideoContentTask.bs @@ -185,8 +185,9 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s end if if m.global.session.user.Configuration.EnableNextEpisodeAutoPlay + episodeQueueLimit = m.global.session.user.settings.["playback.episodeQueueLimit"].ToInt() if isValid(video.showID) - addNextEpisodesToQueue(video.showID, additionalPartsCount) + addNextEpisodesToQueue(video.showID, additionalPartsCount, episodeQueueLimit) end if end if @@ -542,7 +543,7 @@ function getContainerType(meta as object) as string end function ' Add next episodes to the playback queue -sub addNextEpisodesToQueue(showID, additionalPartsCount as integer) +sub addNextEpisodesToQueue(showID, additionalPartsCount as integer, queueLimit as integer) ' Don't queue next episodes if we already have a playback queue maxQueueCount = additionalPartsCount + 1 @@ -569,10 +570,17 @@ sub addNextEpisodesToQueue(showID, additionalPartsCount as integer) end if end if + limitParam = queueLimit + + ' If the queue limit is set to 0 / disabled, default to 50 + if limitParam = 0 + limitParam = 50 + end if + url = Substitute("Shows/{0}/Episodes", showID) urlParams = { "UserId": m.global.session.user.id } urlParams.Append({ "StartItemId": videoID }) - urlParams.Append({ "Limit": 50 }) + urlParams.Append({ "Limit": limitParam }) resp = APIRequest(url, urlParams) data = getJson(resp) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index 39c64e57..bbaa2a79 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1012,6 +1012,14 @@ Albums Albums + + Autoplay Episode Limit + Autoplay Episode Limit + + + The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. Set to 0 to disable. + The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. Set to 0 to disable. + View All View All diff --git a/settings/settings.json b/settings/settings.json index 04ee4dba..5c7de524 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -1,615 +1,622 @@ [ - { - "title": "Secret", - "description": "Settings that are not for public consumption. Do not get mad if these things change without notice.", - "visible": false, + { + "title": "Secret", + "description": "Settings that are not for public consumption. Do not get mad if these things change without notice.", + "visible": false, + "children": [ + { + "title": "CJK Text", + "description": "Use CJK fallback font for text elements across the channel", + "settingName": "cjkText", + "type": "bool", + "default": "false" + }, + { + "title": "Channel Background Image", + "description": "Overlay a background image over your chosen background color", + "settingName": "imageBackground", + "type": "radio", + "default": "", + "options": [ + { + "title": "None", + "id": "" + }, + { + "title": "Circles", + "id": "pkg:/images/background/bg1.jpg" + }, + { + "title": "Swoosh", + "id": "pkg:/images/background/bg2.jpg" + }, + { + "title": "Splashscreen Image", + "id": "splash" + } + ] + }, + { + "title": "Color Settings", + "description": "Change the colors of elements in the channel", "children": [ - { - "title": "CJK Text", - "description": "Use CJK fallback font for text elements across the channel", - "settingName": "cjkText", - "type": "bool", - "default": "false" - }, - { - "title": "Channel Background Image", - "description": "Overlay a background image over your chosen background color", - "settingName": "imageBackground", - "type": "radio", - "default": "", - "options": [ - { - "title": "None", - "id": "" - }, - { - "title": "Circles", - "id": "pkg:/images/background/bg1.jpg" - }, - { - "title": "Swoosh", - "id": "pkg:/images/background/bg2.jpg" - }, - { - "title": "Splashscreen Image", - "id": "splash" - } - ] - }, - { - "title": "Color Settings", - "description": "Change the colors of elements in the channel", - "children": [ - { - "title": "Background", - "description": "The base background color for all screens in Jellyfin.", - "settingName": "colorBackground", - "type": "colorgrid", - "default": "#000018" - }, - { - "title": "Primary Text", - "description": "The primary text color for all screens in Jellyfin.", - "settingName": "colorText", - "type": "colorgrid", - "default": "#FFFFFF" - }, - { - "title": "Secondary Text", - "description": "The secondary text color for all screens in Jellyfin.", - "settingName": "colorSubText", - "type": "colorgrid", - "default": "#777777FF" - }, - { - "title": "Cursor", - "description": "The highlight color for the cursor showing where the user's focus is.", - "settingName": "colorCursor", - "type": "colorgrid", - "default": "#FF6666" - }, - { - "title": "What's New Author", - "description": "The text color for the author in the What's New popup.", - "settingName": "colorWhatsNewAuthor", - "type": "colorgrid", - "default": "#FF6666" - }, - { - "title": "Watched Check Mark & Unplayed Count", - "description": "Watched Check Mark & Unplayed Count color settings", - "children": [ - { - "title": "Background", - "description": "The background color of the check mark & unplayed count shown in the top right corner of items.", - "settingName": "colorPlayedCheckmarkBackground", - "type": "colorgrid", - "default": "#6666FF" - }, - { - "title": "Watched Check Mark", - "description": "The color of the watched check mark.", - "settingName": "colorPlayedCheckmarkIcon", - "type": "colorgrid", - "default": "#FFFFFF" - }, - { - "title": "Unplayed Count", - "description": "The text color of the unplayed count number.", - "settingName": "colorUnplayedCountTextColor", - "type": "colorgrid", - "default": "#FFFFFF" - } - ] - } - ] - } + { + "title": "Background", + "description": "The base background color for all screens in Jellyfin.", + "settingName": "colorBackground", + "type": "colorgrid", + "default": "#000018" + }, + { + "title": "Primary Text", + "description": "The primary text color for all screens in Jellyfin.", + "settingName": "colorText", + "type": "colorgrid", + "default": "#FFFFFF" + }, + { + "title": "Secondary Text", + "description": "The secondary text color for all screens in Jellyfin.", + "settingName": "colorSubText", + "type": "colorgrid", + "default": "#777777FF" + }, + { + "title": "Cursor", + "description": "The highlight color for the cursor showing where the user's focus is.", + "settingName": "colorCursor", + "type": "colorgrid", + "default": "#FF6666" + }, + { + "title": "What's New Author", + "description": "The text color for the author in the What's New popup.", + "settingName": "colorWhatsNewAuthor", + "type": "colorgrid", + "default": "#FF6666" + }, + { + "title": "Watched Check Mark & Unplayed Count", + "description": "Watched Check Mark & Unplayed Count color settings", + "children": [ + { + "title": "Background", + "description": "The background color of the check mark & unplayed count shown in the top right corner of items.", + "settingName": "colorPlayedCheckmarkBackground", + "type": "colorgrid", + "default": "#6666FF" + }, + { + "title": "Watched Check Mark", + "description": "The color of the watched check mark.", + "settingName": "colorPlayedCheckmarkIcon", + "type": "colorgrid", + "default": "#FFFFFF" + }, + { + "title": "Unplayed Count", + "description": "The text color of the unplayed count number.", + "settingName": "colorUnplayedCountTextColor", + "type": "colorgrid", + "default": "#FFFFFF" + } + ] + } ] - }, - { - "title": "Global", - "description": "Global settings that affect everyone that uses this Roku device.", + } + ] + }, + { + "title": "Global", + "description": "Global settings that affect everyone that uses this Roku device.", + "children": [ + { + "title": "Remember Me?", + "description": "Remember the currently logged in user and try to log them in again next time you start Jellyfin.", + "settingName": "global.rememberme", + "type": "bool", + "default": "true" + } + ] + }, + { + "title": "Playback", + "description": "Settings relating to playback and supported codec and media types.", + "children": [ + { + "title": "Autoplay Episode Limit", + "description": "The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. Set to 0 to disable.", + "settingName": "playback.episodeQueueLimit", + "type": "integer", + "default": "0" + }, + { + "title": "Bitrate Limit", + "description": "Configure the maximum playback bitrate.", "children": [ - { - "title": "Remember Me?", - "description": "Remember the currently logged in user and try to log them in again next time you start Jellyfin.", - "settingName": "global.rememberme", - "type": "bool", - "default": "true" - } + { + "title": "Enable Limit", + "description": "Enable or disable the 'Maximum Bitrate' setting.", + "settingName": "playback.bitrate.maxlimited", + "type": "bool", + "default": "true" + }, + { + "title": "Maximum Bitrate", + "description": "Set the maximum bitrate in Mbps. Set to 0 to use Roku's specifications. This setting must be enabled to take effect.", + "settingName": "playback.bitrate.limit", + "type": "integer", + "default": "0" + } ] - }, - { - "title": "Playback", - "description": "Settings relating to playback and supported codec and media types.", + }, + { + "title": "Cinema Mode", + "description": "Bring the theater experience straight to your living room with the ability to play custom intros before the main feature.", + "settingName": "playback.cinemamode", + "type": "bool", + "default": "false" + }, + { + "title": "Compatibility", + "description": "Attempt to prevent playback failures.", "children": [ - { - "title": "Bitrate Limit", - "description": "Configure the maximum playback bitrate.", - "children": [ - { - "title": "Enable Limit", - "description": "Enable or disable the 'Maximum Bitrate' setting.", - "settingName": "playback.bitrate.maxlimited", - "type": "bool", - "default": "true" - }, - { - "title": "Maximum Bitrate", - "description": "Set the maximum bitrate in Mbps. Set to 0 to use Roku's specifications. This setting must be enabled to take effect.", - "settingName": "playback.bitrate.limit", - "type": "integer", - "default": "0" - } - ] - }, - { - "title": "Cinema Mode", - "description": "Bring the theater experience straight to your living room with the ability to play custom intros before the main feature.", - "settingName": "playback.cinemamode", + { + "title": "Disable HEVC", + "description": "Disable the HEVC codec on this device. This may improve playback for some devices (ultra).", + "settingName": "playback.compatibility.disablehevc", + "type": "bool", + "default": "false" + } + ] + }, + { + "title": "Custom Subtitles", + "description": "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.", + "settingName": "playback.subs.custom", + "type": "bool", + "default": "false" + }, + { + "title": "Custom Trickplay", + "description": "Display custom trickplay images even if this Roku says it's displaying its own. This may cause the two images to overlay on top of each other.", + "settingName": "playback.trickplay.forcecustom", + "type": "bool", + "default": "false" + }, + { + "title": "Force Transcoding", + "description": "Force all playable media to be transcoded.", + "settingName": "playback.media.forceTranscode", + "type": "bool", + "default": "false" + }, + { + "title": "Maximum Resolution", + "description": "Configure the maximum resolution when playing video files on this device.", + "children": [ + { + "title": "Mode", + "description": "Apply max resolution to all files or only transcoded files.", + "settingName": "playback.resolution.mode", + "type": "radio", + "default": "transcoding", + "options": [ + { + "title": "All files", + "id": "everything" + }, + { + "title": "Only transcoded files", + "id": "transcoding" + } + ] + }, + { + "title": "Value", + "description": "Set the maximum resolution when playing video files on this device.", + "settingName": "playback.resolution.max", + "type": "radio", + "default": "auto", + "options": [ + { + "title": "Off - Attempt to play all resolutions", + "id": "off" + }, + { + "title": "Auto - Use TV resolution", + "id": "auto" + }, + { + "title": "360p", + "id": "360" + }, + { + "title": "480p", + "id": "480" + }, + { + "title": "720p", + "id": "720" + }, + { + "title": "1080p", + "id": "1080" + }, + { + "title": "4k", + "id": "2160" + }, + { + "title": "8k", + "id": "4320" + } + ] + } + ] + }, + { + "title": "Media Segment Actions", + "description": "Settings relating to how Jellyfin should handle media segments.", + "children": [ + { + "title": "Commercial Segments", + "description": "What action should Jellyfin take for commercial segments? ", + "settingName": "playback.mediasegments.commercial", + "type": "radio", + "default": "none", + "options": [ + { + "title": "Ask To Skip", + "id": "asktoskip" + }, + { + "title": "Skip", + "id": "skip" + }, + { + "title": "None", + "id": "none" + } + ] + }, + { + "title": "Intro Segments", + "description": "What action should Jellyfin take for intro segments? ", + "settingName": "playback.mediasegments.intro", + "type": "radio", + "default": "asktoskip", + "options": [ + { + "title": "Ask To Skip", + "id": "asktoskip" + }, + { + "title": "Skip", + "id": "skip" + }, + { + "title": "None", + "id": "None" + } + ] + }, + { + "title": "Outro Segments", + "description": "What action should Jellyfin take for outro segments? ", + "settingName": "playback.mediasegments.outro", + "type": "radio", + "default": "asktoskip", + "options": [ + { + "title": "Ask To Skip", + "id": "asktoskip" + }, + { + "title": "Skip", + "id": "skip" + }, + { + "title": "None", + "id": "none" + } + ] + }, + { + "title": "Preview Segments", + "description": "What action should Jellyfin take for preview segments? ", + "settingName": "playback.mediasegments.preview", + "type": "radio", + "default": "none", + "options": [ + { + "title": "Ask To Skip", + "id": "asktoskip" + }, + { + "title": "Skip", + "id": "skip" + }, + { + "title": "None", + "id": "none" + } + ] + }, + { + "title": "Recap Segments", + "description": "What action should Jellyfin take for recap segments? ", + "settingName": "playback.mediasegments.recap", + "type": "radio", + "default": "none", + "options": [ + { + "title": "Ask To Skip", + "id": "asktoskip" + }, + { + "title": "Skip", + "id": "skip" + }, + { + "title": "None", + "id": "none" + } + ] + } + ] + }, + { + "title": "Next Episode Button Time", + "description": "If no Outro media segment exists, set how many seconds before the end of an episode the Skip Outro button should appear. Set to 0 to disable.", + "settingName": "playback.nextupbuttonseconds", + "type": "integer", + "default": "30" + }, + { + "title": "Preferred Audio Codec", + "description": "Use the selected audio codec for transcodes. If the device or stream does not support it, a fallback codec will be used.", + "settingName": "playback.preferredAudioCodec", + "type": "radio", + "default": "auto", + "options": [ + { + "title": "Use system settings", + "id": "auto" + }, + { + "title": "AAC", + "id": "aac" + }, + { + "title": "DD (AC3)", + "id": "ac3" + }, + { + "title": "DD+ (EAC3)", + "id": "eac3" + }, + { + "title": "DTS", + "id": "dts" + } + ] + }, + { + "title": "Text Subtitles Only", + "description": "Only display text subtitles to minimize transcoding.", + "settingName": "playback.subs.onlytext", + "type": "bool", + "default": "false" + }, + { + "title": "Video Codec Support", + "description": "Enable or disable Direct Play support for certain codecs.", + "children": [ + { + "title": "MPEG-2", + "description": "Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.", + "settingName": "playback.mpeg2", + "type": "bool", + "default": "false" + }, + { + "title": "MPEG-4", + "description": "Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.", + "settingName": "playback.mpeg4", + "type": "bool", + "default": "true" + } + ] + }, + { + "title": "Video Profile Level Support", + "description": "Attempt Direct Play of potentially unsupported profile levels", + "children": [ + { + "title": "H.264", + "description": "Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.", + "settingName": "playback.tryDirect.h264ProfileLevel", + "type": "bool", + "default": "false" + }, + { + "title": "HEVC", + "description": "Attempt Direct Play for HEVC media with unsupported profile levels before falling back to transcoding if it fails.", + "settingName": "playback.tryDirect.hevcProfileLevel", + "type": "bool", + "default": "false" + } + ] + } + ] + }, + { + "title": "User Interface", + "description": "Settings relating to how the application looks.", + "children": [ + { + "title": "General", + "description": "Settings relating to the appearance of the Home screen and the program in general.", + "children": [ + { + "title": "Episode Images Next Up", + "description": "What type of images to use for Episodes shown in the 'Next Up' and 'Continue Watching' sections.", + "settingName": "ui-general-episodeimagesnextup", + "type": "radio", + "default": "webclient", + "options": [ + { + "title": "Use Web Client Setting", + "id": "webclient" + }, + { + "title": "Use Episode Image", + "id": "episode" + }, + { + "title": "Use Show Image", + "id": "show" + } + ] + }, + { + "title": "Hide Clock", + "description": "Hide all clocks in Jellyfin. Jellyfin will need to be closed and reopened for changes to take effect.", + "settingName": "ui.design.hideclock", + "type": "bool", + "default": "false" + }, + { + "title": "Max Days Next Up", + "description": "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.", + "settingName": "ui.details.maxdaysnextup", + "type": "integer", + "default": "365" + }, + { + "title": "Rewatching Next Up", + "description": "Show already watched episodes in 'Next Up' sections.", + "settingName": "ui.details.enablerewatchingnextup", + "type": "bool", + "default": "false" + }, + { + "title": "Show What's New Popup", + "description": "Show What's New popup when Jellyfin is updated to a new version.", + "settingName": "load.allowwhatsnew", + "type": "bool", + "default": "true" + }, + { + "title": "Use Web Client's Home Section Arrangement", + "description": "Make the arrangement of the Roku home view sections match the web client's home screen. Jellyfin will need to be closed and reopened for change to take effect.", + "settingName": "ui.home.useWebSectionArrangement", + "type": "bool", + "default": "true" + } + ] + }, + { + "title": "Item Detail Screen", + "description": "Settings relating to the appearance of the item detail screen.", + "children": [ + { + "title": "Community and Critical Ratings", + "description": "Community and critic review ratings from Rotten Tomatoes.", + "settingName": "ui.itemdetail.showRatings", + "type": "bool", + "default": "true" + }, + { + "title": "Overview Content", + "description": "A quick overview of the item selected - typically a short plot outline.", + "settingName": "ui.itemdetail.showoverviewcontent", + "type": "bool", + "default": "true" + } + ] + }, + { + "title": "Libraries", + "description": "Settings relating to the appearance of Library pages.", + "children": [ + { + "title": "General", + "description": "Settings relating to the appearance of pages in all Libraries.", + "children": [ + { + "title": "Forget Filters", + "description": "Forget applied library filters when Jellyfin is closed.", + "settingName": "itemgrid.forgetFilters", "type": "bool", - "default": "false" - }, - { - "title": "Compatibility", - "description": "Attempt to prevent playback failures.", + "default": "true" + }, + { + "title": "Grid View Settings", + "description": "Settings that apply when Grid views are enabled.", "children": [ - { - "title": "Disable HEVC", - "description": "Disable the HEVC codec on this device. This may improve playback for some devices (ultra).", - "settingName": "playback.compatibility.disablehevc", - "type": "bool", - "default": "false" - } + { + "title": "Item Count", + "description": "Show item count in the library and index of selected item.", + "settingName": "itemgrid.showItemCount", + "type": "bool", + "default": "false" + }, + { + "title": "Item Titles", + "description": "Select when to show titles.", + "settingName": "itemgrid.gridTitles", + "type": "radio", + "default": "showonhover", + "options": [ + { + "title": "Show On Hover", + "id": "showonhover" + }, + { + "title": "Always Show", + "id": "showalways" + }, + { + "title": "Always Hide", + "id": "hidealways" + } + ] + } ] - }, - { - "title": "Custom Subtitles", - "description": "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.", - "settingName": "playback.subs.custom", - "type": "bool", - "default": "false" - }, - { - "title": "Custom Trickplay", - "description": "Display custom trickplay images even if this Roku says it's displaying its own. This may cause the two images to overlay on top of each other.", - "settingName": "playback.trickplay.forcecustom", + } + ] + }, + { + "title": "TV Shows", + "description": "Settings relating to the appearance of pages in TV Libraries.", + "children": [ + { + "title": "Disable Community Rating for Episodes", + "description": "Hide the star and community rating for episodes of a TV show. This is to prevent spoilers of an upcoming good/bad episode.", + "settingName": "ui.itemdetail.showRatings", "type": "bool", "default": "false" - }, - { - "title": "Force Transcoding", - "description": "Force all playable media to be transcoded.", - "settingName": "playback.media.forceTranscode", + }, + { + "title": "Disable Unwatched Episode Count", + "description": "If enabled, the number of unwatched episodes in a series/season will be removed.", + "settingName": "ui.tvshows.disableUnwatchedEpisodeCount", "type": "bool", "default": "false" - }, - { - "title": "Maximum Resolution", - "description": "Configure the maximum resolution when playing video files on this device.", - "children": [ - { - "title": "Mode", - "description": "Apply max resolution to all files or only transcoded files.", - "settingName": "playback.resolution.mode", - "type": "radio", - "default": "transcoding", - "options": [ - { - "title": "All files", - "id": "everything" - }, - { - "title": "Only transcoded files", - "id": "transcoding" - } - ] - }, - { - "title": "Value", - "description": "Set the maximum resolution when playing video files on this device.", - "settingName": "playback.resolution.max", - "type": "radio", - "default": "auto", - "options": [ - { - "title": "Off - Attempt to play all resolutions", - "id": "off" - }, - { - "title": "Auto - Use TV resolution", - "id": "auto" - }, - { - "title": "360p", - "id": "360" - }, - { - "title": "480p", - "id": "480" - }, - { - "title": "720p", - "id": "720" - }, - { - "title": "1080p", - "id": "1080" - }, - { - "title": "4k", - "id": "2160" - }, - { - "title": "8k", - "id": "4320" - } - ] - } - ] - }, - { - "title": "Media Segment Actions", - "description": "Settings relating to how Jellyfin should handle media segments.", - "children": [ - { - "title": "Commercial Segments", - "description": "What action should Jellyfin take for commercial segments? ", - "settingName": "playback.mediasegments.commercial", - "type": "radio", - "default": "none", - "options": [ - { - "title": "Ask To Skip", - "id": "asktoskip" - }, - { - "title": "Skip", - "id": "skip" - }, - { - "title": "None", - "id": "none" - } - ] - }, - { - "title": "Intro Segments", - "description": "What action should Jellyfin take for intro segments? ", - "settingName": "playback.mediasegments.intro", - "type": "radio", - "default": "asktoskip", - "options": [ - { - "title": "Ask To Skip", - "id": "asktoskip" - }, - { - "title": "Skip", - "id": "skip" - }, - { - "title": "None", - "id": "None" - } - ] - }, - { - "title": "Outro Segments", - "description": "What action should Jellyfin take for outro segments? ", - "settingName": "playback.mediasegments.outro", - "type": "radio", - "default": "asktoskip", - "options": [ - { - "title": "Ask To Skip", - "id": "asktoskip" - }, - { - "title": "Skip", - "id": "skip" - }, - { - "title": "None", - "id": "none" - } - ] - }, - { - "title": "Preview Segments", - "description": "What action should Jellyfin take for preview segments? ", - "settingName": "playback.mediasegments.preview", - "type": "radio", - "default": "none", - "options": [ - { - "title": "Ask To Skip", - "id": "asktoskip" - }, - { - "title": "Skip", - "id": "skip" - }, - { - "title": "None", - "id": "none" - } - ] - }, - { - "title": "Recap Segments", - "description": "What action should Jellyfin take for recap segments? ", - "settingName": "playback.mediasegments.recap", - "type": "radio", - "default": "none", - "options": [ - { - "title": "Ask To Skip", - "id": "asktoskip" - }, - { - "title": "Skip", - "id": "skip" - }, - { - "title": "None", - "id": "none" - } - ] - } - ] - }, - { - "title": "Next Episode Button Time", - "description": "If no Outro media segment exists, set how many seconds before the end of an episode the Skip Outro button should appear. Set to 0 to disable.", - "settingName": "playback.nextupbuttonseconds", - "type": "integer", - "default": "30" - }, - { - "title": "Preferred Audio Codec", - "description": "Use the selected audio codec for transcodes. If the device or stream does not support it, a fallback codec will be used.", - "settingName": "playback.preferredAudioCodec", - "type": "radio", - "default": "auto", - "options": [ - { - "title": "Use system settings", - "id": "auto" - }, - { - "title": "AAC", - "id": "aac" - }, - { - "title": "DD (AC3)", - "id": "ac3" - }, - { - "title": "DD+ (EAC3)", - "id": "eac3" - }, - { - "title": "DTS", - "id": "dts" - } - ] - }, - { - "title": "Text Subtitles Only", - "description": "Only display text subtitles to minimize transcoding.", - "settingName": "playback.subs.onlytext", + }, + { + "title": "Skip Details for Single Seasons", + "description": "If enabled, selecting a TV series with only one season will go straight to the episode list rather than the show details and season list.", + "settingName": "ui.tvshows.goStraightToEpisodeListing", "type": "bool", "default": "false" - }, - { - "title": "Video Codec Support", - "description": "Enable or disable Direct Play support for certain codecs.", - "children": [ - { - "title": "MPEG-2", - "description": "Support Direct Play of MPEG-2 content (e.g., Live TV). This will prevent transcoding of MPEG-2 content, but uses significantly more bandwidth.", - "settingName": "playback.mpeg2", - "type": "bool", - "default": "false" - }, - { - "title": "MPEG-4", - "description": "Support Direct Play of MPEG-4 content. This may need to be disabled for playback of DIVX encoded video files.", - "settingName": "playback.mpeg4", - "type": "bool", - "default": "true" - } - ] - }, - { - "title": "Video Profile Level Support", - "description": "Attempt Direct Play of potentially unsupported profile levels", - "children": [ - { - "title": "H.264", - "description": "Attempt Direct Play for H.264 media with unsupported profile levels before falling back to transcoding if it fails.", - "settingName": "playback.tryDirect.h264ProfileLevel", - "type": "bool", - "default": "false" - }, - { - "title": "HEVC", - "description": "Attempt Direct Play for HEVC media with unsupported profile levels before falling back to transcoding if it fails.", - "settingName": "playback.tryDirect.hevcProfileLevel", - "type": "bool", - "default": "false" - } - ] - } - ] - }, - { - "title": "User Interface", - "description": "Settings relating to how the application looks.", - "children": [ - { - "title": "General", - "description": "Settings relating to the appearance of the Home screen and the program in general.", - "children": [ - { - "title": "Episode Images Next Up", - "description": "What type of images to use for Episodes shown in the 'Next Up' and 'Continue Watching' sections.", - "settingName": "ui-general-episodeimagesnextup", - "type": "radio", - "default": "webclient", - "options": [ - { - "title": "Use Web Client Setting", - "id": "webclient" - }, - { - "title": "Use Episode Image", - "id": "episode" - }, - { - "title": "Use Show Image", - "id": "show" - } - ] - }, - { - "title": "Hide Clock", - "description": "Hide all clocks in Jellyfin. Jellyfin will need to be closed and reopened for changes to take effect.", - "settingName": "ui.design.hideclock", - "type": "bool", - "default": "false" - }, - { - "title": "Max Days Next Up", - "description": "Set the maximum amount of days a show should stay in the 'Next Up' list without watching it.", - "settingName": "ui.details.maxdaysnextup", - "type": "integer", - "default": "365" - }, - { - "title": "Rewatching Next Up", - "description": "Show already watched episodes in 'Next Up' sections.", - "settingName": "ui.details.enablerewatchingnextup", - "type": "bool", - "default": "false" - }, - { - "title": "Show What's New Popup", - "description": "Show What's New popup when Jellyfin is updated to a new version.", - "settingName": "load.allowwhatsnew", - "type": "bool", - "default": "true" - }, - { - "title": "Use Web Client's Home Section Arrangement", - "description": "Make the arrangement of the Roku home view sections match the web client's home screen. Jellyfin will need to be closed and reopened for change to take effect.", - "settingName": "ui.home.useWebSectionArrangement", - "type": "bool", - "default": "true" - } - ] - }, - { - "title": "Item Detail Screen", - "description": "Settings relating to the appearance of the item detail screen.", - "children": [ - { - "title": "Community and Critical Ratings", - "description": "Community and critic review ratings from Rotten Tomatoes.", - "settingName": "ui.itemdetail.showRatings", - "type": "bool", - "default": "true" - }, - { - "title": "Overview Content", - "description": "A quick overview of the item selected - typically a short plot outline.", - "settingName": "ui.itemdetail.showoverviewcontent", - "type": "bool", - "default": "true" - } - ] - }, - { - "title": "Libraries", - "description": "Settings relating to the appearance of Library pages.", - "children": [ - { - "title": "General", - "description": "Settings relating to the appearance of pages in all Libraries.", - "children": [ - { - "title": "Forget Filters", - "description": "Forget applied library filters when Jellyfin is closed.", - "settingName": "itemgrid.forgetFilters", - "type": "bool", - "default": "true" - }, - { - "title": "Grid View Settings", - "description": "Settings that apply when Grid views are enabled.", - "children": [ - { - "title": "Item Count", - "description": "Show item count in the library and index of selected item.", - "settingName": "itemgrid.showItemCount", - "type": "bool", - "default": "false" - }, - { - "title": "Item Titles", - "description": "Select when to show titles.", - "settingName": "itemgrid.gridTitles", - "type": "radio", - "default": "showonhover", - "options": [ - { - "title": "Show On Hover", - "id": "showonhover" - }, - { - "title": "Always Show", - "id": "showalways" - }, - { - "title": "Always Hide", - "id": "hidealways" - } - ] - } - ] - } - ] - }, - { - "title": "TV Shows", - "description": "Settings relating to the appearance of pages in TV Libraries.", - "children": [ - { - "title": "Disable Community Rating for Episodes", - "description": "Hide the star and community rating for episodes of a TV show. This is to prevent spoilers of an upcoming good/bad episode.", - "settingName": "ui.itemdetail.showRatings", - "type": "bool", - "default": "false" - }, - { - "title": "Disable Unwatched Episode Count", - "description": "If enabled, the number of unwatched episodes in a series/season will be removed.", - "settingName": "ui.tvshows.disableUnwatchedEpisodeCount", - "type": "bool", - "default": "false" - }, - { - "title": "Skip Details for Single Seasons", - "description": "If enabled, selecting a TV series with only one season will go straight to the episode list rather than the show details and season list.", - "settingName": "ui.tvshows.goStraightToEpisodeListing", - "type": "bool", - "default": "false" - } - ] - } - ] - } + } + ] + } ] - } -] \ No newline at end of file + } + ] + } +] From 732bb56cd0f405521811a33f0a5262bc39682e40 Mon Sep 17 00:00:00 2001 From: David Briglio <3886546+DavidBriglio@users.noreply.github.com> Date: Fri, 2 May 2025 14:58:40 -0400 Subject: [PATCH 15/27] Fixed default and accessor for autoplay episode limit --- components/ItemGrid/LoadVideoContentTask.bs | 12 +++--------- locale/en_US/translations.ts | 4 ++-- settings/settings.json | 6 +++--- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.bs b/components/ItemGrid/LoadVideoContentTask.bs index 1c2db6c5..4b2d3dac 100644 --- a/components/ItemGrid/LoadVideoContentTask.bs +++ b/components/ItemGrid/LoadVideoContentTask.bs @@ -185,7 +185,8 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s end if if m.global.session.user.Configuration.EnableNextEpisodeAutoPlay - episodeQueueLimit = m.global.session.user.settings.["playback.episodeQueueLimit"].ToInt() + episodeQueueLimit = chainLookupReturn(m.global.session, "user.settings.episodeQueueLimit", "50") + episodeQueueLimit = episodeQueueLimit.ToInt() if isValid(video.showID) addNextEpisodesToQueue(video.showID, additionalPartsCount, episodeQueueLimit) end if @@ -570,17 +571,10 @@ sub addNextEpisodesToQueue(showID, additionalPartsCount as integer, queueLimit a end if end if - limitParam = queueLimit - - ' If the queue limit is set to 0 / disabled, default to 50 - if limitParam = 0 - limitParam = 50 - end if - url = Substitute("Shows/{0}/Episodes", showID) urlParams = { "UserId": m.global.session.user.id } urlParams.Append({ "StartItemId": videoID }) - urlParams.Append({ "Limit": limitParam }) + urlParams.Append({ "Limit": queueLimit }) resp = APIRequest(url, urlParams) data = getJson(resp) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index bbaa2a79..026a565b 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1017,8 +1017,8 @@ Autoplay Episode Limit - The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. Set to 0 to disable. - The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. Set to 0 to disable. + The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. + The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. View All diff --git a/settings/settings.json b/settings/settings.json index 5c7de524..89c55ae7 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -125,10 +125,10 @@ "children": [ { "title": "Autoplay Episode Limit", - "description": "The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. Set to 0 to disable.", - "settingName": "playback.episodeQueueLimit", + "description": "The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled.", + "settingName": "episodeQueueLimit", "type": "integer", - "default": "0" + "default": "50" }, { "title": "Bitrate Limit", From 701f90d952638d531bc23a34d204f80e755d7c86 Mon Sep 17 00:00:00 2001 From: David Briglio <3886546+DavidBriglio@users.noreply.github.com> Date: Fri, 2 May 2025 17:08:28 -0400 Subject: [PATCH 16/27] Change autoplay limit to plus 1 --- components/ItemGrid/LoadVideoContentTask.bs | 2 +- locale/en_US/translations.ts | 4 ++-- settings/settings.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.bs b/components/ItemGrid/LoadVideoContentTask.bs index 4b2d3dac..6cedfae4 100644 --- a/components/ItemGrid/LoadVideoContentTask.bs +++ b/components/ItemGrid/LoadVideoContentTask.bs @@ -574,7 +574,7 @@ sub addNextEpisodesToQueue(showID, additionalPartsCount as integer, queueLimit a url = Substitute("Shows/{0}/Episodes", showID) urlParams = { "UserId": m.global.session.user.id } urlParams.Append({ "StartItemId": videoID }) - urlParams.Append({ "Limit": queueLimit }) + urlParams.Append({ "Limit": queueLimit + 1 }) resp = APIRequest(url, urlParams) data = getJson(resp) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index 026a565b..5451c916 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1017,8 +1017,8 @@ Autoplay Episode Limit - The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. - The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled. + The number of episodes that will play automatically after the selected episode. Requires 'Play next episode automatically' server setting to be enabled. + The number of episodes that will play automatically after the selected episode. Requires 'Play next episode automatically' server setting to be enabled. View All diff --git a/settings/settings.json b/settings/settings.json index 89c55ae7..080a7ae6 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -125,7 +125,7 @@ "children": [ { "title": "Autoplay Episode Limit", - "description": "The number of episodes that will play automatically before stopping. Requires 'Play next episode automatically' server setting to be enabled.", + "description": "The number of episodes that will play automatically after the selected episode. Requires 'Play next episode automatically' server setting to be enabled.", "settingName": "episodeQueueLimit", "type": "integer", "default": "50" From bd23edfa3630d10468c944d45fcea228f6a20cce Mon Sep 17 00:00:00 2001 From: David Briglio <3886546+DavidBriglio@users.noreply.github.com> Date: Fri, 2 May 2025 17:45:40 -0400 Subject: [PATCH 17/27] Cleanup and changelog --- components/ItemGrid/LoadVideoContentTask.bs | 7 +++---- source/static/whatsNew/3.0.3.json | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/components/ItemGrid/LoadVideoContentTask.bs b/components/ItemGrid/LoadVideoContentTask.bs index 6cedfae4..a3b40aa7 100644 --- a/components/ItemGrid/LoadVideoContentTask.bs +++ b/components/ItemGrid/LoadVideoContentTask.bs @@ -184,12 +184,11 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s end for end if - if m.global.session.user.Configuration.EnableNextEpisodeAutoPlay + if m.global.session.user.Configuration.EnableNextEpisodeAutoPlay and isValid(video.showID) episodeQueueLimit = chainLookupReturn(m.global.session, "user.settings.episodeQueueLimit", "50") episodeQueueLimit = episodeQueueLimit.ToInt() - if isValid(video.showID) - addNextEpisodesToQueue(video.showID, additionalPartsCount, episodeQueueLimit) - end if + + addNextEpisodesToQueue(video.showID, additionalPartsCount, episodeQueueLimit) end if playbackPosition = 0! diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json index 5dfbfa1d..d79265b1 100644 --- a/source/static/whatsNew/3.0.3.json +++ b/source/static/whatsNew/3.0.3.json @@ -1,4 +1,8 @@ [ + { + "description": "Add playback \"Autoplay Episode Limit\" setting and behavior", + "author": "rookly" + }, { "description": "Fix crash when watching a recording in progress", "author": "1hitsong" From 425ab02c917f7152c3e4b08cc4c56de1fdfa5929 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 19 Apr 2025 12:46:16 -0400 Subject: [PATCH 18/27] Move voice box search --- components/ItemGrid/ItemGrid.bs | 1 - components/ItemGrid/ItemGrid.xml | 1 - components/Libraries/MusicLibraryView.bs | 1 - components/Libraries/MusicLibraryView.xml | 1 - components/Libraries/VisualLibraryScene.bs | 63 +++++++-------------- components/Libraries/VisualLibraryScene.xml | 15 +---- components/liveTv/LiveTVLibraryView.bs | 2 - components/liveTv/LiveTVLibraryView.xml | 1 - 8 files changed, 24 insertions(+), 61 deletions(-) diff --git a/components/ItemGrid/ItemGrid.bs b/components/ItemGrid/ItemGrid.bs index fa669532..3f60cc17 100644 --- a/components/ItemGrid/ItemGrid.bs +++ b/components/ItemGrid/ItemGrid.bs @@ -13,7 +13,6 @@ import "pkg:/source/utils/deviceCapabilities.bs" import "pkg:/source/utils/misc.bs" sub init() - m.top.findNode("VoiceBoxCover").color = chainLookupReturn(m.global.session, "user.settings.colorBackground", ColorPalette.VIEWBACKGROUND) m.loadItemsTask1 = createObject("roSGNode", "LoadItemsTask") overhang = m.top.getScene().findNode("overhang") diff --git a/components/ItemGrid/ItemGrid.xml b/components/ItemGrid/ItemGrid.xml index 958db28f..e66e0aad 100644 --- a/components/ItemGrid/ItemGrid.xml +++ b/components/ItemGrid/ItemGrid.xml @@ -2,7 +2,6 @@ - diff --git a/components/Libraries/MusicLibraryView.bs b/components/Libraries/MusicLibraryView.bs index abace9ea..afce454a 100644 --- a/components/Libraries/MusicLibraryView.bs +++ b/components/Libraries/MusicLibraryView.bs @@ -65,7 +65,6 @@ sub init() m.itemGrid.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.genrelist.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) - m.top.findNode("VoiceBoxCover").color = chainLookupReturn(m.global.session, "user.settings.colorBackground", ColorPalette.VIEWBACKGROUND) m.overhang.isVisible = false diff --git a/components/Libraries/MusicLibraryView.xml b/components/Libraries/MusicLibraryView.xml index c740ce09..2a0b82fb 100644 --- a/components/Libraries/MusicLibraryView.xml +++ b/components/Libraries/MusicLibraryView.xml @@ -2,7 +2,6 @@ - diff --git a/components/Libraries/VisualLibraryScene.bs b/components/Libraries/VisualLibraryScene.bs index d4450aa8..25e10d39 100644 --- a/components/Libraries/VisualLibraryScene.bs +++ b/components/Libraries/VisualLibraryScene.bs @@ -35,12 +35,6 @@ sub setupNodes() m.dropdownOptions = m.top.findNode("dropdownOptions") m.submitButton = m.top.findNode("submitButton") - m.searchButton = m.top.findNode("searchButton") - m.searchButton.textColor = ColorPalette.DARKGREY - m.searchButton.focusTextColor = ColorPalette.WHITE - m.searchButton.background = ColorPalette.WHITE - m.searchButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) - m.sortButton = m.top.findNode("sortButton") m.sortButton.textColor = ColorPalette.DARKGREY m.sortButton.focusTextColor = ColorPalette.WHITE @@ -73,7 +67,6 @@ sub init() m.itemGrid.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) m.genreList.focusBitmapBlendColor = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) - m.top.findNode("VoiceBoxCover").color = chainLookupReturn(m.global.session, "user.settings.colorBackground", ColorPalette.VIEWBACKGROUND) m.overhang.isVisible = false @@ -101,11 +94,7 @@ sub init() m.genreList.content = m.genreData 'Voice filter setup - m.voiceBox.voiceEnabled = true - m.voiceBox.active = true m.voiceBox.observeField("text", "onvoiceFilter") - 'set voice help text - m.voiceBox.hintText = tr("Use voice remote to search") 'backdrop m.newBackdrop.observeField("loadStatus", "newBGLoaded") @@ -281,7 +270,7 @@ sub loadInitialItems() else if isValid(m.top.parentItem.parentFolder) ' The API does not support searching inside boxsets - m.searchButton.disabled = true + ' m.searchButton.disabled = true end if end if end if @@ -360,7 +349,7 @@ sub loadInitialItems() if isStringEqual(m.top.mediaType, ItemType.MYLIST) ' Search doesn't work for My List because it is a playlist - m.searchButton.disabled = true + ' m.searchButton.disabled = true m.itemGrid.itemComponentName = "GridItemMedium" m.itemGrid.itemSize = "[400, 330]" @@ -782,10 +771,9 @@ sub ItemDataLoaded(msg) if m.loadedItems = 0 m.emptyText.text = tr("No items found. Try adjusting your selected filters.") m.emptyText.visible = true - m.searchButton.focus = true - m.searchButton.setfocus(true) + m.voiceBox.setfocus(true) group = m.global.sceneManager.callFunc("getActiveScene") - group.lastFocus = m.searchButton + group.lastFocus = m.voiceBox end if return @@ -845,10 +833,9 @@ sub ItemDataLoaded(msg) m.emptyText.text = tr("No items found. Try adjusting your selected filters.") m.emptyText.visible = true - m.searchButton.focus = true - m.searchButton.setfocus(true) + m.voiceBox.setfocus(true) group = m.global.sceneManager.callFunc("getActiveScene") - group.lastFocus = m.searchButton + group.lastFocus = m.voiceBox end if stopLoadingSpinner() @@ -1175,9 +1162,6 @@ sub resetDropdownsToDefaultState() end sub sub onSearchTermChanged() - m.searchButton.text = m.top.searchTerm - m.searchButton.focus = false - processDropdownState(m.top.searchTerm) if m.bypassSearchEvent @@ -1222,7 +1206,6 @@ sub alphaSelectedChanged() end sub sub unfocusAllButtons() - m.searchButton.focus = false m.sortButton.focus = false m.sortOrderButton.focus = false m.filterButton.focus = false @@ -1258,11 +1241,11 @@ sub onvoiceFilter() return end if - if m.searchButton.disabled - m.itemGrid.setFocus(m.itemGrid.opacity = 1) - m.genreList.setFocus(m.genreList.opacity = 1) - return - end if + ' if m.searchButton.disabled + ' m.itemGrid.setFocus(m.itemGrid.opacity = 1) + ' m.genreList.setFocus(m.genreList.opacity = 1) + ' return + ' end if m.top.searchTerm = m.voiceBox.text.trim() end sub @@ -1418,12 +1401,12 @@ end sub function getOverhangingButton() if m.itemGrid.isinFocusChain() if m.itemGrid.numColumns = 4 - buttonList = [m.searchButton, m.sortButton, m.filterButton, m.viewButton] + buttonList = [m.voiceBox, m.sortButton, m.filterButton, m.viewButton] return buttonList[m.itemGrid.currFocusColumn] end if if m.itemGrid.numColumns = 7 - if m.itemGrid.currFocusColumn = 0 then return m.searchButton + if m.itemGrid.currFocusColumn = 0 then return m.voiceBox if m.itemGrid.currFocusColumn <= 2 then return m.sortButton if m.itemGrid.currFocusColumn <= 3 then return m.sortOrderButton if m.itemGrid.currFocusColumn <= 5 then return m.filterButton @@ -1432,14 +1415,14 @@ function getOverhangingButton() end if if m.genreList.isinFocusChain() - if m.genreList.currFocusColumn <= 0 then return m.searchButton + if m.genreList.currFocusColumn <= 0 then return m.voiceBox if m.genreList.currFocusColumn <= 2 then return m.sortButton if m.genreList.currFocusColumn <= 3 then return m.sortOrderButton if m.genreList.currFocusColumn <= 5 then return m.filterButton return m.viewButton end if - return m.searchButton + return m.voiceBox end function @@ -1458,8 +1441,8 @@ function onKeyEvent(key as string, press as boolean) as boolean end if if isStringEqual(key, KeyCode.OK) - if m.searchButton.isinFocusChain() - if m.searchButton.disabled then return false + if m.voiceBox.isinFocusChain() + ' if m.searchButton.disabled then return false buttonData = [ tr("Search") @@ -1525,8 +1508,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if m.dropdownOptions.isinFocusChain() if isStringEqual(key, KeyCode.LEFT) - if m.searchButton.isinFocusChain() - m.searchButton.focus = false + if m.voiceBox.isinFocusChain() m.sortButton.focus = false m.sortOrderButton.focus = false m.viewButton.focus = false @@ -1540,11 +1522,10 @@ function onKeyEvent(key as string, press as boolean) as boolean end if if m.sortButton.isinFocusChain() - m.searchButton.focus = true - m.searchButton.setFocus(true) + m.voiceBox.setFocus(true) m.sortButton.focus = false group = m.global.sceneManager.callFunc("getActiveScene") - group.lastFocus = m.searchButton + group.lastFocus = m.voiceBox return true end if @@ -1577,8 +1558,7 @@ function onKeyEvent(key as string, press as boolean) as boolean end if if isStringEqual(key, KeyCode.Right) - if m.searchButton.isinFocusChain() - m.searchButton.focus = false + if m.voiceBox.isinFocusChain() m.sortButton.focus = true m.sortButton.setFocus(true) group = m.global.sceneManager.callFunc("getActiveScene") @@ -1617,7 +1597,6 @@ function onKeyEvent(key as string, press as boolean) as boolean if isStringEqual(key, KeyCode.DOWN) if m.loadedItems = 0 then return false - m.searchButton.focus = false m.sortButton.focus = false m.sortOrderButton.focus = false m.viewButton.focus = false diff --git a/components/Libraries/VisualLibraryScene.xml b/components/Libraries/VisualLibraryScene.xml index a8f03f13..0b47e043 100644 --- a/components/Libraries/VisualLibraryScene.xml +++ b/components/Libraries/VisualLibraryScene.xml @@ -1,9 +1,6 @@ - - - @@ -12,15 +9,9 @@ - + + + diff --git a/components/liveTv/LiveTVLibraryView.bs b/components/liveTv/LiveTVLibraryView.bs index 0dcdf56e..783a53e0 100644 --- a/components/liveTv/LiveTVLibraryView.bs +++ b/components/liveTv/LiveTVLibraryView.bs @@ -8,8 +8,6 @@ import "pkg:/source/utils/deviceCapabilities.bs" import "pkg:/source/utils/misc.bs" sub init() - m.top.findNode("VoiceBoxCover").color = chainLookupReturn(m.global.session, "user.settings.colorBackground", ColorPalette.VIEWBACKGROUND) - overhang = m.top.getScene().findNode("overhang") overhang.isVisible = false diff --git a/components/liveTv/LiveTVLibraryView.xml b/components/liveTv/LiveTVLibraryView.xml index 1f13c2a6..495d2101 100644 --- a/components/liveTv/LiveTVLibraryView.xml +++ b/components/liveTv/LiveTVLibraryView.xml @@ -2,7 +2,6 @@ - Date: Sat, 19 Apr 2025 15:17:00 -0400 Subject: [PATCH 19/27] Move voice search to search dropdown --- components/ItemGrid/Alpha.bs | 9 ---- components/ItemGrid/Alpha.xml | 9 ---- components/ItemGrid/ItemGrid.bs | 7 --- components/Libraries/MusicLibraryView.bs | 6 --- components/Libraries/VisualLibraryScene.bs | 53 ++++++++++++++++--- components/Libraries/VisualLibraryScene.xml | 57 ++++++++++++--------- components/liveTv/LiveTVLibraryView.bs | 7 --- 7 files changed, 80 insertions(+), 68 deletions(-) diff --git a/components/ItemGrid/Alpha.bs b/components/ItemGrid/Alpha.bs index 5bf5b0ef..7aee06ef 100644 --- a/components/ItemGrid/Alpha.bs +++ b/components/ItemGrid/Alpha.bs @@ -12,15 +12,6 @@ sub init() m.alphaMenu.focusFootprintBlendColor = ColorPalette.TRANSPARENT m.alphaMenu.setFocus(false) - - ' show mic icon above alpha menu if remote supports voice commands - if m.global.device.hasVoiceRemote - alphaMic = m.top.findNode("alphaMic") - alphaMic.visible = true - - micText = m.top.findNode("alphaMicText") - micText.font.size = 22 - end if end sub function onKeyEvent(key as string, press as boolean) as boolean diff --git a/components/ItemGrid/Alpha.xml b/components/ItemGrid/Alpha.xml index a1c817c6..1cf9c54d 100644 --- a/components/ItemGrid/Alpha.xml +++ b/components/ItemGrid/Alpha.xml @@ -1,15 +1,6 @@ - - - + + + + - - + + @@ -88,27 +120,6 @@ - - - - diff --git a/components/liveTv/LiveTVLibraryView.bs b/components/liveTv/LiveTVLibraryView.bs index 783a53e0..30466fe8 100644 --- a/components/liveTv/LiveTVLibraryView.bs +++ b/components/liveTv/LiveTVLibraryView.bs @@ -11,13 +11,6 @@ sub init() overhang = m.top.getScene().findNode("overhang") overhang.isVisible = false - ' adjust alpha menu mic icon since there is no overhang - m.alpha = m.top.findNode("alpha") - alphaMic = m.alpha.findNode("alphaMic") - alphaMic.translation = [60, 62] - alphaMicText = m.alpha.findNode("alphaMicText") - alphaMicText.visible = false - m.options = m.top.findNode("options") m.showItemCount = m.global.session.user.settings["itemgrid.showItemCount"] From 7e53802a6e567f7632c34ff279ce2e4a720335d4 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 19 Apr 2025 15:48:25 -0400 Subject: [PATCH 20/27] Allow disabled state for search --- components/Libraries/VisualLibraryScene.bs | 73 +++++++++++++++------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/components/Libraries/VisualLibraryScene.bs b/components/Libraries/VisualLibraryScene.bs index 0efa6bee..c92e15d1 100644 --- a/components/Libraries/VisualLibraryScene.bs +++ b/components/Libraries/VisualLibraryScene.bs @@ -10,6 +10,12 @@ import "pkg:/source/utils/config.bs" import "pkg:/source/utils/deviceCapabilities.bs" import "pkg:/source/utils/misc.bs" +enum SearchButtonState + ACTIVE + INACTIVE + DISABLED +end enum + sub setupNodes() m.options = m.top.findNode("options") m.itemGrid = m.top.findNode("itemGrid") @@ -60,10 +66,11 @@ sub setupNodes() m.viewButton.background = ColorPalette.WHITE m.viewButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) - setSearchBackground(false) + setSearchBackground(SearchButtonState.INACTIVE) end sub sub init() + m.searchButtonDisabled = false m.top.optionsAvailable = false setupNodes() m.bypassSearchEvent = false @@ -127,6 +134,10 @@ sub init() searchPoster.bitmap = "" searchPoster.blendColor = ColorPalette.WHITE end if + searchPoster = m.voiceBox.getChild(5) + if isChainValid(searchPoster, "bitmap") + searchPoster.blendColor = ColorPalette.MIDGREY + end if end if end sub @@ -279,7 +290,8 @@ sub loadInitialItems() else if isValid(m.top.parentItem.parentFolder) ' The API does not support searching inside boxsets - ' m.searchButton.disabled = true + m.searchButtonDisabled = true + setSearchBackground(SearchButtonState.DISABLED) end if end if end if @@ -358,7 +370,8 @@ sub loadInitialItems() if isStringEqual(m.top.mediaType, ItemType.MYLIST) ' Search doesn't work for My List because it is a playlist - ' m.searchButton.disabled = true + m.searchButtonDisabled = true + setSearchBackground(SearchButtonState.DISABLED) m.itemGrid.itemComponentName = "GridItemMedium" m.itemGrid.itemSize = "[400, 330]" @@ -781,7 +794,7 @@ sub ItemDataLoaded(msg) m.emptyText.text = tr("No items found. Try adjusting your selected filters.") m.emptyText.visible = true m.voiceBox.setfocus(true) - setSearchBackground(true) + setSearchBackground(SearchButtonState.ACTIVE) group = m.global.sceneManager.callFunc("getActiveScene") group.lastFocus = m.voiceBox end if @@ -844,7 +857,7 @@ sub ItemDataLoaded(msg) m.emptyText.text = tr("No items found. Try adjusting your selected filters.") m.emptyText.visible = true m.voiceBox.setfocus(true) - setSearchBackground(true) + setSearchBackground(SearchButtonState.ACTIVE) group = m.global.sceneManager.callFunc("getActiveScene") group.lastFocus = m.voiceBox end if @@ -1174,7 +1187,7 @@ end sub sub onSearchTermChanged() m.voiceBox.hintText = m.top.searchTerm - setSearchBackground(false) + setSearchBackground(SearchButtonState.INACTIVE) processDropdownState(m.top.searchTerm) @@ -1220,7 +1233,7 @@ sub alphaSelectedChanged() end sub sub unfocusAllButtons() - setSearchBackground(false) + setSearchBackground(SearchButtonState.INACTIVE) m.sortButton.focus = false m.sortOrderButton.focus = false m.filterButton.focus = false @@ -1258,11 +1271,11 @@ sub onvoiceFilter() return end if - ' if m.searchButton.disabled - ' m.itemGrid.setFocus(m.itemGrid.opacity = 1) - ' m.genreList.setFocus(m.genreList.opacity = 1) - ' return - ' end if + if m.searchButtonDisabled + m.itemGrid.setFocus(m.itemGrid.opacity = 1) + m.genreList.setFocus(m.genreList.opacity = 1) + return + end if m.top.searchTerm = m.voiceBox.text.trim() end sub @@ -1450,7 +1463,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if m.itemGrid.isinFocusChain() or m.genreList.isinFocusChain() overhangButton = getOverhangingButton() if overhangButton.isSubType("VoiceTextEditBox") - setSearchBackground(true) + setSearchBackground(SearchButtonState.ACTIVE) else overhangButton.focus = true end if @@ -1463,7 +1476,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if isStringEqual(key, KeyCode.OK) if m.voiceBox.isinFocusChain() - ' if m.searchButton.disabled then return false + if m.searchButtonDisabled then return false buttonData = [ tr("Search") @@ -1530,7 +1543,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if m.dropdownOptions.isinFocusChain() if isStringEqual(key, KeyCode.LEFT) if m.voiceBox.isinFocusChain() - setSearchBackground(false) + setSearchBackground(SearchButtonState.INACTIVE) m.sortButton.focus = false m.sortOrderButton.focus = false m.viewButton.focus = false @@ -1545,7 +1558,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if m.sortButton.isinFocusChain() m.voiceBox.setFocus(true) - setSearchBackground(true) + setSearchBackground(SearchButtonState.ACTIVE) m.sortButton.focus = false group = m.global.sceneManager.callFunc("getActiveScene") group.lastFocus = m.voiceBox @@ -1582,7 +1595,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if isStringEqual(key, KeyCode.Right) if m.voiceBox.isinFocusChain() - setSearchBackground(false) + setSearchBackground(SearchButtonState.INACTIVE) m.sortButton.focus = true m.sortButton.setFocus(true) group = m.global.sceneManager.callFunc("getActiveScene") @@ -1621,7 +1634,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if isStringEqual(key, KeyCode.DOWN) if m.loadedItems = 0 then return false - setSearchBackground(false) + setSearchBackground(SearchButtonState.INACTIVE) m.sortButton.focus = false m.sortOrderButton.focus = false m.viewButton.focus = false @@ -1640,7 +1653,7 @@ function onKeyEvent(key as string, press as boolean) as boolean m.itemGrid.setFocus(m.itemGrid.opacity = 1) m.genreList.setFocus(m.genreList.opacity = 1) m.voiceBox.setFocus(false) - setSearchBackground(false) + setSearchBackground(SearchButtonState.INACTIVE) group = m.global.sceneManager.callFunc("getActiveScene") group.lastFocus = m.itemGrid.opacity = 1 ? m.itemGrid : m.genreList end if @@ -1770,15 +1783,33 @@ sub reclaimResources() m.genreList.content = m.genreData end sub -sub setSearchBackground(isActive as boolean) - if isActive +sub setSearchBackground(state) + if state = SearchButtonState.ACTIVE m.voiceBox.textColor = ColorPalette.WHITE m.voiceBox.hintTextColor = ColorPalette.WHITE m.voiceBoxBackground.blendColor = ColorPalette.HIGHLIGHT + + searchPoster = m.voiceBox.getChild(5) + if isChainValid(searchPoster, "bitmap") + searchPoster.blendColor = ColorPalette.WHITE + end if + + return + end if + + if state = SearchButtonState.DISABLED + m.voiceBox.textColor = ColorPalette.MIDGREY + m.voiceBox.hintTextColor = ColorPalette.MIDGREY + m.voiceBoxBackground.blendColor = ColorPalette.MIDGREY return end if m.voiceBox.textColor = ColorPalette.MIDGREY m.voiceBox.hintTextColor = ColorPalette.MIDGREY m.voiceBoxBackground.blendColor = ColorPalette.WHITE + + searchPoster = m.voiceBox.getChild(5) + if isChainValid(searchPoster, "bitmap") + searchPoster.blendColor = ColorPalette.MIDGREY + end if end sub From 4058c80e7657d7c419b5bcda093d087bf0566707 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 19 Apr 2025 19:37:30 -0400 Subject: [PATCH 21/27] Reactivate voice after keyboard dialog closes --- components/Libraries/VisualLibraryScene.bs | 96 ++++++++++++++++------ components/data/SceneManager.bs | 3 +- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/components/Libraries/VisualLibraryScene.bs b/components/Libraries/VisualLibraryScene.bs index c92e15d1..3a5ecd19 100644 --- a/components/Libraries/VisualLibraryScene.bs +++ b/components/Libraries/VisualLibraryScene.bs @@ -70,6 +70,7 @@ sub setupNodes() end sub sub init() + m.disabledReasaon = string.EMPTY m.searchButtonDisabled = false m.top.optionsAvailable = false setupNodes() @@ -134,9 +135,40 @@ sub init() searchPoster.bitmap = "" searchPoster.blendColor = ColorPalette.WHITE end if - searchPoster = m.voiceBox.getChild(5) - if isChainValid(searchPoster, "bitmap") - searchPoster.blendColor = ColorPalette.MIDGREY + + searchText = m.voiceBox.getChild(1) + if isChainValid(searchText, "height") + searchText.vertAlign = "bottom" + searchText.height = 37 + end if + + m.micIcon = m.voiceBox.getChild(6) + if isValid(m.micIcon) + m.micIcon.observeFieldScoped("blendColor", "onMicIconBlendColorChange") + end if + end if +end sub + +' VoiceTextEditBox.voiceEnabled doesn't have alwaysNotify enabled +' To trigger a change event, we must change the value to something else, then back to the value we want +sub onVoiceEnabledChange() + m.voiceBox.voiceEnabled = m.searchButtonDisabled + m.voiceBox.voiceEnabled = not m.searchButtonDisabled +end sub + +sub onMicIconBlendColorChange() + ' DARKGREY: 269488383 + ' WHITE: -1 + ' LIGHTGREY: -1431655681 + ' HIGHLIGHT: -9934849 + + if m.voiceBoxBackground.blendColor = -9934849 + if m.micIcon.blendColor <> -1 + m.micIcon.blendColor = ColorPalette.WHITE + end if + else + if m.micIcon.blendColor <> 269488383 + m.micIcon.blendColor = ColorPalette.DARKGREY end if end if end sub @@ -289,9 +321,7 @@ sub loadInitialItems() end if else if isValid(m.top.parentItem.parentFolder) - ' The API does not support searching inside boxsets - m.searchButtonDisabled = true - setSearchBackground(SearchButtonState.DISABLED) + disableSearch(tr("Search is unavailable because the API does not support searching inside boxsets.")) end if end if end if @@ -369,9 +399,7 @@ sub loadInitialItems() end if if isStringEqual(m.top.mediaType, ItemType.MYLIST) - ' Search doesn't work for My List because it is a playlist - m.searchButtonDisabled = true - setSearchBackground(SearchButtonState.DISABLED) + disableSearch(tr("Search is unavailable because the API does not support searching inside My List.")) m.itemGrid.itemComponentName = "GridItemMedium" m.itemGrid.itemSize = "[400, 330]" @@ -1186,7 +1214,21 @@ sub resetDropdownsToDefaultState() end sub sub onSearchTermChanged() - m.voiceBox.hintText = m.top.searchTerm + if m.top.searchTerm = string.EMPTY + ' User clicked on search, then pressed back without searching + if m.loadItemsTask.searchTerm = string.EMPTY + m.voiceBox.hintText = tr("Press OK to type") + onVoiceEnabledChange() + return + else + ' User cleared an old search + m.voiceBox.hintText = tr("Press OK to type") + end if + else + m.voiceBox.hintText = m.top.searchTerm + onVoiceEnabledChange() + end if + setSearchBackground(SearchButtonState.INACTIVE) processDropdownState(m.top.searchTerm) @@ -1246,6 +1288,7 @@ sub onvoiceFilter() unfocusAllButtons() if isStringEqual(m.voiceBox.text.trim(), "reset search") then m.voiceBox.text = string.EMPTY + if isStringEqual(m.voiceBox.text.trim(), "clear search") then m.voiceBox.text = string.EMPTY m.voiceBox.hintText = m.voiceBox.text @@ -1476,7 +1519,10 @@ function onKeyEvent(key as string, press as boolean) as boolean if isStringEqual(key, KeyCode.OK) if m.voiceBox.isinFocusChain() - if m.searchButtonDisabled then return false + if m.searchButtonDisabled + m.global.sceneManager.callFunc("standardDialog", "Search Unavailable", { data: [`

${m.disabledReasaon}

`] }) + return false + end if buttonData = [ tr("Search") @@ -1785,21 +1831,22 @@ end sub sub setSearchBackground(state) if state = SearchButtonState.ACTIVE + if m.searchButtonDisabled + m.voiceBox.textColor = ColorPalette.MIDGREY + m.voiceBox.hintTextColor = ColorPalette.MIDGREY + m.voiceBoxBackground.blendColor = ColorPalette.DARKGREY + return + end if + m.voiceBox.textColor = ColorPalette.WHITE m.voiceBox.hintTextColor = ColorPalette.WHITE m.voiceBoxBackground.blendColor = ColorPalette.HIGHLIGHT - - searchPoster = m.voiceBox.getChild(5) - if isChainValid(searchPoster, "bitmap") - searchPoster.blendColor = ColorPalette.WHITE - end if - return end if if state = SearchButtonState.DISABLED - m.voiceBox.textColor = ColorPalette.MIDGREY - m.voiceBox.hintTextColor = ColorPalette.MIDGREY + m.voiceBox.textColor = ColorPalette.DARKGREY + m.voiceBox.hintTextColor = ColorPalette.DARKGREY m.voiceBoxBackground.blendColor = ColorPalette.MIDGREY return end if @@ -1807,9 +1854,12 @@ sub setSearchBackground(state) m.voiceBox.textColor = ColorPalette.MIDGREY m.voiceBox.hintTextColor = ColorPalette.MIDGREY m.voiceBoxBackground.blendColor = ColorPalette.WHITE +end sub - searchPoster = m.voiceBox.getChild(5) - if isChainValid(searchPoster, "bitmap") - searchPoster.blendColor = ColorPalette.MIDGREY - end if +sub disableSearch(disabledReason as string) + m.disabledReasaon = disabledReason + m.searchButtonDisabled = true + m.voiceBox.voiceEnabled = false + m.voiceBox.hintText = "Search Unavailable" + setSearchBackground(SearchButtonState.DISABLED) end sub diff --git a/components/data/SceneManager.bs b/components/data/SceneManager.bs index 9abfba0c..aa93b05b 100644 --- a/components/data/SceneManager.bs +++ b/components/data/SceneManager.bs @@ -445,7 +445,8 @@ sub optionClosed() itemID: m.itemID, params: m.params, indexSelected: -1, - buttonSelected: "" + buttonSelected: "", + text: m.scene.dialog.text } m.top.dataReturned = true end sub From afbf2e6ead2fb6c4e46dc05a5963d07bc5db379d Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 19 Apr 2025 21:20:57 -0400 Subject: [PATCH 22/27] Update voice search in music library --- components/Libraries/MusicLibraryView.bs | 140 +++++++++++++++------ components/Libraries/MusicLibraryView.xml | 65 +++++----- components/Libraries/VisualLibraryScene.bs | 32 ++--- source/enums/SearchButtonState.bs | 5 + 4 files changed, 148 insertions(+), 94 deletions(-) create mode 100644 source/enums/SearchButtonState.bs diff --git a/components/Libraries/MusicLibraryView.bs b/components/Libraries/MusicLibraryView.bs index 1be05ad2..abc875f2 100644 --- a/components/Libraries/MusicLibraryView.bs +++ b/components/Libraries/MusicLibraryView.bs @@ -3,6 +3,7 @@ import "pkg:/source/api/Image.bs" import "pkg:/source/enums/ColorPalette.bs" import "pkg:/source/enums/ItemType.bs" import "pkg:/source/enums/KeyCode.bs" +import "pkg:/source/enums/SearchButtonState.bs" import "pkg:/source/enums/String.bs" import "pkg:/source/enums/TaskControl.bs" import "pkg:/source/utils/config.bs" @@ -13,6 +14,7 @@ sub setupNodes() m.options = m.top.findNode("options") m.itemGrid = m.top.findNode("itemGrid") m.voiceBox = m.top.findNode("voiceBox") + m.voiceBoxBackground = m.top.findNode("voiceBoxBackground") m.backdrop = m.top.findNode("backdrop") m.newBackdrop = m.top.findNode("backdropTransition") m.emptyText = m.top.findNode("emptyText") @@ -28,12 +30,6 @@ sub setupNodes() m.genreList = m.top.findNode("genrelist") m.dropdownOptions = m.top.findNode("dropdownOptions") - m.searchButton = m.top.findNode("searchButton") - m.searchButton.textColor = ColorPalette.DARKGREY - m.searchButton.focusTextColor = ColorPalette.WHITE - m.searchButton.background = ColorPalette.WHITE - m.searchButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) - m.sortButton = m.top.findNode("sortButton") m.sortButton.textColor = ColorPalette.DARKGREY m.sortButton.focusTextColor = ColorPalette.WHITE @@ -57,6 +53,8 @@ sub setupNodes() m.viewButton.focusTextColor = ColorPalette.WHITE m.viewButton.background = ColorPalette.WHITE m.viewButton.focusBackground = chainLookupReturn(m.global.session, "user.settings.colorCursor", ColorPalette.HIGHLIGHT) + + setSearchBackground(SearchButtonState.INACTIVE) end sub sub init() @@ -89,11 +87,7 @@ sub init() m.itemGrid.observeField("itemSelected", "onItemSelected") 'Voice filter setup - m.voiceBox.voiceEnabled = true - m.voiceBox.active = true m.voiceBox.observeField("text", "onvoiceFilter") - 'set voice help text - m.voiceBox.hintText = tr("Use voice remote to search") 'backdrop m.newBackdrop.observeField("loadStatus", "newBGLoaded") @@ -113,6 +107,53 @@ sub init() 'set inital counts for overhang before content is loaded. m.loadItemsTask.totalRecordCount = 0 + + if not m.global.device.hasVoiceRemote + m.voiceBox.backgroundUri = "pkg:/images/transparent.png" + m.voiceBox.translation = "[15, 0]" + m.voiceBox.width = m.voiceBox.width - 15 + else + searchPoster = m.voiceBox.getChild(0) + if isChainValid(searchPoster, "bitmap") + searchPoster.bitmap = "" + searchPoster.blendColor = ColorPalette.WHITE + end if + + searchText = m.voiceBox.getChild(1) + if isChainValid(searchText, "height") + searchText.vertAlign = "bottom" + searchText.height = 37 + end if + + m.micIcon = m.voiceBox.getChild(6) + if isValid(m.micIcon) + m.micIcon.observeFieldScoped("blendColor", "onMicIconBlendColorChange") + end if + end if +end sub + +' VoiceTextEditBox.voiceEnabled doesn't have alwaysNotify enabled +' To trigger a change event, we must change the value to something else, then back to the value we want +sub onVoiceEnabledChange() + m.voiceBox.voiceEnabled = false + m.voiceBox.voiceEnabled = true +end sub + +sub onMicIconBlendColorChange() + ' DARKGREY: 269488383 + ' WHITE: -1 + ' LIGHTGREY: -1431655681 + ' HIGHLIGHT: -9934849 + + if m.voiceBoxBackground.blendColor = -9934849 + if m.micIcon.blendColor <> -1 + m.micIcon.blendColor = ColorPalette.WHITE + end if + else + if m.micIcon.blendColor <> 269488383 + m.micIcon.blendColor = ColorPalette.DARKGREY + end if + end if end sub sub onSortChange() @@ -591,10 +632,10 @@ sub ItemDataLoaded(msg) m.emptyText.text = tr("NO_ITEMS").Replace("%1", m.top.parentItem.Type) m.emptyText.visible = true - m.searchButton.focus = true - m.searchButton.setfocus(true) + m.voiceBox.setfocus(true) + setSearchBackground(SearchButtonState.ACTIVE) group = m.global.sceneManager.callFunc("getActiveScene") - group.lastFocus = m.searchButton + group.lastFocus = m.voiceBox end if m.loading = false @@ -638,10 +679,10 @@ sub ItemDataLoaded(msg) m.emptyText.text = tr("NO_ITEMS").Replace("%1", m.top.parentItem.Type) m.emptyText.visible = true - m.searchButton.focus = true - m.searchButton.setfocus(true) + m.voiceBox.setfocus(true) + setSearchBackground(SearchButtonState.ACTIVE) group = m.global.sceneManager.callFunc("getActiveScene") - group.lastFocus = m.searchButton + group.lastFocus = m.voiceBox end if stopLoadingSpinner() @@ -910,8 +951,13 @@ sub resetDropdownsToDefaultState() end sub sub onSearchTermChanged() - m.searchButton.text = m.top.searchTerm - m.searchButton.focus = false + m.voiceBox.hintText = m.top.searchTerm = string.EMPTY ? tr("Press OK to type") : m.top.searchTerm + + onVoiceEnabledChange() + + if isStringEqual(m.loadItemsTask.searchTerm, m.top.searchTerm) then return + + setSearchBackground(SearchButtonState.INACTIVE) processDropdownState(m.top.searchTerm) @@ -957,7 +1003,7 @@ sub alphaSelectedChanged() end sub sub unfocusAllButtons() - m.searchButton.focus = false + setSearchBackground(SearchButtonState.INACTIVE) m.sortButton.focus = false m.sortOrderButton.focus = false m.filterButton.focus = false @@ -970,6 +1016,9 @@ sub onvoiceFilter() unfocusAllButtons() if isStringEqual(m.voiceBox.text.trim(), "reset search") then m.voiceBox.text = string.EMPTY + if isStringEqual(m.voiceBox.text.trim(), "clear search") then m.voiceBox.text = string.EMPTY + + m.voiceBox.hintText = m.voiceBox.text ' If user searched for a letter, selected it from the alpha menu if m.voiceBox.text.len() = 1 @@ -993,12 +1042,6 @@ sub onvoiceFilter() return end if - if m.searchButton.disabled - m.itemGrid.setFocus(m.itemGrid.opacity = 1) - m.genreList.setFocus(m.genreList.opacity = 1) - return - end if - m.top.searchTerm = m.voiceBox.text.trim() end sub @@ -1007,12 +1050,12 @@ end sub function getOverhangingButton() if m.itemGrid.isinFocusChain() if m.itemGrid.numColumns = 4 - buttonList = [m.searchButton, m.sortButton, m.filterButton, m.viewButton] + buttonList = [m.voiceBox, m.sortButton, m.filterButton, m.viewButton] return buttonList[m.itemGrid.itemFocused] end if if m.itemGrid.numColumns = 6 - if m.itemGrid.itemFocused <= 0 then return m.searchButton + if m.itemGrid.itemFocused <= 0 then return m.voiceBox if m.itemGrid.itemFocused <= 2 then return m.sortButton if m.itemGrid.itemFocused <= 3 then return m.sortOrderButton if m.itemGrid.itemFocused <= 4 then return m.filterButton @@ -1021,14 +1064,14 @@ function getOverhangingButton() end if if m.genreList.isinFocusChain() - if m.genreList.itemFocused <= 0 then return m.searchButton + if m.genreList.itemFocused <= 0 then return m.voiceBox if m.genreList.itemFocused <= 2 then return m.sortButton if m.genreList.itemFocused <= 3 then return m.sortOrderButton if m.genreList.itemFocused <= 4 then return m.filterButton return m.viewButton end if - return m.searchButton + return m.voiceBox end function function onKeyEvent(key as string, press as boolean) as boolean @@ -1038,6 +1081,7 @@ function onKeyEvent(key as string, press as boolean) as boolean m.itemGrid.setFocus(m.itemGrid.opacity = 1) m.genreList.setFocus(m.genreList.opacity = 1) m.voiceBox.setFocus(false) + setSearchBackground(SearchButtonState.INACTIVE) group = m.global.sceneManager.callFunc("getActiveScene") group.lastFocus = m.itemGrid.opacity = 1 ? m.itemGrid : m.genreList end if @@ -1045,7 +1089,11 @@ function onKeyEvent(key as string, press as boolean) as boolean if isStringEqual(key, KeyCode.UP) if m.itemGrid.isinFocusChain() or m.genreList.isinFocusChain() overhangButton = getOverhangingButton() - overhangButton.focus = true + if overhangButton.isSubType("VoiceTextEditBox") + setSearchBackground(SearchButtonState.ACTIVE) + else + overhangButton.focus = true + end if overhangButton.setFocus(true) group = m.global.sceneManager.callFunc("getActiveScene") group.lastFocus = overhangButton @@ -1054,8 +1102,7 @@ function onKeyEvent(key as string, press as boolean) as boolean end if if isStringEqual(key, KeyCode.OK) - if m.searchButton.isinFocusChain() - if m.searchButton.disabled then return false + if m.voiceBox.isinFocusChain() buttonData = [ tr("Search") ] @@ -1120,8 +1167,8 @@ function onKeyEvent(key as string, press as boolean) as boolean if m.dropdownOptions.isinFocusChain() if isStringEqual(key, KeyCode.LEFT) - if m.searchButton.isinFocusChain() - m.searchButton.focus = false + if m.voiceBox.isinFocusChain() + setSearchBackground(SearchButtonState.INACTIVE) m.sortButton.focus = false m.sortOrderButton.focus = false m.viewButton.focus = false @@ -1135,11 +1182,11 @@ function onKeyEvent(key as string, press as boolean) as boolean end if if m.sortButton.isinFocusChain() - m.searchButton.focus = true - m.searchButton.setFocus(true) + m.voiceBox.setFocus(true) + setSearchBackground(SearchButtonState.ACTIVE) m.sortButton.focus = false group = m.global.sceneManager.callFunc("getActiveScene") - group.lastFocus = m.searchButton + group.lastFocus = m.voiceBox return true end if @@ -1172,8 +1219,8 @@ function onKeyEvent(key as string, press as boolean) as boolean end if if isStringEqual(key, KeyCode.Right) - if m.searchButton.isinFocusChain() - m.searchButton.focus = false + if m.voiceBox.isinFocusChain() + setSearchBackground(SearchButtonState.INACTIVE) m.sortButton.focus = true m.sortButton.setFocus(true) group = m.global.sceneManager.callFunc("getActiveScene") @@ -1212,7 +1259,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if isStringEqual(key, KeyCode.DOWN) if m.loadedItems = 0 then return false - m.searchButton.focus = false + setSearchBackground(SearchButtonState.INACTIVE) m.sortButton.focus = false m.sortOrderButton.focus = false m.viewButton.focus = false @@ -1314,3 +1361,16 @@ sub reclaimResources() m.genreData = CreateObject("roSGNode", "ContentNode") m.genreList.content = m.genreData end sub + +sub setSearchBackground(state) + if state = SearchButtonState.ACTIVE + m.voiceBox.textColor = ColorPalette.WHITE + m.voiceBox.hintTextColor = ColorPalette.WHITE + m.voiceBoxBackground.blendColor = ColorPalette.HIGHLIGHT + return + end if + + m.voiceBox.textColor = ColorPalette.MIDGREY + m.voiceBox.hintTextColor = ColorPalette.MIDGREY + m.voiceBoxBackground.blendColor = ColorPalette.WHITE +end sub diff --git a/components/Libraries/MusicLibraryView.xml b/components/Libraries/MusicLibraryView.xml index 2a0b82fb..89c730df 100644 --- a/components/Libraries/MusicLibraryView.xml +++ b/components/Libraries/MusicLibraryView.xml @@ -1,25 +1,48 @@ - - + + + + - + + + @@ -81,26 +104,6 @@ - - - - diff --git a/components/Libraries/VisualLibraryScene.bs b/components/Libraries/VisualLibraryScene.bs index 3a5ecd19..b3324926 100644 --- a/components/Libraries/VisualLibraryScene.bs +++ b/components/Libraries/VisualLibraryScene.bs @@ -4,18 +4,13 @@ import "pkg:/source/enums/CollectionType.bs" import "pkg:/source/enums/ColorPalette.bs" import "pkg:/source/enums/ItemType.bs" import "pkg:/source/enums/KeyCode.bs" +import "pkg:/source/enums/SearchButtonState.bs" import "pkg:/source/enums/String.bs" import "pkg:/source/enums/TaskControl.bs" import "pkg:/source/utils/config.bs" import "pkg:/source/utils/deviceCapabilities.bs" import "pkg:/source/utils/misc.bs" -enum SearchButtonState - ACTIVE - INACTIVE - DISABLED -end enum - sub setupNodes() m.options = m.top.findNode("options") m.itemGrid = m.top.findNode("itemGrid") @@ -70,7 +65,7 @@ sub setupNodes() end sub sub init() - m.disabledReasaon = string.EMPTY + m.disabledReason = string.EMPTY m.searchButtonDisabled = false m.top.optionsAvailable = false setupNodes() @@ -1214,20 +1209,11 @@ sub resetDropdownsToDefaultState() end sub sub onSearchTermChanged() - if m.top.searchTerm = string.EMPTY - ' User clicked on search, then pressed back without searching - if m.loadItemsTask.searchTerm = string.EMPTY - m.voiceBox.hintText = tr("Press OK to type") - onVoiceEnabledChange() - return - else - ' User cleared an old search - m.voiceBox.hintText = tr("Press OK to type") - end if - else - m.voiceBox.hintText = m.top.searchTerm - onVoiceEnabledChange() - end if + m.voiceBox.hintText = m.top.searchTerm = string.EMPTY ? tr("Press OK to type") : m.top.searchTerm + + onVoiceEnabledChange() + + if isStringEqual(m.loadItemsTask.searchTerm, m.top.searchTerm) then return setSearchBackground(SearchButtonState.INACTIVE) @@ -1520,7 +1506,7 @@ function onKeyEvent(key as string, press as boolean) as boolean if isStringEqual(key, KeyCode.OK) if m.voiceBox.isinFocusChain() if m.searchButtonDisabled - m.global.sceneManager.callFunc("standardDialog", "Search Unavailable", { data: [`

${m.disabledReasaon}

`] }) + m.global.sceneManager.callFunc("standardDialog", "Search Unavailable", { data: [`

${m.disabledReason}

`] }) return false end if @@ -1857,7 +1843,7 @@ sub setSearchBackground(state) end sub sub disableSearch(disabledReason as string) - m.disabledReasaon = disabledReason + m.disabledReason = disabledReason m.searchButtonDisabled = true m.voiceBox.voiceEnabled = false m.voiceBox.hintText = "Search Unavailable" diff --git a/source/enums/SearchButtonState.bs b/source/enums/SearchButtonState.bs new file mode 100644 index 00000000..2cb520f2 --- /dev/null +++ b/source/enums/SearchButtonState.bs @@ -0,0 +1,5 @@ +enum SearchButtonState + ACTIVE + INACTIVE + DISABLED +end enum From 6891ac5fbc35ee09de5746d877cc0144baa0d17f Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 25 Apr 2025 19:40:05 -0400 Subject: [PATCH 23/27] Cleanup search in "other" libraries --- components/ItemGrid/ItemGrid.bs | 15 ++++++++++----- components/ItemGrid/ItemGrid.xml | 2 +- components/liveTv/LiveTVLibraryView.bs | 3 ++- components/liveTv/LiveTVLibraryView.xml | 2 +- source/utils/misc.bs | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/components/ItemGrid/ItemGrid.bs b/components/ItemGrid/ItemGrid.bs index ef03bc7a..23336f87 100644 --- a/components/ItemGrid/ItemGrid.bs +++ b/components/ItemGrid/ItemGrid.bs @@ -189,7 +189,7 @@ sub loadInitialItems() m.loadItemsTask.nameStartsWith = string.EMPTY m.top.alphaSelected = string.EMPTY else - m.loadItemsTask.nameStartsWith = m.alpha.letterSelected + m.loadItemsTask.nameStartsWith = m.top.alphaSelected end if m.loadItemsTask.searchTerm = m.voiceBox.text @@ -744,16 +744,15 @@ end sub sub onvoiceFilter() if m.VoiceBox.text = string.EMPTY then return - if LCase(m.voiceBox.text) = "reset search" then m.voiceBox.text = string.EMPTY + if isStringEqual(m.voiceBox.text, "reset search") then m.voiceBox.text = string.EMPTY + if isStringEqual(m.voiceBox.text, "clear search") then m.voiceBox.text = string.EMPTY m.loadedRows = 0 m.loadedItems = 0 m.data = CreateObject("roSGNode", "ContentNode") m.itemGrid.content = m.data m.top.alphaSelected = string.EMPTY - m.loadItemsTask.NameStartsWith = " " - m.loadItemsTask.searchTerm = m.voiceBox.text - m.loadItemsTask.recursive = true + m.loadItemsTask.NameStartsWith = string.EMPTY ' If user searched for a letter, selected it from the alpha menu if m.voiceBox.text.len() = 1 @@ -772,7 +771,13 @@ sub onvoiceFilter() end if end for end if + + m.top.alphaSelected = m.voiceBox.text + return end if + + m.loadItemsTask.searchTerm = m.voiceBox.text + loadInitialItems() end sub diff --git a/components/ItemGrid/ItemGrid.xml b/components/ItemGrid/ItemGrid.xml index e66e0aad..a34f1961 100644 --- a/components/ItemGrid/ItemGrid.xml +++ b/components/ItemGrid/ItemGrid.xml @@ -1,7 +1,7 @@ - + diff --git a/components/liveTv/LiveTVLibraryView.bs b/components/liveTv/LiveTVLibraryView.bs index 30466fe8..09018966 100644 --- a/components/liveTv/LiveTVLibraryView.bs +++ b/components/liveTv/LiveTVLibraryView.bs @@ -345,7 +345,8 @@ end sub sub onvoiceFilter() if m.VoiceBox.text = string.EMPTY then return - if LCase(m.voiceBox.text) = "reset search" then m.voiceBox.text = string.EMPTY + if isStringEqual(m.voiceBox.text, "reset search") then m.voiceBox.text = string.EMPTY + if isStringEqual(m.voiceBox.text, "clear search") then m.voiceBox.text = string.EMPTY m.loadedRows = 0 m.loadedItems = 0 diff --git a/components/liveTv/LiveTVLibraryView.xml b/components/liveTv/LiveTVLibraryView.xml index 495d2101..4af9c11a 100644 --- a/components/liveTv/LiveTVLibraryView.xml +++ b/components/liveTv/LiveTVLibraryView.xml @@ -1,7 +1,7 @@ - + Date: Fri, 25 Apr 2025 19:47:27 -0400 Subject: [PATCH 24/27] Hide voicebox if device has no voice remote --- components/ItemGrid/ItemGrid.bs | 13 ++++++++----- source/static/whatsNew/3.0.3.json | 4 ++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/components/ItemGrid/ItemGrid.bs b/components/ItemGrid/ItemGrid.bs index 23336f87..aaf88062 100644 --- a/components/ItemGrid/ItemGrid.bs +++ b/components/ItemGrid/ItemGrid.bs @@ -45,11 +45,14 @@ sub init() 'Voice filter setup m.voiceBox = m.top.findNode("voiceBox") - m.voiceBox.voiceEnabled = true - m.voiceBox.active = true - m.voiceBox.observeField("text", "onvoiceFilter") - 'set voice help text - m.voiceBox.hintText = tr("Use voice remote to search") + if m.global.device.hasVoiceRemote + m.voiceBox.voiceEnabled = true + m.voiceBox.active = true + m.voiceBox.observeField("text", "onvoiceFilter") + else + m.voiceBox.visible = false + m.voiceBox.backgroundUri = "pkg:/images/transparent.png" + end if 'backdrop m.newBackdrop.observeField("loadStatus", "newBGLoaded") diff --git a/source/static/whatsNew/3.0.3.json b/source/static/whatsNew/3.0.3.json index d79265b1..df7b6e30 100644 --- a/source/static/whatsNew/3.0.3.json +++ b/source/static/whatsNew/3.0.3.json @@ -26,5 +26,9 @@ { "description": "Improve fix for skip outro can result in video stuck in buffering state", "author": "1hitsong" + }, + { + "description": " Improve voice search across all libraries that support searching", + "author": "1hitsong" } ] From f67dad8674b0bc2cbd8d1962005693865604db66 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 2 May 2025 19:56:49 -0400 Subject: [PATCH 25/27] Fix bugs from Roku crash logs 2 for home items 1 for played checkmark --- components/PlayedCheckmark.bs | 3 ++- components/home/HomeItem.bs | 2 +- components/home/HomeRows.bs | 8 ++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/components/PlayedCheckmark.bs b/components/PlayedCheckmark.bs index ef82c14b..0ddf07c3 100644 --- a/components/PlayedCheckmark.bs +++ b/components/PlayedCheckmark.bs @@ -26,7 +26,8 @@ sub onDataChange() if not isValidAndNotEmpty(m.top.data) then return if chainLookupReturn(m.top.data, "unplayedCount", 0) > 0 - if m.global.session.user.settings["ui.tvshows.disableUnwatchedEpisodeCount"] then return + disableUnwatchedEpisodeCount = m.global.session.user.settings["ui.tvshows.disableUnwatchedEpisodeCount"] ?? false + if disableUnwatchedEpisodeCount then return unplayedCount = m.top.data.unplayedCount.ToStr() unplayedCountLength = len(unplayedCount) diff --git a/components/home/HomeItem.bs b/components/home/HomeItem.bs index c20c8675..18d087fb 100644 --- a/components/home/HomeItem.bs +++ b/components/home/HomeItem.bs @@ -65,7 +65,7 @@ sub itemContentChanged() unplayedCount: chainLookupReturn(itemData, "json.UserData.UnplayedItemCount", 0) } - itemData.Title = itemData.name + itemData.AddReplace("Title", itemData.LookupCI("name")) initItemText() initItemTextExtra() diff --git a/components/home/HomeRows.bs b/components/home/HomeRows.bs index edf51b67..9bde6ba6 100644 --- a/components/home/HomeRows.bs +++ b/components/home/HomeRows.bs @@ -839,7 +839,9 @@ function onKeyEvent(key as string, press as boolean) as boolean m.overhang.setfocus(true) group = m.global.sceneManager.callFunc("getActiveScene") - group.lastFocus = m.overhang + if isValid(group) + group.lastFocus = m.overhang + end if return true end if @@ -848,7 +850,9 @@ function onKeyEvent(key as string, press as boolean) as boolean end if group = m.global.sceneManager.callFunc("getActiveScene") - group.lastFocus = m.top + if isValid(group) + group.lastFocus = m.top + end if return false end function From 2e4bd98f0e11a9f19e25295fb4126c288e037ca9 Mon Sep 17 00:00:00 2001 From: David Briglio <3886546+DavidBriglio@users.noreply.github.com> Date: Sat, 3 May 2025 11:29:11 -0400 Subject: [PATCH 26/27] Fix malformed XML --- locale/en_US/translations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts index 5451c916..edacaf53 100644 --- a/locale/en_US/translations.ts +++ b/locale/en_US/translations.ts @@ -1014,7 +1014,7 @@
Autoplay Episode Limit - Autoplay Episode Limit + Autoplay Episode Limit The number of episodes that will play automatically after the selected episode. Requires 'Play next episode automatically' server setting to be enabled. From d63171ebfe2b24e05d4d2fb3cabdbbb3b3026be3 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 3 May 2025 14:59:33 -0400 Subject: [PATCH 27/27] Release Prep --- manifest | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest b/manifest index a631df60..ea7548ee 100644 --- a/manifest +++ b/manifest @@ -7,9 +7,9 @@ build_version=3 ### Main Menu Icons / Channel Poster Artwork -mm_icon_focus_fhd=pkg:/images/channel-poster_fhd_dev.png -mm_icon_focus_hd=pkg:/images/channel-poster_hd_dev.png -mm_icon_focus_sd=pkg:/images/channel-poster_sd_dev.png +mm_icon_focus_fhd=pkg:/images/channel-poster_fhd.png +mm_icon_focus_hd=pkg:/images/channel-poster_hd.png +mm_icon_focus_sd=pkg:/images/channel-poster_sd.png ### Splash Screen + Loading Screen Artwork