diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml
index 90f01e7bd..b6eb244a7 100644
--- a/.github/workflows/build-dev.yml
+++ b/.github/workflows/build-dev.yml
@@ -12,7 +12,7 @@ jobs:
dev:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
+ - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3
with:
node-version: "lts/*"
diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml
index 69f6387d8..12cfe1ffb 100644
--- a/.github/workflows/build-prod.yml
+++ b/.github/workflows/build-prod.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout master (the latest release)
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
+ uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
with:
ref: master
- name: Install jq to parse json
@@ -33,7 +33,7 @@ jobs:
- name: Save old Makefile version
run: awk 'BEGIN { FS=" = " } /^VERSION/ { print "oldMakeVersion="$2; }' Makefile >> $GITHUB_ENV
- name: Checkout PR branch
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
+ uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
- name: Save new package.json version
run: echo "newPackVersion=$(jq -r ".version" package.json)" >> $GITHUB_ENV
- name: package.json version must be updated
@@ -61,7 +61,7 @@ jobs:
prod:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
+ - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3
with:
node-version: "lts/*"
diff --git a/.github/workflows/roku-analysis.yml b/.github/workflows/roku-analysis.yml
index 0965214fc..73122243a 100644
--- a/.github/workflows/roku-analysis.yml
+++ b/.github/workflows/roku-analysis.yml
@@ -11,7 +11,7 @@ jobs:
static:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
+ - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3
with:
node-version: "lts/*"
diff --git a/components/ItemGrid/GridItem.brs b/components/ItemGrid/GridItem.brs
index ea386b3b5..ff624e7d5 100644
--- a/components/ItemGrid/GridItem.brs
+++ b/components/ItemGrid/GridItem.brs
@@ -53,6 +53,9 @@ sub itemContentChanged()
if itemData.json.UserData.UnplayedItemCount > 0
m.unplayedCount.visible = true
m.unplayedEpisodeCount.text = itemData.json.UserData.UnplayedItemCount
+ else
+ m.unplayedCount.visible = false
+ m.unplayedEpisodeCount.text = ""
end if
end if
end if
diff --git a/components/data/SceneManager.brs b/components/data/SceneManager.brs
index e876957f5..51191bb8d 100755
--- a/components/data/SceneManager.brs
+++ b/components/data/SceneManager.brs
@@ -77,16 +77,17 @@ end sub
sub popScene()
group = m.groups.pop()
if group <> invalid
- if group.isSubType("JFGroup")
+ groupType = group.subtype()
+ if groupType = "JFGroup"
unregisterOverhangData(group)
- else if group.isSubType("JFVideo")
+ else if groupType = "JFVideo"
' Stop video to make sure app communicates stop playstate to server
group.control = "stop"
end if
group.visible = false
- if group.isSubType("JFScreen")
+ if groupType = "JFScreen"
group.callFunc("OnScreenHidden")
end if
else
diff --git a/components/data/UserData.brs b/components/data/UserData.brs
index 564848a6f..446abbf38 100644
--- a/components/data/UserData.brs
+++ b/components/data/UserData.brs
@@ -20,9 +20,6 @@ sub loadFromRegistry(id as string)
end sub
sub saveToRegistry()
- set_user_setting("username", m.top.username)
- set_user_setting("token", m.top.token)
-
users = parseJson(get_setting("available_users", "[]"))
this_user = invalid
for each user in users
@@ -57,7 +54,9 @@ function setPreference(key as string, value as string)
end function
sub setActive()
- set_setting("active_user", m.top.id)
+ if m.global.session.user.settings["global.rememberme"]
+ set_setting("active_user", m.top.id)
+ end if
end sub
sub setServer(hostname as string)
diff --git a/components/movies/MovieDetails.brs b/components/movies/MovieDetails.brs
index 4f72b4783..913ccd796 100644
--- a/components/movies/MovieDetails.brs
+++ b/components/movies/MovieDetails.brs
@@ -38,11 +38,13 @@ end sub
sub trailerAvailableChanged()
if m.top.trailerAvailable
' add trailor button to button group
- trailerButton = CreateObject("roSGNode", "JFButton")
+ trailerButton = CreateObject("roSGNode", "Button")
trailerButton.id = "trailer-button"
trailerButton.text = tr("Play Trailer")
+ trailerButton.iconUri = ""
+ trailerButton.focusedIconUri = ""
trailerButton.maxWidth = "300"
- trailerButton.minWidth = "300"
+ trailerButton.minWidth = "280"
m.buttonGrp.appendChild(trailerButton)
else
' remove trailor button from button group
diff --git a/components/settings/settings.brs b/components/settings/settings.brs
index 5809353f4..d4354e065 100644
--- a/components/settings/settings.brs
+++ b/components/settings/settings.brs
@@ -1,10 +1,10 @@
import "pkg:/source/utils/config.brs"
import "pkg:/source/utils/misc.brs"
import "pkg:/source/roku_modules/log/LogMixin.brs"
+import "pkg:/source/api/sdk.bs"
sub init()
m.log = log.Logger("Settings")
- m.top.overhangTitle = tr("Settings")
m.top.optionsAvailable = false
m.userLocation = []
@@ -12,7 +12,6 @@ sub init()
m.settingsMenu = m.top.findNode("settingsMenu")
m.settingDetail = m.top.findNode("settingDetail")
m.settingDesc = m.top.findNode("settingDesc")
- m.settingTitle = m.top.findNode("settingTitle")
m.path = m.top.findNode("path")
m.boolSetting = m.top.findNode("boolSetting")
@@ -72,7 +71,7 @@ sub LoadMenu(configSection)
end if
' Set Path display
- m.path.text = ""
+ m.path.text = tr("Settings")
for each level in m.userLocation
if level.title <> invalid then m.path.text += " / " + tr(level.title)
end for
@@ -82,7 +81,7 @@ sub settingFocused()
selectedSetting = m.userLocation.peek().children[m.settingsMenu.itemFocused]
m.settingDesc.text = tr(selectedSetting.Description)
- m.settingTitle.text = tr(selectedSetting.Title)
+ m.top.overhangTitle = tr(selectedSetting.Title)
' Hide Settings
m.boolSetting.visible = false
@@ -160,14 +159,40 @@ end sub
sub boolSettingChanged()
-
if m.boolSetting.focusedChild = invalid then return
selectedSetting = m.userLocation.peek().children[m.settingsMenu.itemFocused]
if m.boolSetting.checkedItem
- set_user_setting(selectedSetting.settingName, "true")
+ session.user.settings.Save(selectedSetting.settingName, "true")
+ if Left(selectedSetting.settingName, 7) = "global."
+ ' global user setting
+ ' save to main registry block
+ set_setting(selectedSetting.settingName, "true")
+ ' setting specific triggers
+ if selectedSetting.settingName = "global.rememberme"
+ print "m.global.session.user.id=", m.global.session.user.id
+ set_setting("active_user", m.global.session.user.id)
+ end if
+ else
+ ' regular user setting
+ ' save to user specific registry block
+ set_user_setting(selectedSetting.settingName, "true")
+ end if
else
- set_user_setting(selectedSetting.settingName, "false")
+ session.user.settings.Save(selectedSetting.settingName, "false")
+ if Left(selectedSetting.settingName, 7) = "global."
+ ' global user setting
+ ' save to main registry block
+ set_setting(selectedSetting.settingName, "false")
+ ' setting specific triggers
+ if selectedSetting.settingName = "global.rememberme"
+ unset_setting("active_user")
+ end if
+ else
+ ' regular user setting
+ ' save to user specific registry block
+ set_user_setting(selectedSetting.settingName, "false")
+ end if
end if
end sub
diff --git a/components/settings/settings.xml b/components/settings/settings.xml
index 707bda79d..f35a6aa02 100644
--- a/components/settings/settings.xml
+++ b/components/settings/settings.xml
@@ -7,32 +7,35 @@
+ itemSpacing="[0,9]"
+ textVertAlign="center" />
-
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/video/VideoPlayerView.brs b/components/video/VideoPlayerView.brs
index d3c7bd6cd..b4f479484 100644
--- a/components/video/VideoPlayerView.brs
+++ b/components/video/VideoPlayerView.brs
@@ -23,6 +23,9 @@ sub init()
m.top.observeField("content", "onContentChange")
m.top.observeField("selectedSubtitle", "onSubtitleChange")
+ ' Custom Caption Function
+ m.top.observeField("allowCaptions", "onAllowCaptionsChange")
+
m.playbackTimer.observeField("fire", "ReportPlayback")
m.bufferPercentage = 0 ' Track whether content is being loaded
m.playReported = false
@@ -51,6 +54,51 @@ sub init()
m.top.trickPlayBar.filledBarBlendColor = m.global.constants.colors.blue
end sub
+' Only setup captain items if captions are allowed
+sub onAllowCaptionsChange()
+ if not m.top.allowCaptions then return
+
+ m.captionGroup = m.top.findNode("captionGroup")
+ m.captionGroup.createchildren(9, "LayoutGroup")
+ m.captionTask = createObject("roSGNode", "captionTask")
+ m.captionTask.observeField("currentCaption", "updateCaption")
+ m.captionTask.observeField("useThis", "checkCaptionMode")
+ m.top.observeField("subtitleTrack", "loadCaption")
+ m.top.observeField("globalCaptionMode", "toggleCaption")
+
+ if m.global.session.user.settings["playback.subs.custom"]
+ m.top.suppressCaptions = true
+ toggleCaption()
+ else
+ m.top.suppressCaptions = false
+ end if
+end sub
+
+' Set caption url to server subtitle track
+sub loadCaption()
+ if m.top.suppressCaptions
+ m.captionTask.url = m.top.subtitleTrack
+ end if
+end sub
+
+' Toggles visibility of custom subtitles and sets captionTask's player state
+sub toggleCaption()
+ m.captionTask.playerState = m.top.state + m.top.globalCaptionMode
+ if LCase(m.top.globalCaptionMode) = "on"
+ m.captionTask.playerState = m.top.state + m.top.globalCaptionMode + "w"
+ m.captionGroup.visible = true
+ else
+ m.captionGroup.visible = false
+ end if
+end sub
+
+' Removes old subtitle lines and adds new subtitle lines
+sub updateCaption()
+ m.captionGroup.removeChildrenIndex(m.captionGroup.getChildCount(), 0)
+ m.captionGroup.appendChildren(m.captionTask.currentCaption)
+end sub
+
+' Event handler for when selectedSubtitle changes
sub onSubtitleChange()
' Save the current video position
m.global.queueManager.callFunc("setTopStartingPoint", int(m.top.position) * 10000000&)
@@ -114,7 +162,11 @@ sub onVideoContentLoaded()
m.top.transcodeParams = videoContent[0].transcodeparams
if m.LoadMetaDataTask.isIntro
+ ' Disable trackplay bar for intro videos
m.top.enableTrickPlay = false
+ else
+ ' Allow custom captions for non intro videos
+ m.top.allowCaptions = true
end if
if isValid(m.top.audioIndex)
@@ -132,21 +184,12 @@ sub onContentChange()
if not isValid(m.top.content) then return
m.top.observeField("position", "onPositionChanged")
-
- ' If video content type is not episode, remove position observer
- if m.top.content.contenttype <> 4
- m.top.unobserveField("position")
- end if
end sub
sub onNextEpisodeDataLoaded()
m.checkedForNextEpisode = true
m.top.observeField("position", "onPositionChanged")
-
- if m.getNextEpisodeTask.nextEpisodeData.Items.count() <> 2
- m.top.unobserveField("position")
- end if
end sub
'
@@ -189,16 +232,27 @@ end sub
' When Video Player state changes
sub onPositionChanged()
+ if isValid(m.captionTask)
+ m.captionTask.currentPos = Int(m.top.position * 1000)
+ end if
+
' Check if dialog is open
m.dialog = m.top.getScene().findNode("dialogBackground")
if not isValid(m.dialog)
- checkTimeToDisplayNextEpisode()
+ ' Do not show Next Episode button for intro videos
+ if not m.LoadMetaDataTask.isIntro
+ checkTimeToDisplayNextEpisode()
+ end if
end if
end sub
'
' When Video Player state changes
sub onState(msg)
+ if isValid(m.captionTask)
+ m.captionTask.playerState = m.top.state + m.top.globalCaptionMode
+ end if
+
' When buffering, start timer to monitor buffering process
if m.top.state = "buffering" and m.bufferCheckTimer <> invalid
@@ -319,11 +373,17 @@ function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
if key = "down"
- m.top.selectSubtitlePressed = true
- return true
+ ' Do not show subtitle selection for intro videos
+ if not m.LoadMetaDataTask.isIntro
+ m.top.selectSubtitlePressed = true
+ return true
+ end if
else if key = "up"
- m.top.selectPlaybackInfoPressed = true
- return true
+ ' Do not show playback info for intro videos
+ if not m.LoadMetaDataTask.isIntro
+ m.top.selectPlaybackInfoPressed = true
+ return true
+ end if
else if key = "OK"
' OK will play/pause depending on current state
' return false to allow selection during seeking
diff --git a/components/video/VideoPlayerView.xml b/components/video/VideoPlayerView.xml
index df5230ba9..609cfc11a 100644
--- a/components/video/VideoPlayerView.xml
+++ b/components/video/VideoPlayerView.xml
@@ -7,7 +7,6 @@
-
@@ -24,9 +23,11 @@
+
+
diff --git a/locale/de_DE/translations.ts b/locale/de_DE/translations.ts
index ad7ce59f6..04df13cfd 100644
--- a/locale/de_DE/translations.ts
+++ b/locale/de_DE/translations.ts
@@ -11439,5 +11439,100 @@
Aktivieren oder Deaktivieren von Direct Play für optionale CodecsSettings Menu - Title for settings group related to codec support
+
+
+ Fehler während der Wiedergabe
+ Dialog title when error occurs during playback
+
+
+
+ Abmelden
+
+
+
+ Fehler beim Laden dieses Eintrags vom Server.
+ Dialog detail when unable to load Content from Server
+
+
+
+ Lade Kanaldaten
+
+
+ Name or Title field of media item
+
+ Name
+
+
+
+ Fehler beim Laden der Kanaldaten
+
+
+
+ Jetzt läuft
+
+
+
+ Bei der Wiedergabe dieses Eintrags ist ein Fehler aufgetreten.
+ Dialog detail when error occurs during playback
+
+
+
+ Kanaldaten konnten nicht vom Server geladen werden
+
+
+
+ Gespeicherte löschen
+
+
+ Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)
+
+ %1 enthält keine Einträge
+
+
+
+ Anmeldedaten speichern?
+
+
+
+ Server wechseln
+
+
+
+ Kritikerbewertung
+
+
+
+ IMDb-Bewertung
+
+
+
+ Abspieldatum
+
+
+
+ Hinzugefügt am
+
+
+
+ Fehler beim Empfangen des Inhalts
+ Dialog title when unable to load Content from Server
+
+
+
+ Abmelden
+
+
+
+ Jetzt läuft
+
+
+
+ Server wechseln
+
+
+
+ Fehler beim Empfangen des Inhalts
+ Dialog title when unable to load Content from Server
+
diff --git a/locale/en_US/translations.ts b/locale/en_US/translations.ts
index e4a3d6b0b..2ea904966 100644
--- a/locale/en_US/translations.ts
+++ b/locale/en_US/translations.ts
@@ -1208,5 +1208,25 @@
Disable the HEVC codec on this device. This may improve playback for some devices (ultra).User Setting - Setting description
+
+
+ Global
+ User Setting - Setting title
+
+
+
+ Global settings that affect everyone that uses this Roku device.
+ User Setting - Setting description
+
+
+
+ Remember Me?
+ User Setting - Setting title
+
+
+
+ Remember the currently logged in user and try to log them in again next time you start the Jellyfin app.
+ User Setting - Setting description
+
\ No newline at end of file
diff --git a/locale/es_ES/translations.ts b/locale/es_ES/translations.ts
index be1c7d257..47ed8b0d5 100644
--- a/locale/es_ES/translations.ts
+++ b/locale/es_ES/translations.ts
@@ -4418,5 +4418,1200 @@
Cargando datos de canales
+
+
+ El contenido solicitado no existe en el servidor
+ Content of message box when the requested content is not found on the server
+
+
+
+ Un error ha ocurrido reproduciendo este elemento. El servidor no entrego los datos de transcodificación requeridos
+ Content of message box when trying to play an item which requires transcoding, and the server did not provide transcode url
+
+
+ Title of Tab for options to filter library content
+
+ Filtro
+
+
+
+ Programas de TV
+
+
+
+ Calificación parental
+
+
+
+ hoy
+ Current day
+
+
+ Name or Title field of media item
+
+ Nombre
+
+
+
+ Habilitado
+
+
+
+ Ha ocurrido un error reproduciendo este elemento.
+ Dialog detail when error occurs during playback
+
+
+ Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)
+
+ Este %1 no contiene elementos
+
+
+
+ Jueves
+ Day of Week
+
+
+
+ Empezó a las
+ (Past Tense) For defining time when a program started today (e.g. Started at 08:00)
+
+
+
+ En vivo
+ If TV Show is being broadcast live (not pre-recorded)
+
+
+
+ Repetir
+ If TV Shows has previously been broadcasted
+
+
+
+ Desconocido
+ Title for a cast member for which we have no information for
+
+
+
+ Viernes
+ Day of Week
+
+
+
+ Cerrar
+
+
+
+ Martes
+ Day of Week
+
+
+
+ Sábado
+ Day of Week
+
+
+
+ Fecha reproducido
+
+
+
+ ayer
+ Previous day
+
+
+
+ mañana
+ Next day
+
+
+
+ Domingo
+ Day of Week
+
+
+
+ Empieza
+ (Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00)
+
+
+
+ Terminó a las
+ (Past Tense) For defining time when a program will ended (e.g. Ended at 08:00)
+
+
+
+ Canales
+ Menu option for showing Live TV Channel List
+
+
+
+ Guía TV
+ Menu option for showing Live TV Guide / Schedule
+
+
+
+ Cancelar grabación
+
+
+
+ Calificación IMDb
+
+
+
+ Fecha añadido
+
+
+
+ Edad
+
+
+
+ Más como este
+
+
+
+ Desenfocar Episodios sin ver
+ Option Title in user setting screen
+
+
+
+ Ocultar reloj
+ Option Title in user setting screen
+
+
+
+ Siguiente episodio
+
+
+
+ Grabar serie
+
+
+
+ Cancelar grabación de serie
+
+
+
+ Contador de elementos
+ UI -> Media Grid -> Item Count in user setting screen.
+
+
+
+ Mostrar contador de elementos en la librería y el indice del elemento seleccionado
+ Description for option in Setting Screen
+
+
+
+ Ir a series
+ Continue Watching Popup Menu - Navigate to the Series Detail Page
+
+
+
+ Ir a temporada
+ Continue Watching Popup Menu - Navigate to the Season Page
+
+
+
+ Ir a episodio
+ Continue Watching Popup Menu - Navigate to the Episode Detail Page
+
+
+
+ Buscar ahora
+ Help text in search Box
+
+
+
+ %1 de %2
+ Item position and count. %1 = current item. %2 = total number of items
+
+
+
+ (Dialogo se cerrará automáticamente)
+
+
+
+ Ir directamente a lista de episodios si la serie de TV tiene solo una temporada.
+ Settings Menu - Description for option
+
+
+
+ Desenfocar imágenes de episodios sin ver.
+
+
+
+ Terminó
+ (Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00)
+
+
+
+ Ver canal
+
+
+
+ Grabar
+
+
+
+ Versión
+
+
+
+ Interfaz de usuario
+ Title for User Interface section in user setting screen.
+
+
+
+ Reproducir adelanto
+
+
+
+ Soporte de codificador
+ Settings Menu - Title for settings group related to codec support
+
+
+
+ MPEG-4
+ Name of codec used in settings menu
+
+
+
+ AV1
+ Name of a setting - should we try to direct play experimental av1 codec
+
+
+
+ Volver arriba
+ UI -> Media Grid -> Item Title in user setting screen.
+
+
+
+ Modo cine
+ Settings Menu - Title for option
+
+
+
+ H.264
+ Name of codec used in settings menu
+
+
+
+ HEVC
+ Name of codec used in settings menu
+
+
+
+ Este es tu código de conexión rápida:
+
+
+
+ Hubo un error autenticando por medio de Conexión rápida.
+
+
+
+ Redes
+
+
+
+ Estudios
+
+
+
+ Puedes buscar por Títulos, Personas, Canales en vivo y más
+ Help text in search results
+
+
+
+ Si no hay servidores en la lista anterior, también puedes ingresar la URL manualmente
+ Instructions on initial app launch when the user is asked to manually enter a server URL
+
+
+
+ Deshabilitado
+
+
+
+ Transmitiendo Ahora
+
+
+
+ Error cargando datos del canal
+
+
+
+ Contador de reproducciones
+
+
+
+ Fecha de estreno
+
+
+ Title of Tab for options to sort library content
+
+ Ordenar
+
+
+
+ Películas
+
+
+
+ Imposible cargar datos del servidor
+
+
+
+ Lunes
+ Day of Week
+
+
+
+ Miércoles
+ Day of Week
+
+
+
+ Empezó
+ (Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00)
+
+
+
+ Empieza a las
+ (Future Tense) For defining time when a program will start today (e.g. Starts at 08:00)
+
+
+
+ Conectando al servidor
+ Message to display to user while client is attempting to connect to the server
+
+
+
+ No encontrado
+ Title of message box when the requested content is not found on the server
+
+
+
+ Ingresa nombre o dirección IP del servidor
+ Title of KeyboardDialog when manually entering a server URL
+
+
+
+ Selecciona un servidor Jellyfin disponible en tu red local:
+ Instructions on initial app launch when the user is asked to pick a server from a list
+
+
+
+ Habilita o deshabilita reproducción directa para codificadores opcionales
+ Settings Menu - Title for settings group related to codec support
+
+
+
+ MPEG-2
+ Name of codec used in settings menu
+
+
+
+ Conexión rápida
+
+
+
+ Ha ocurrido un error reproduciendo este elemento.
+ Dialog detail when error occurs during playback
+
+
+ Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)
+
+ Este %1 no contiene elementos
+
+
+
+ Puntuación de los Críticos
+
+
+
+ Calificación Parental
+
+
+
+ Duración
+
+
+
+ Lunes
+ Day of Week
+
+
+
+ Jueves
+ Day of Week
+
+
+
+ Empieza a las
+ (Future Tense) For defining time when a program will start today (e.g. Starts at 08:00)
+
+
+
+ Terminó a las
+ (Past Tense) For defining time when a program will ended (e.g. Ended at 08:00)
+
+
+
+ Repetir
+ If TV Shows has previously been broadcasted
+
+
+
+ Conectando al Servidor
+ Message to display to user while client is attempting to connect to the server
+
+
+
+ El contenido solicitado no existe en el servidor
+ Content of message box when the requested content is not found on the server
+
+
+
+ Selecciona un servidor Jellyfin disponible en tu red local:
+ Instructions on initial app launch when the user is asked to pick a server from a list
+
+
+
+ Un error ha ocurrido reproduciendo este elemento. El servidor no entregó los datos de transcodificación requeridos.
+ Content of message box when trying to play an item which requires transcoding, and the server did not provide transcode url
+
+
+ Title of Tab for options to filter library content
+
+ Filtrar
+
+
+
+ Programas de TV
+
+
+
+ hoy
+ Current day
+
+
+ Name or Title field of media item
+
+ Nombre
+
+
+
+ Miércoles
+ Day of Week
+
+
+
+ Nacido en
+
+
+
+ Partes Adicionales
+ Additional parts of a video
+
+
+
+ Películas
+
+
+
+ Empezó a las
+ (Past Tense) For defining time when a program started today (e.g. Started at 08:00)
+
+
+
+ Empezó
+ (Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00)
+
+
+
+ En vivo
+ If TV Show is being broadcast live (not pre-recorded)
+
+
+
+ Desconocido
+ Title for a cast member for which we have no information for
+
+
+
+ MPEG-2
+ Name of codec used in settings menu
+
+
+
+ Viernes
+ Day of Week
+
+
+
+ Cerrar
+
+
+
+ Martes
+ Day of Week
+
+
+
+ Sábado
+ Day of Week
+
+
+
+ Reproducido en Fecha
+
+
+
+ Murió en
+
+
+
+ Presiona 'OK' para Cerrar
+
+
+
+ Características Especiales
+
+
+
+ ayer
+ Previous day
+
+
+
+ mañana
+ Next day
+
+
+
+ Domingo
+ Day of Week
+
+
+
+ Empieza
+ (Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00)
+
+
+
+ Canales
+ Menu option for showing Live TV Channel List
+
+
+
+ Guía de TV
+ Menu option for showing Live TV Guide / Schedule
+
+
+
+ Cancelar Grabación
+
+
+ Title of Tab for switching "views" when looking at a library
+
+ Ver
+
+
+
+ Calificación IMDb
+
+
+
+ Añadido en Fecha
+
+
+
+ Edad
+
+
+
+ Elenco y Equipo
+
+
+
+ Más como este
+
+
+
+ ¿Guardar Credenciales?
+
+
+
+ Películas (Cuadrícula)
+ Movie library view option
+
+
+
+ Grabar Serie
+
+
+
+ Cancelar Grabación de Serie
+
+
+
+ Terminó
+ (Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00)
+
+
+
+ Ver Canal
+
+
+
+ Grabar
+
+
+
+ Versión
+
+
+
+ Reproducción
+ Title for Playback section in user setting screen.
+
+
+
+ Soporte de Codificador
+ Settings Menu - Title for settings group related to codec support
+
+
+
+ MPEG-4
+ Name of codec used in settings menu
+
+
+
+ Si no hay servidores en la lista anterior, también puedes ingresar la URL manualmente:
+ Instructions on initial app launch when the user is asked to manually enter a server URL
+
+
+
+ Cambiar de Servidor
+
+
+
+ Transmitiendo Ahora
+
+
+
+ Error Al Recuperar Contenido
+ Dialog title when unable to load Content from Server
+
+
+
+ Error Al Reproducir
+ Dialog title when error occurs during playback
+
+
+
+ Ha ocurrido un error al obtener este elemento desde el servidor.
+ Dialog detail when unable to load Content from Server
+
+
+
+ Cerrar Sesión
+
+
+
+ Borrar Guardados
+
+
+
+ Cargando Datos del Canal
+
+
+
+ Error al Cargar Datos del Canal
+
+
+
+ Veces Reproducido
+
+
+
+ Fecha de Estreno
+
+
+ Title of Tab for options to sort library content
+
+ Ordenar
+
+
+
+ Imposible cargar Datos del Canal desde el servidor
+
+
+
+ Películas (Modo Presentación)
+ Movie library view option
+
+
+
+ No Encontrado
+ Title of message box when the requested content is not found on the server
+
+
+
+ Ingresa nombre o dirección IP del servidor
+ Title of KeyboardDialog when manually entering a server URL
+
+
+
+ Error al Obtener la Información de Reproducción
+ Dialog Title: Received error from server when trying to get information about the selected item for playback
+
+
+
+ Habilita o deshabilita la Reproducción Directa para codificadores opcionales
+ Settings Menu - Title for settings group related to codec support
+
+
+
+ Cuadrícula de multimedia
+ UI -> Media Grid section in user setting screen.
+
+
+
+ Mostrar cantidad de elementos en la biblioteca y el índice del elemento seleccionado.
+ Description for option in Setting Screen
+
+
+
+ Ocultar Eslogan
+ Option Title in user setting screen
+
+
+
+ Desenfocar imágenes de episodios sin ver.
+
+
+
+ Subtitulos Personalizados
+ Name of a setting - custom subtitles that support CJK fonts
+
+
+
+ Habilitado
+
+
+
+ Códec
+
+
+
+ Conexión Rápida
+
+
+
+ Elementos de Diseño
+
+
+
+ Desenfocar Episodios No Vistos
+ Option Title in user setting screen
+
+
+
+ Saltar Detalles para Temporadas Únicas
+ Settings Menu - Title for option
+
+
+
+ Ocultar Reloj
+ Option Title in user setting screen
+
+
+
+ Siguiente episodio
+
+
+
+ Cantidad de Elementos
+ UI -> Media Grid -> Item Count in user setting screen.
+
+
+
+ Agregar a Favoritos
+ Button Text - When pressed, sets item as Favorite
+
+
+
+ Marcar como Visto
+ Button Text - When pressed, marks item as Warched
+
+
+
+ Ir a series
+ Continue Watching Popup Menu - Navigate to the Series Detail Page
+
+
+
+ Ir a temporada
+ Continue Watching Popup Menu - Navigate to the Season Page
+
+
+
+ Ir a episodio
+ Continue Watching Popup Menu - Navigate to the Episode Detail Page
+
+
+
+ Buscar ahora
+ Help text in search Box
+
+
+
+ %1 de %2
+ Item position and count. %1 = current item. %2 = total number of items
+
+
+
+ (El Diálogo se cerrará automáticamente)
+
+
+
+ Ocultar texto de eslogan en páginas de detalles.
+
+
+
+ Opciones para Series de TV.
+ Description for TV Shows user settings.
+
+
+
+ Ir directamente a lista de episodios si la serie de TV tiene solo una temporada.
+ Settings Menu - Description for option
+
+
+
+ Códec de Audio
+
+
+
+ Tipo de rango de video
+
+
+
+ Códec de Video
+
+
+
+ Información de Reproducción
+
+
+
+ Tasa de Bits Total
+
+
+
+ Tamaño
+ Video size
+
+
+
+ Tasa de Bits
+ Video streaming bit rate
+
+
+
+ Formato de pixeles
+ Video pixel format
+
+
+
+ No se pudieron encontrar álbumes o canciones relacionadas a este artista
+ Popup message when we find no audio data for an artist
+
+
+
+ WxH
+ Video width x height
+
+
+
+ Subtítulos de Texto Únicamente
+ Name of a setting - should we hide subtitles that might transcode
+
+
+
+ Interfaz de usuario
+ Title for User Interface section in user setting screen.
+
+
+
+ Opciones de la Cuadrícula de Multimedia.
+
+
+
+ Reproducir Trailer
+
+
+
+ directo
+
+
+
+ Publicado
+ Aired date label
+
+
+
+ AV1
+ Name of a setting - should we try to direct play experimental av1 codec
+
+
+
+ Volver Arriba
+ UI -> Media Grid -> Item Title in user setting screen.
+
+
+
+ Opciones que alteran el diseño de Jellyfin.
+ Description for Design Elements user settings.
+
+
+
+ Usar Pantalla de Bienvenida como Fondo de Inicio
+ Option Title in user setting screen
+
+
+
+ Modo Cine
+ Settings Menu - Title for option
+
+
+
+ H.264
+ Name of codec used in settings menu
+
+
+
+ HEVC
+ Name of codec used in settings menu
+
+
+
+ Configuración relacionada a como se ve la aplicación.
+
+
+
+ Razón
+
+
+
+ Información de Transcodificación
+
+
+
+ Tag del Códec
+
+
+
+ Nivel
+ Video profile level
+
+
+
+ Canales de Audio
+
+
+
+ Información de la Transmisión
+
+
+
+ Contenedor
+ Video streaming container
+
+
+
+ Solo mostrar subtítulos de texto para minimizar la transcodificación.
+ Description of a setting - should we hide subtitles that might transcode
+
+
+
+ todo
+ all will reset the searchTerm so all data will be availible
+
+
+
+ Este es tu código de Conexión Rápida:
+
+
+
+ Hubo un error autenticando por medio de Conexión Rápida.
+
+
+
+ Redes
+
+
+
+ Estudios
+
+
+
+ Series
+
+
+
+ Puedes buscar por Títulos, Personas, Canales en vivo y más
+ Help text in search results
+
+
+
+ Deshabilitado
+
+
+
+ **EXPERIMENTAL** Soporte de Reproducción Directa para contenido AV1 si este dispositivo Roku es compatible.
+ Description of a setting - should we try to direct play experimental av1 codec
+
+
+
+ Utilizar la búsqueda por voz
+ Help text in search voice text box
+
+
+
+ Soporte de reproducción directa para contenido MPEG-2 (ej., televisión en vivo). Esto previene la transcodificación de contenido MPEG-2, pero a mayor uso de ancho de banda.
+ Settings Menu - Description for option
+
+
+
+ Soporte de reproducción directa para contenido MPEG-4. Esto podría requerir ser deshabilitado para poder reproducir los archivos de video con encodificación DIVX.
+ Settings Menu - Description for option
+
+
+
+ Use el botón de reproducción para animar lentamente al primer elemento de la carpeta. (Si está deshabilitado, la carpeta se restablecerá al primer elemento inmediatamente).
+ Description for option in Setting Screen
+
+
+
+ Lunes
+ Day of Week
+
+
+
+ Martes
+ Day of Week
+
+
+
+ Jueves
+ Day of Week
+
+
+
+ Domingo
+ Day of Week
+
+
+
+ Presiona 'OK' para Cerrar
+
+
+
+ hoy
+ Current day
+
+
+
+ ayer
+ Previous day
+
+
+
+ Viernes
+ Day of Week
+
+
+
+ Guía de TV
+ Menu option for showing Live TV Guide / Schedule
+
+
+
+ Miércoles
+ Day of Week
+
+
+
+ Desconocido
+ Title for a cast member for which we have no information for
+
+
+
+ Sábado
+ Day of Week
+
+
+
+ mañana
+ Next day
+
+
+
+ Canales
+ Menu option for showing Live TV Channel List
+
+
+
+ Puntuación IMDb
+
+
+
+ Grabar
+
+
+
+ Error Obteniendo Contenido
+ Dialog title when unable to load Content from Server
+
+
+
+ Se produjo un error durante la reproducción de este elemento.
+ Dialog detail when error occurs during playback
+
+
+
+ ¿Guardar Credenciales?
+
+
+
+ Cambiar Servidor
+
+
+
+ Ahora
+
+
+
+ Error Durante la Reproducción
+ Dialog title when error occurs during playback
+
+
+
+ Se produjo un error al recuperar información para este elemento desde el servidor.
+ Dialog detail when unable to load Content from Server
+
+
+
+ Cerrar Sesión
+
+
+
+ Eliminar Guardado
+
+
+
+ Cargando información del canal
+
diff --git a/locale/fr/translations.ts b/locale/fr/translations.ts
index 793d0b36b..243e10f51 100644
--- a/locale/fr/translations.ts
+++ b/locale/fr/translations.ts
@@ -26,7 +26,7 @@
- Favoris
+ Favori
@@ -50,7 +50,7 @@
- Se connecter
+ Connectez-vous s'il vous plaît
@@ -118,7 +118,7 @@
- Entrer le mot de passe
+ Entrez le mot de passe
@@ -142,7 +142,7 @@
- Trie par ordre
+ Ordre de tri
@@ -166,7 +166,7 @@
- Direction
+ Réalisateur
@@ -174,7 +174,7 @@
- Musiques
+ Audio
@@ -7426,5 +7426,455 @@
Nombre d'élémentsUI -> Media Grid -> Item Count in user setting screen.
+
+
+ Erreur lors de la récupération du contenu
+ Dialog title when unable to load Content from Server
+
+
+
+ Une erreur s'est produite lors de la récupération des données de cet élément sur le serveur.
+ Dialog detail when unable to load Content from Server
+
+
+
+ Erreur lors du chargement des données de la chaîne
+
+
+
+ Impossible de charger les données de la chaîne depuis le serveur
+
+
+
+ Appuyez sur «OK» pour fermer
+
+
+
+ Lundi
+ Day of Week
+
+
+
+ Commence à
+ (Future Tense) For defining time when a program will start today (e.g. Starts at 08:00)
+
+
+
+ Annuler l'enregistrement
+
+
+
+ Connexion au serveur en cours
+ Message to display to user while client is attempting to connect to the server
+
+
+
+ Entrez le nom du serveur ou son adresse IP
+ Title of KeyboardDialog when manually entering a server URL
+
+
+
+ Sélectionnez un serveur Jellyfin disponible sur votre réseau local :
+ Instructions on initial app launch when the user is asked to pick a server from a list
+
+
+
+ Une erreur a été rencontrée lors de la lecture de cet élément. Le serveur n'a pas communiqué les informations nécessaires au transcodage.
+ Content of message box when trying to play an item which requires transcoding, and the server did not provide transcode url
+
+
+
+ Activer ou désactiver DirectPlay pour les codecs optionnels
+ Settings Menu - Title for settings group related to codec support
+
+
+
+ Utiliser DirectPlay pour les contenus en MPEG-2 (par ex. TV en direct). Cela évitera la conversion des contenus en MPEG-2 mais utilisera beaucoup plus de bande passante.
+ Settings Menu - Description for option
+
+
+
+ Utiliser DirectPlay pour les contenus en MPEG-4. Désactiver ce paramètres pourrait être nécessaire pour la lecture de fichiers vidéo encodés en DivX.
+ Settings Menu - Description for option
+
+
+
+ ** EXPERIMENTAL** Utiliser DirectPlay pour les contenus en AV1 si cet équipement Roku le supporte.
+ Description of a setting - should we try to direct play experimental av1 codec
+
+
+
+ HEVC
+ Name of codec used in settings menu
+
+
+
+ H.264
+ Name of codec used in settings menu
+
+
+
+ Nombre de vues
+
+
+
+ Samedi
+ Day of Week
+
+
+
+ Enregistrer la série
+
+
+
+ Si aucun serveur n'est dans la liste ci-dessus, vous pouvez aussi saisir l'URL du serveur manuellement :
+ Instructions on initial app launch when the user is asked to manually enter a server URL
+
+
+
+ Désactivé
+
+
+
+ Fermer
+
+
+
+ Chaines
+ Menu option for showing Live TV Channel List
+
+
+
+ Une erreur s'est produite lors de la lecture de cet élément.
+ Dialog detail when error occurs during playback
+
+
+
+ Erreur lors de la lecture
+ Dialog title when error occurs during playback
+
+
+
+ Enregistrer les identifiants ?
+
+
+
+ Suppression enregistrée
+
+
+
+ En ce moment
+
+
+
+ Séries TV
+
+
+
+ Vendredi
+ Day of Week
+
+
+
+ Distribution
+
+
+ Title of Tab for options to filter library content
+
+ Filtre
+
+
+
+ hier
+ Previous day
+
+
+
+ Contenu additionnel
+ Additional parts of a video
+
+
+
+ Films
+
+
+
+ Fini
+ (Past Tense) For defining a day and time when a program ended (e.g. Ended Wednesday, 08:00)
+
+
+
+ Voir la chaîne
+
+
+
+ Décès
+
+
+
+ Guide des programmes
+ Menu option for showing Live TV Guide / Schedule
+
+
+
+ Films (Grille)
+ Movie library view option
+
+
+
+ Codecs supportés
+ Settings Menu - Title for settings group related to codec support
+
+
+
+ Interface utilisateur
+ Title for User Interface section in user setting screen.
+
+
+
+ Marquer comme favori
+ Button Text - When pressed, sets item as Favorite
+
+
+
+ Aller à la série
+ Continue Watching Popup Menu - Navigate to the Series Detail Page
+
+
+
+ Notation des critiques
+
+
+
+ Classification parentale
+
+
+
+ Date de naissance
+
+
+ Title of Tab for options to sort library content
+
+ Trier
+
+
+
+ Films (Présentation)
+ Movie library view option
+
+
+
+ A commencé à
+ (Past Tense) For defining time when a program started today (e.g. Started at 08:00)
+
+
+
+ A commencé
+ (Past Tense) For defining a day and time when a program started (e.g. Started Wednesday, 08:00)
+
+
+
+ Rediffusion
+ If TV Shows has previously been broadcasted
+
+
+
+ Annuler l'enregistrement de la série
+
+
+
+ Grille de médias
+ UI -> Media Grid section in user setting screen.
+
+
+
+ Marquer comme vu
+ Button Text - When pressed, marks item as Warched
+
+
+
+ Caractéristiques spécifiques
+
+
+
+ Dimanche
+ Day of Week
+
+
+
+ Mardi
+ Day of Week
+
+
+
+ Mercredi
+ Day of Week
+
+
+
+ Jeudi
+ Day of Week
+
+
+
+ Commence
+ (Future Tense) For defining a day and time when a program will start (e.g. Starts Wednesday, 08:00)
+
+
+
+ Enregistrer
+
+
+
+ Introuvable
+ Title of message box when the requested content is not found on the server
+
+
+
+ Le contenu demandé n'existe pas sur le serveur
+ Content of message box when the requested content is not found on the server
+
+
+
+ Erreur lors de la récupération des informations de lecture
+ Dialog Title: Received error from server when trying to get information about the selected item for playback
+
+
+
+ Aller à la saison
+ Continue Watching Popup Menu - Navigate to the Season Page
+
+
+
+ Chercher
+ Help text in search Box
+
+
+
+ Studios
+
+
+
+ MPEG-2
+ Name of codec used in settings menu
+
+
+
+ MPEG-4
+ Name of codec used in settings menu
+
+
+
+ AV1
+ Name of a setting - should we try to direct play experimental av1 codec
+
+
+
+ Réseaux
+
+
+
+ Version
+
+
+
+ Utilisez la télécommande vocale pour chercher
+ Help text in search voice text box
+
+
+
+ Chargement des données de la chaîne
+
+
+ Name or Title field of media item
+
+ Titre
+
+
+
+ Notation IMDb
+
+
+
+ Date d'ajout
+
+
+
+ Date de lecture
+
+
+
+ Durée
+
+
+ Title of Tab for switching "views" when looking at a library
+
+ Vue
+
+
+
+ Contenu similaire
+
+
+
+ aujourd'hui
+ Current day
+
+
+
+ A fini à
+ (Past Tense) For defining time when a program will ended (e.g. Ended at 08:00)
+
+
+
+ En direct
+ If TV Show is being broadcast live (not pre-recorded)
+
+
+
+ Date de sortie
+
+
+
+ Inconnu
+ Title for a cast member for which we have no information for
+
+
+
+ Lecture
+ Title for Playback section in user setting screen.
+
+
+
+ Activé
+
+
+
+ Age
+
+
+
+ demain
+ Next day
+
+
+ Message displayed in Item Grid when no item to display. %1 is container type (e.g. Boxset, Collection, Folder, etc)
+
+ Ce(tte) %1 ne contient pas d'éléments
+
+
+
+ Nombre d'éléments
+ UI -> Media Grid -> Item Count in user setting screen.
+
+
+
+ Aller à l'épisode
+ Continue Watching Popup Menu - Navigate to the Episode Detail Page
+
+
+
+ Séries
+
diff --git a/locale/hu/translations.ts b/locale/hu/translations.ts
index 47529ceaa..c2ad7fc3e 100644
--- a/locale/hu/translations.ts
+++ b/locale/hu/translations.ts
@@ -11268,5 +11268,10 @@
Közvetlen lejátszás
+
+ Name or Title field of media item
+
+ Név
+
diff --git a/package-lock.json b/package-lock.json
index 3f0ef5340..1463d15c8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,25 +12,121 @@
"dependencies": {
"@rokucommunity/bslib": "0.1.1",
"bgv": "npm:button-group-vert@1.0.2",
- "brighterscript-formatter": "1.6.32",
+ "brighterscript-formatter": "1.6.33",
"intKeyboard": "npm:integer-keyboard@1.0.12",
"log": "npm:roku-log@0.11.1",
"sob": "npm:slide-out-button@1.0.1"
},
"devDependencies": {
"@rokucommunity/bslint": "0.8.10",
- "brighterscript": "0.65.5",
+ "brighterscript": "0.65.7",
"jshint": "2.13.6",
- "markdownlint-cli2": "0.9.2",
- "rimraf": "5.0.1",
+ "markdownlint-cli2": "0.10.0",
+ "rimraf": "5.0.5",
"roku-deploy": "3.10.3",
"roku-log-bsc-plugin": "0.8.1",
"rooibos-roku": "5.7.0",
- "ropm": "0.10.16",
+ "ropm": "0.10.17",
"spellchecker-cli": "6.1.1",
"undent": "0.1.0"
}
},
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -533,9 +629,9 @@
}
},
"node_modules/brighterscript": {
- "version": "0.65.5",
- "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.5.tgz",
- "integrity": "sha512-xDkWIZhjTLhp6dVZ6lX7zWRyNvjdiAwZncJRnErSbqRhteNJFL7ic2UDJew9zCOYTQDrG7B85lpPpXc/1JlV+Q==",
+ "version": "0.65.7",
+ "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.7.tgz",
+ "integrity": "sha512-cmV0JzlYuKIKkMsbHR2FyaD2YugHeHESdMmUOtK61EsEUS895Y6yXftDVnFUwij7beLiZR6s1oRpMfiS7MJ40g==",
"dependencies": {
"@rokucommunity/bslib": "^0.1.1",
"@xml-tools/parser": "^1.0.7",
@@ -575,11 +671,11 @@
}
},
"node_modules/brighterscript-formatter": {
- "version": "1.6.32",
- "resolved": "https://registry.npmjs.org/brighterscript-formatter/-/brighterscript-formatter-1.6.32.tgz",
- "integrity": "sha512-7rbNmSsj2v8iv+iSWXczSg4hC7L1zxMEwo+jZcDaMDPu0TBt4zmmCpaT0WvvvYcE8fD9I1tJ4dlTaj61QnH0QQ==",
+ "version": "1.6.33",
+ "resolved": "https://registry.npmjs.org/brighterscript-formatter/-/brighterscript-formatter-1.6.33.tgz",
+ "integrity": "sha512-XmirSAOBBHsprJvLOuh8nzXNiPQJXQa2NJ581OZdQrofFdBNPGH+yFhV6XXUZ+pA6fVLSxMxXA0toF83jtKjrA==",
"dependencies": {
- "brighterscript": "^0.65.5",
+ "brighterscript": "^0.65.7",
"glob-all": "^3.3.0",
"jsonc-parser": "^3.0.0",
"source-map": "^0.7.3",
@@ -590,16 +686,6 @@
"bsfmt": "dist/cli.js"
}
},
- "node_modules/brighterscript-formatter/node_modules/cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
"node_modules/brighterscript-formatter/node_modules/glob-all": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/glob-all/-/glob-all-3.3.1.tgz",
@@ -612,6 +698,21 @@
"glob-all": "bin/glob-all"
}
},
+ "node_modules/brighterscript-formatter/node_modules/glob-all/node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "node_modules/brighterscript-formatter/node_modules/glob-all/node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
+ },
"node_modules/brighterscript-formatter/node_modules/glob-all/node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
@@ -633,6 +734,18 @@
"node": ">=8"
}
},
+ "node_modules/brighterscript-formatter/node_modules/glob-all/node_modules/yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/brighterscript-formatter/node_modules/jsonc-parser": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
@@ -651,23 +764,6 @@
"node": ">=8"
}
},
- "node_modules/brighterscript-formatter/node_modules/y18n": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
- },
- "node_modules/brighterscript-formatter/node_modules/yargs-parser": {
- "version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "dependencies": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/brighterscript/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@@ -1264,6 +1360,12 @@
"domelementtype": "1"
}
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true
+ },
"node_modules/ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -1990,12 +2092,12 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"node_modules/jackspeak": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.0.tgz",
- "integrity": "sha512-DiEwVPqsieUzZBNxQ2cxznmFzfg/AMgJUjYw5xl6rSmCxAQXECcbSdwcLM6Ds6T09+SBfSNCGPhYUoQ96P4h7A==",
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.5.tgz",
+ "integrity": "sha512-Ratx+B8WeXLAtRJn26hrhY8S1+Jz6pxPMrkrdkgb/NstTNiqMhX0/oFVu5wX+g5n6JlEu2LPsDJmY8nRP4+alw==",
"dev": true,
"dependencies": {
- "cliui": "^7.0.4"
+ "@isaacs/cliui": "^8.0.2"
},
"engines": {
"node": ">=14"
@@ -2316,9 +2418,9 @@
}
},
"node_modules/markdownlint": {
- "version": "0.30.0",
- "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.30.0.tgz",
- "integrity": "sha512-nInuFvI/rEzanAOArW5490Ez4EYpB5ODqVM0mcDYCPx9DKJWCQqCgejjiCvbSeE7sjbDscVtZmwr665qpF5xGA==",
+ "version": "0.31.1",
+ "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.31.1.tgz",
+ "integrity": "sha512-CKMR2hgcIBrYlIUccDCOvi966PZ0kJExDrUi1R+oF9PvqQmCrTqjOsgIvf2403OmJ+CWomuzDoylr6KbuMyvHA==",
"dev": true,
"dependencies": {
"markdown-it": "13.0.1",
@@ -2329,17 +2431,17 @@
}
},
"node_modules/markdownlint-cli2": {
- "version": "0.9.2",
- "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.9.2.tgz",
- "integrity": "sha512-ndijEHIOikcs29W8068exHXlfkFviGFT/mPhREia7zSfQzHvTDkL2s+tWizvELjLHiKRO4KGTkkJyR3oeR8A5g==",
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.10.0.tgz",
+ "integrity": "sha512-kVxjPyKFC+eW7iqcxiNI50RDzwugpXkEX5eQlDso/0IUs9M73jXYguLFHDzgi5KatcxU/57Fu8KoGtkFft9lfA==",
"dev": true,
"dependencies": {
"globby": "13.2.2",
- "markdownlint": "0.30.0",
+ "markdownlint": "0.31.1",
"markdownlint-cli2-formatter-default": "0.0.4",
"micromatch": "4.0.5",
"strip-json-comments": "5.0.1",
- "yaml": "2.3.1"
+ "yaml": "2.3.2"
},
"bin": {
"markdownlint-cli2": "markdownlint-cli2.js",
@@ -3482,13 +3584,13 @@
}
},
"node_modules/path-scurry": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.7.0.tgz",
- "integrity": "sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==",
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
+ "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
"dev": true,
"dependencies": {
- "lru-cache": "^9.0.0",
- "minipass": "^5.0.0"
+ "lru-cache": "^9.1.1 || ^10.0.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -4219,15 +4321,15 @@
}
},
"node_modules/rimraf": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.1.tgz",
- "integrity": "sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==",
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
+ "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
"dev": true,
"dependencies": {
- "glob": "^10.2.5"
+ "glob": "^10.3.7"
},
"bin": {
- "rimraf": "dist/cjs/src/bin.js"
+ "rimraf": "dist/esm/bin.mjs"
},
"engines": {
"node": ">=14"
@@ -4246,19 +4348,19 @@
}
},
"node_modules/rimraf/node_modules/glob": {
- "version": "10.2.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.5.tgz",
- "integrity": "sha512-Gj+dFYPZ5hc5dazjXzB0iHg2jKWJZYMjITXYPBRQ/xc2Buw7H0BINknRTwURJ6IC6MEFpYbLvtgVb3qD+DwyuA==",
+ "version": "10.3.10",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+ "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
"dev": true,
"dependencies": {
"foreground-child": "^3.1.0",
- "jackspeak": "^2.0.3",
- "minimatch": "^9.0.0",
- "minipass": "^5.0.0 || ^6.0.2",
- "path-scurry": "^1.7.0"
+ "jackspeak": "^2.3.5",
+ "minimatch": "^9.0.1",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+ "path-scurry": "^1.10.1"
},
"bin": {
- "glob": "dist/cjs/src/bin.js"
+ "glob": "dist/esm/bin.mjs"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -4268,9 +4370,9 @@
}
},
"node_modules/rimraf/node_modules/minimatch": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.0.tgz",
- "integrity": "sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==",
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -4419,14 +4521,14 @@
"dev": true
},
"node_modules/ropm": {
- "version": "0.10.16",
- "resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.16.tgz",
- "integrity": "sha512-YZ49ie+dSRkyOz7RbXpku+neujhk6WfrFKUpda844W2kB0Xk/p4XtwirIdorVNtefBqEeQMXswwrVaWLNaL+vQ==",
+ "version": "0.10.17",
+ "resolved": "https://registry.npmjs.org/ropm/-/ropm-0.10.17.tgz",
+ "integrity": "sha512-JFq/PAzrC3xVweRdTt4zAYCWWdSBzMg5MP9i302L+vi4eXLp4jDYGeYKZwSucZswWNGxVpSK3gW1paHpv7GVEw==",
"dev": true,
"dependencies": {
"@xml-tools/ast": "^5.0.5",
"@xml-tools/parser": "1.0.10",
- "brighterscript": "^0.65.4",
+ "brighterscript": "^0.65.5",
"del": "6.0.0",
"fs-extra": "9.1.0",
"glob-all": "3.2.1",
@@ -4476,7 +4578,7 @@
"node": ">=10"
}
},
- "node_modules/ropm/node_modules/fs-extra/node_modules/jsonfile": {
+ "node_modules/ropm/node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
@@ -4488,7 +4590,7 @@
"graceful-fs": "^4.1.6"
}
},
- "node_modules/ropm/node_modules/fs-extra/node_modules/universalify": {
+ "node_modules/ropm/node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
@@ -4497,45 +4599,6 @@
"node": ">= 10.0.0"
}
},
- "node_modules/ropm/node_modules/roku-deploy": {
- "version": "3.10.3",
- "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.10.3.tgz",
- "integrity": "sha512-COJSQ638QklcM+8AN1nujFuzT04rTZLFuLSww35edm8w/y0l60oF/Iu7TQ46m75DwoGFzGFfomLEmA1ltQk9mA==",
- "dev": true,
- "dependencies": {
- "chalk": "^2.4.2",
- "dateformat": "^3.0.3",
- "dayjs": "^1.11.0",
- "fast-glob": "^3.2.12",
- "fs-extra": "^7.0.1",
- "is-glob": "^4.0.3",
- "jsonc-parser": "^2.3.0",
- "jszip": "^3.6.0",
- "micromatch": "^4.0.4",
- "moment": "^2.29.1",
- "parse-ms": "^2.1.0",
- "postman-request": "^2.88.1-postman.32",
- "temp-dir": "^2.0.0",
- "xml2js": "^0.5.0"
- },
- "bin": {
- "roku-deploy": "dist/cli.js"
- }
- },
- "node_modules/ropm/node_modules/roku-deploy/node_modules/fs-extra": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
- "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
- "dev": true,
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- },
- "engines": {
- "node": ">=6 <7 || >=8"
- }
- },
"node_modules/ropm/node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
@@ -4834,6 +4897,21 @@
"node": ">=8"
}
},
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/stringify-object": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
@@ -4859,6 +4937,19 @@
"node": ">=8"
}
},
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-bom": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
@@ -5452,6 +5543,24 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -5492,9 +5601,9 @@
"dev": true
},
"node_modules/yaml": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz",
- "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz",
+ "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==",
"dev": true,
"engines": {
"node": ">= 14"
diff --git a/package.json b/package.json
index 1412e72b6..781e37854 100644
--- a/package.json
+++ b/package.json
@@ -5,21 +5,21 @@
"dependencies": {
"@rokucommunity/bslib": "0.1.1",
"bgv": "npm:button-group-vert@1.0.2",
- "brighterscript-formatter": "1.6.32",
+ "brighterscript-formatter": "1.6.33",
"intKeyboard": "npm:integer-keyboard@1.0.12",
"log": "npm:roku-log@0.11.1",
"sob": "npm:slide-out-button@1.0.1"
},
"devDependencies": {
"@rokucommunity/bslint": "0.8.10",
- "brighterscript": "0.65.5",
+ "brighterscript": "0.65.7",
"jshint": "2.13.6",
- "markdownlint-cli2": "0.9.2",
- "rimraf": "5.0.1",
+ "markdownlint-cli2": "0.10.0",
+ "rimraf": "5.0.5",
"roku-deploy": "3.10.3",
"roku-log-bsc-plugin": "0.8.1",
"rooibos-roku": "5.7.0",
- "ropm": "0.10.16",
+ "ropm": "0.10.17",
"spellchecker-cli": "6.1.1",
"undent": "0.1.0"
},
diff --git a/settings/settings.json b/settings/settings.json
index f5240e70f..422174741 100644
--- a/settings/settings.json
+++ b/settings/settings.json
@@ -1,4 +1,18 @@
[
+ {
+ "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 the Jellyfin app.",
+ "settingName": "global.rememberme",
+ "type": "bool",
+ "default": "false"
+ }
+
+ ]
+ },
{
"title": "Playback",
"description": "Settings relating to playback and supported codec and media types.",
diff --git a/source/Main.brs b/source/Main.brs
index 298650bd2..8a86ac075 100644
--- a/source/Main.brs
+++ b/source/Main.brs
@@ -62,7 +62,8 @@ sub Main (args as dynamic) as void
end if
' Only show the Whats New popup the first time a user runs a new client version.
- if m.global.app.version <> get_setting("LastRunVersion")
+ appLastRunVersion = get_setting("LastRunVersion")
+ if m.global.app.version <> appLastRunVersion
' Ensure the user hasn't disabled Whats New popups
if m.global.session.user.settings["load.allowwhatsnew"] = true
set_setting("LastRunVersion", m.global.app.version)
@@ -72,6 +73,34 @@ sub Main (args as dynamic) as void
end if
end if
+ ' Registry migrations
+ if isValid(appLastRunVersion) and not versionChecker(appLastRunVersion, "1.7.0")
+ ' last app version used less than 1.7.0
+ ' no longer saving raw password to registry
+ ' auth token and username are now stored in user settings and not global settings
+ print "Running 1.7.0 registry migrations"
+ ' remove global settings
+ unset_setting("token")
+ unset_setting("username")
+ unset_setting("password")
+ ' remove user settings
+ unset_user_setting("password")
+ ' remove saved credentials from saved_servers
+ saved = get_setting("saved_servers")
+ if isValid(saved)
+ savedServers = ParseJson(saved)
+ if isValid(savedServers.serverList) and savedServers.serverList.Count() > 0
+ newServers = { serverList: [] }
+ for each item in savedServers.serverList
+ item.Delete("username")
+ item.Delete("password")
+ newServers.serverList.Push(item)
+ end for
+ set_setting("saved_servers", FormatJson(newServers))
+ end if
+ end if
+ end if
+
' Handle input messages
input = CreateObject("roInput")
input.SetMessagePort(m.port)
@@ -197,25 +226,16 @@ sub Main (args as dynamic) as void
selectedItem.selectedAudioStreamIndex = audio_stream_idx
- ' If we are playing a playlist, always start at the beginning
- if m.global.queueManager.callFunc("getCount") > 1
- selectedItem.startingPoint = 0
+ ' Display playback options dialog
+ if selectedItem.json.userdata.PlaybackPositionTicks > 0
+ m.global.queueManager.callFunc("hold", selectedItem)
+ playbackOptionDialog(selectedItem.json.userdata.PlaybackPositionTicks, selectedItem.json)
+ else
m.global.queueManager.callFunc("clear")
m.global.queueManager.callFunc("push", selectedItem)
m.global.queueManager.callFunc("playQueue")
- else
- ' Display playback options dialog
- if selectedItem.json.userdata.PlaybackPositionTicks > 0
- m.global.queueManager.callFunc("hold", selectedItem)
- playbackOptionDialog(selectedItem.json.userdata.PlaybackPositionTicks, selectedItem.json)
- else
- m.global.queueManager.callFunc("clear")
- m.global.queueManager.callFunc("push", selectedItem)
- m.global.queueManager.callFunc("playQueue")
- end if
end if
-
else if selectedItemType = "Series"
group = CreateSeriesDetailsGroup(selectedItem.json.id)
else if selectedItemType = "Season"
@@ -520,7 +540,11 @@ sub Main (args as dynamic) as void
group.findNode("SearchBox").findNode("search_Key").active = true
else if button.id = "change_server"
unset_setting("server")
- unset_setting("port")
+ session.server.Delete()
+ SignOut(false)
+ sceneManager.callFunc("clearScenes")
+ goto app_start
+ else if button.id = "change_user"
SignOut(false)
sceneManager.callFunc("clearScenes")
goto app_start
diff --git a/source/ShowScenes.brs b/source/ShowScenes.brs
index a07715a4e..48a1853db 100644
--- a/source/ShowScenes.brs
+++ b/source/ShowScenes.brs
@@ -1,4 +1,4 @@
-function LoginFlow(startOver = false as boolean)
+function LoginFlow()
'Collect Jellyfin server and user information
start_login:
@@ -41,9 +41,11 @@ function LoginFlow(startOver = false as boolean)
activeUser = get_setting("active_user")
if activeUser = invalid
print "No active user found in registry"
+ user_select:
SendPerformanceBeacon("AppDialogInitiate") ' Roku Performance monitoring - Dialog Starting
publicUsers = GetPublicUsers()
- if publicUsers.count()
+ numPubUsers = publicUsers.count()
+ if numPubUsers > 0
publicUsersNodes = []
for each item in publicUsers
user = CreateObject("roSGNode", "PublicUserData")
@@ -55,18 +57,57 @@ function LoginFlow(startOver = false as boolean)
publicUsersNodes.push(user)
end for
userSelected = CreateUserSelectGroup(publicUsersNodes)
+
+ SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
if userSelected = "backPressed"
- SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
- return LoginFlow(true)
+ session.server.Delete()
+ unset_setting("server")
+ goto start_login
else
+ print "A public user was selected with username=" + userSelected
+ session.user.Update("name", userSelected)
+ regex = CreateObject("roRegex", "[^a-zA-Z0-9\ \-\_]", "")
+ session.user.Update("friendlyName", regex.ReplaceAll(userSelected, ""))
+ ' save userid to session
+ for each user in publicUsersNodes
+ if user.name = userSelected
+ session.user.Update("id", user.id)
+ exit for
+ end if
+ end for
+ ' try to login with token from registry
+ myToken = get_user_setting("token")
+ if myToken <> invalid
+ ' check if token is valid
+ print "Auth token found in registry for selected user"
+ session.user.Update("authToken", myToken)
+ print "Attempting to use API with auth token"
+ currentUser = AboutMe()
+ if currentUser = invalid
+ print "Auth token is no longer valid - deleting token"
+ unset_user_setting("token")
+ unset_user_setting("username")
+ else
+ print "Success! Auth token is still valid"
+ session.user.Login(currentUser)
+ LoadUserPreferences()
+ LoadUserAbilities()
+ return true
+ end if
+ else
+ print "No auth token found in registry for selected user"
+ end if
'Try to login without password. If the token is valid, we're done
+ print "Attempting to login with no password"
userData = get_token(userSelected, "")
if isValid(userData)
+ print "login success!"
session.user.Login(userData)
LoadUserPreferences()
LoadUserAbilities()
- SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
return true
+ else
+ print "Auth failed. Password required"
end if
end if
else
@@ -75,65 +116,52 @@ function LoginFlow(startOver = false as boolean)
passwordEntry = CreateSigninGroup(userSelected)
SendPerformanceBeacon("AppDialogComplete") ' Roku Performance monitoring - Dialog Closed
if passwordEntry = "backPressed"
- m.global.sceneManager.callFunc("clearScenes")
- return LoginFlow(true)
+ if numPubUsers > 0
+ goto user_select
+ else
+ session.server.Delete()
+ unset_setting("server")
+ goto start_login
+ end if
end if
else
print "Active user found in registry"
session.user.Update("id", activeUser)
+ myUsername = get_user_setting("username")
myAuthToken = get_user_setting("token")
- if isValid(myAuthToken)
+ if isValid(myAuthToken) and isValid(myUsername)
print "Auth token found in registry"
session.user.Update("authToken", myAuthToken)
+ session.user.Update("name", myUsername)
+ regex = CreateObject("roRegex", "[^a-zA-Z0-9\ \-\_]", "")
+ session.user.Update("friendlyName", regex.ReplaceAll(myUsername, ""))
print "Attempting to use API with auth token"
currentUser = AboutMe()
if currentUser = invalid
- print "Auth token is no longer valid - restart login flow"
- unset_user_setting("token")
- unset_setting("active_user")
- session.user.Logout()
- goto start_login
+ print "Auth token is no longer valid"
+ 'Try to login without password. If the token is valid, we're done
+ print "Attempting to login with no password"
+ userData = get_token(userSelected, "")
+ if isValid(userData)
+ print "login success!"
+ session.user.Login(userData)
+ LoadUserPreferences()
+ LoadUserAbilities()
+ return true
+ else
+ print "Auth failed. Password required"
+ print "delete token and restart login flow"
+ unset_user_setting("token")
+ unset_user_setting("username")
+ goto start_login
+ end if
else
print "Success! Auth token is still valid"
session.user.Login(currentUser)
end if
else
print "No auth token found in registry"
- myUsername = get_setting("username")
- myPassword = get_setting("password")
- userData = invalid
-
- if isValid(myUsername) and isValid(myPassword)
- if myUsername <> ""
- print "Username and password found in registry. Attempting to login"
- userData = get_token(myUsername, myPassword)
- else
- print "Username in registry is an empty string"
- unset_setting("username")
- unset_setting("password")
- end if
- else if isValid(myUsername) and not isValid(myPassword)
- print "Username found in registry but no password"
- if myUsername <> ""
- print "Attempting to login with no password"
- userData = get_token(myUsername, "")
- else
- print "Username in registry is an empty string"
- unset_setting("username")
- end if
-
- else if not isValid(myUsername) and not isValid(myPassword)
- print "Neither username nor password found in registry - restart login flow"
- unset_setting("active_user")
- session.user.Logout()
- goto start_login
- end if
-
- if isValid(userData)
- print "login success!"
- session.user.Login(userData)
- end if
end if
end if
@@ -254,11 +282,6 @@ function CreateServerGroup()
m.scene.dialog = dialog
serverUrl = standardize_jellyfin_url(screen.serverUrl)
- 'If this is a different server from what we know, reset username/password setting
- if m.global.session.server.url <> serverUrl
- set_setting("username", "")
- set_setting("password", "")
- end if
set_setting("server", serverUrl)
isConnected = session.server.UpdateURL(serverUrl)
@@ -362,25 +385,6 @@ function CreateSigninGroup(user = "")
group.findNode("prompt").text = tr("Sign In")
- 'Load in any saved server data and see if we can just log them in...
- server = m.global.session.server.url
- if isValid(server)
- server = LCase(server)'Saved server data is always lowercase
- end if
- saved = get_setting("saved_servers")
- if isValid(saved)
- savedServers = ParseJson(saved)
- for each item in savedServers.serverList
- if item.baseUrl = server and isValid(item.username) and isValid(item.password)
- userData = get_token(item.username, item.password)
- if isValid(userData)
- session.user.Login(userData)
- return "true"
- end if
- end if
- end for
- end if
-
config = group.findNode("configOptions")
username_field = CreateObject("roSGNode", "ConfigData")
username_field.label = tr("Username")
@@ -447,11 +451,10 @@ function CreateSigninGroup(user = "")
activeUser = get_token(username.value, password.value)
if isValid(activeUser)
session.user.Login(activeUser)
- set_setting("username", username.value)
- set_setting("password", password.value)
+ ' save credentials
if checkbox.checkedState[0] = true
- 'Update our saved server list, so next time the user can just click and go
- UpdateSavedServerList()
+ set_user_setting("token", activeUser.token)
+ set_user_setting("username", username.value)
end if
return "true"
end if
@@ -515,6 +518,7 @@ function CreateHomeGroup()
new_options = []
options_buttons = [
{ "title": "Search", "id": "goto_search" },
+ { "title": "Change user", "id": "change_user" },
{ "title": "Change server", "id": "change_server" },
{ "title": "Sign out", "id": "sign_out" }
]
@@ -862,34 +866,6 @@ function CreatePersonView(personData as object) as dynamic
return person
end function
-sub UpdateSavedServerList()
- server = m.global.session.server.url
- username = get_setting("username")
- password = get_setting("password")
-
- if server = invalid or username = invalid or password = invalid
- return
- end if
-
- server = LCase(server)'Saved server data is always lowercase
-
- saved = get_setting("saved_servers")
- if isValid(saved)
- savedServers = ParseJson(saved)
- if isValid(savedServers.serverList) and savedServers.serverList.Count() > 0
- newServers = { serverList: [] }
- for each item in savedServers.serverList
- if item.baseUrl = server
- item.username = username
- item.password = password
- end if
- newServers.serverList.Push(item)
- end for
- set_setting("saved_servers", FormatJson(newServers))
- end if
- end if
-end sub
-
'Opens dialog asking user if they want to resume video or start playback over only on the home screen
sub playbackOptionDialog(time as longinteger, meta as object)
diff --git a/source/api/baserequest.brs b/source/api/baserequest.brs
index fde121ac1..103461339 100644
--- a/source/api/baserequest.brs
+++ b/source/api/baserequest.brs
@@ -203,12 +203,16 @@ function authRequest(request as object) as object
if m.global.session.user.id <> invalid
auth = auth + ", UserId=" + QUOTE + m.global.session.user.id + QUOTE
- auth = auth + ", DeviceId=" + QUOTE + m.global.device.id + QUOTE
- if m.global.session.user.authToken <> invalid
- auth = auth + ", Token=" + QUOTE + m.global.session.user.authToken + QUOTE
- end if
+ end if
+
+ if m.global.session.user <> invalid and m.global.session.user.friendlyName <> invalid
+ auth = auth + ", DeviceId=" + QUOTE + m.global.device.id + m.global.session.user.friendlyName + QUOTE
else
- auth = auth + ", DeviceId=" + QUOTE + m.global.device.uuid + QUOTE
+ auth = auth + ", DeviceId=" + QUOTE + m.global.device.id + QUOTE
+ end if
+
+ if m.global.session.user.authToken <> invalid
+ auth = auth + ", Token=" + QUOTE + m.global.session.user.authToken + QUOTE
end if
request.AddHeader("Authorization", auth)
diff --git a/source/api/userauth.brs b/source/api/userauth.brs
index 657af4931..ce00984b9 100644
--- a/source/api/userauth.brs
+++ b/source/api/userauth.brs
@@ -32,28 +32,9 @@ function AboutMe(id = "" as string)
end function
sub SignOut(deleteSavedEntry = true as boolean)
- if m.global.session.user.id <> invalid
+ if m.global.session.user.id <> invalid and deleteSavedEntry = true
unset_user_setting("token")
- unset_setting("username")
- unset_setting("password")
- if deleteSavedEntry = true
- 'Also delete any credentials in the "saved servers" list
- saved = get_setting("saved_servers")
- server = m.global.session.server.url
- if server <> invalid
- server = LCase(server)
- savedServers = ParseJson(saved)
- newServers = { serverList: [] }
- for each item in savedServers.serverList
- if item.baseUrl = server
- item.username = ""
- item.password = ""
- end if
- newServers.serverList.Push(item)
- end for
- set_setting("saved_servers", FormatJson(newServers))
- end if
- end if
+ unset_user_setting("username")
end if
unset_setting("active_user")
session.user.Logout()
diff --git a/source/utils/deviceCapabilities.brs b/source/utils/deviceCapabilities.brs
index 902e9805f..46353fbd3 100644
--- a/source/utils/deviceCapabilities.brs
+++ b/source/utils/deviceCapabilities.brs
@@ -12,7 +12,7 @@ function getDeviceCapabilities() as object
"Photo"
],
"SupportedCommands": [],
- "SupportsPersistentIdentifier": false,
+ "SupportsPersistentIdentifier": true,
"SupportsMediaControl": false,
"SupportsContentUploading": false,
"SupportsSync": false,
diff --git a/source/utils/session.bs b/source/utils/session.bs
index 072eb5d19..72059547c 100644
--- a/source/utils/session.bs
+++ b/source/utils/session.bs
@@ -156,6 +156,10 @@ namespace session
tmpSession.AddReplace("user", userData.json.User)
tmpSession.user.AddReplace("authToken", userData.json.AccessToken)
end if
+ ' remove special characters from name
+ regex = CreateObject("roRegex", "[^a-zA-Z0-9\ \-\_]", "")
+ friendlyName = regex.ReplaceAll(tmpSession.user.name, "")
+ tmpSession.user.AddReplace("friendlyName", friendlyName)
tmpSession.user.AddReplace("settings", oldUserSettings)
' update global user session
@@ -174,9 +178,10 @@ namespace session
print "m.global.session.user.settings = ", m.global.session.user.settings
end if
- ' ensure registry is updated
- set_user_setting("username", tmpSession.user.name)
- set_user_setting("token", tmpSession.user.authToken)
+ if m.global.session.user.settings["global.rememberme"]
+ set_user_setting("token", tmpSession.user.authToken)
+ set_user_setting("username", tmpSession.user.name)
+ end if
end sub
' Empty the global user session array and reload defaults
@@ -254,6 +259,20 @@ namespace session
end for
end if
end for
+
+ ' load globals
+ session.user.settings.LoadGlobals()
+ end sub
+
+ ' Grab global vars from registry and overwrite defaults
+ sub LoadGlobals()
+ ' search main registry block for all keys that start with "global."
+ jfRegistry = RegistryReadAll("Jellyfin")
+ for each item in jfRegistry
+ if Left(item, 7) = "global."
+ session.user.settings.Save(item, get_setting(item))
+ end if
+ end for
end sub
' Saves the user setting to the global session.