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