Members

(static, constant) HOME_ROW_LIMIT_DEFAULT

Mirrors the uiHomeRowLimit default in settings/settings.json, and is reached only when that setting is missing or unusable. Pinned to the JSON by a unit test rather than by this comment, because a comment cannot fail.

Default Value
  • 32

Methods

(static) DeleteFromServerList(idOrUrl) → {void}

Parameters:
NameTypeDescription
idOrUrlstring
Returns:
Type: 
void

(static) GetConfigTree() → {dynamic}

Read config tree from json config file and return

Returns:
Type: 
dynamic

(static) RegistryReadAll(section) → {dynamic}

Return all data found inside a registry section

Parameters:
NameTypeDescription
sectionstring
Returns:
Type: 
dynamic

(static) SaveServerList() → {void}

Saved-server list persistence. Pure registry operations on the saved_servers key — config.bs's domain, alongside getSavedUsers — called from both SetServerScreen (render thread, delete) and the loginRouter coordinator (main thread, save).

Returns:
Type: 
void

(static) clampToSettingRange(value, bounds) → {integer}

Bring a typed integer inside a declared range.

Returns the value unchanged when it already fits, so a caller can compare against its input to decide whether the user needs telling. Kept separate from settingRangeBounds for that reason — the SCREEN needs to know that a change happened, not just what to store.

Parameters:
NameTypeDescription
valueinteger
boundsobject
Returns:
Type: 
integer

(static) findConfigTreeKey(key, tree) → {dynamic}

Recursivly search the config tree for entry with settingname equal to key

Parameters:
NameTypeDescription
keystring
treedynamic
Returns:
Type: 
dynamic

(static) findServerInList(savedJson, guid) → {object}

Pure: match the saved_servers JSON against a server GUID, case-insensitively (via serverIdsMatch). Split out from the registry read (findSavedServerByGuid in replayRoute.bs) so the match logic is unit-testable without touching the registry (getSetting reads the real "JellyRock" section, which tests must not write). Lives here alongside the saved_servers shape.

Parameters:
NameTypeDescription
savedJsondynamic
guidstring
Returns:
Type: 
object

(static) getGlobalRegistrySection() → {string}

Helper to get the global registry section name Returns "test-global" during tests, "JellyRock" in production

Returns:
Type: 
string

(static) getRegistrySections() → {object}

Return an array of all the registry section keys

Returns:
Type: 
object

(static) getSavedUsers() → {object}

Returns an array of saved users from the registry that belong to the active server

Returns:
Type: 
object

(static) getSetting(key, defaultValueopt) → {dynamic}

"JellyRock" registry accessors for the default global settings

Parameters:
NameTypeAttributesDefaultDescription
keydynamic
defaultValuedynamic<optional>
invalid
Returns:
Type: 
dynamic

(static) getSettingKeysToDelete(allKeys, preserveKeys) → {object}

Filter registry keys to find those that should be deleted during a settings reset Preserves session/identity keys, deletes everything else

Parameters:
NameTypeDescription
allKeysobject

associative array of all registry key-value pairs

preserveKeysobject

array of key names to preserve

Returns:
  • array of key names that should be deleted
Type: 
object

(static) getUserSetting(key) → {dynamic}

User registry accessors for the currently active user

Parameters:
NameTypeDescription
keystring
Returns:
Type: 
dynamic

(static) isGlobalSetting(key) → {boolean}

Helper to determine if a setting is global (applies to all users)

Parameters:
NameTypeDescription
keystring
Returns:
Type: 
boolean

(static) isIntSafe(value) → {boolean}

Is this value safe to hand to Int()?

Int() faults with a Type Mismatch on anything that is not a number — measured on a Stick 4K — and BrighterScript cannot catch it, because every caller here holds the value as dynamic. So the check is by TYPE rather than by coercion: accepting "1" would legitimize two spellings of one field and let the JSON drift between them, and the schema test forces the right one at PR time. An absent value reports its type as invalid and lands here too, which is why callers need no separate isValid() check.

Named for the question rather than for either caller: it guards settingRangeBounds (authored JSON) and resolveHomeRowLimit (a node field), which share nothing except that both would fault on the same input.

Parameters:
NameTypeDescription
valuedynamic
Returns:
Type: 
boolean

(static) registryDelete(key, sectionopt) → {void}

Parameters:
NameTypeAttributesDefaultDescription
keydynamic
sectiondynamic<optional>
invalid
Returns:
Type: 
void

(static) registryRead(key, sectionopt) → {dynamic}

Generic registry accessors

Parameters:
NameTypeAttributesDefaultDescription
keydynamic
sectiondynamic<optional>
invalid
Returns:
Type: 
dynamic

(static) registryWrite(key, value, sectionopt) → {void}

Parameters:
NameTypeAttributesDefaultDescription
keydynamic
valuedynamic
sectiondynamic<optional>
invalid
Returns:
Type: 
void

(static) resolveHomeRowLimit(settingValue) → {integer}

How many items a Home browse FEED asks the server for.

ONE number for all three feeds — Recently Added, On Now, Active Recordings — because they had no reason to differ. All three sent 16, and they were tied to each other only by comment ("16 to be consistent with Latest In", "parity with onNow"). Sharing the setting makes that parity a fact instead of a claim three files have to keep agreeing on by hand.

The line this setting does NOT cross is feed vs worklist:

FEED - a sample of a larger set (these three). A limit chooses how much of the sample to show, so it is a real user preference and nothing is hidden by raising it. WORKLIST - a set the user is trying to get THROUGH. Continue Watching and Next Up send no limit at all, deliberately: any cap hides something already started, and no number is the right one to hide at. Next Up sent an arbitrary 69 until this change removed it; Continue Watching has always sent none.

The Favorites TAB (itemsToLoad = "favorites", loaded by FavoritesRows) is not a Home row at all, so it is out of scope on identity rather than on this rule.

The number is the uiHomeRowLimit user setting; this is the guard for the setting not being there. NOT a second copy of the range enforced when the value is SAVED — components/settings/settings.bs reads min/max straight off the settings.json entry, so the range has one source of truth and it is not this function.

Zero is the case worth naming. It is what an integer node field reads before SaveDefaults() has populated it, and it is a legal value to send: Jellyfin would answer a Limit=0 with an empty list, so every affected row would silently come back EMPTY rather than fail. A fallback that only checked isValid would let that through.

Pure — the caller passes the field rather than this reading m.global — so it is unit testable without a global, and so a Task thread pays one rendezvous at its own call site instead of one hidden in here.

Parameters:
NameTypeDescription
settingValuedynamic

the raw uiHomeRowLimit field, possibly unset

Returns:

the item limit to send, never below 1

Type: 
integer

(static) serverIdsMatch(a, b) → {boolean}

True when two Jellyfin server ids refer to the same server. Jellyfin GUIDs are canonical lower-case hex, but fold both sides so a mis-cased id (e.g. from an external deep-link/cast sender) still matches. The single source of truth for "same server?" across the saved-server list and the saved-user filter — keeps every id comparison consistent.

Parameters:
NameTypeDescription
adynamic
bdynamic
Returns:
Type: 
boolean

(static) setSetting(key, value) → {void}

Parameters:
NameTypeDescription
keydynamic
valuedynamic
Returns:
Type: 
void

(static) setUserSetting(key, value) → {void}

Parameters:
NameTypeDescription
keystring
valuedynamic
Returns:
Type: 
void

(static) settingRangeBounds(entry) → {object}

The range a settings.json entry declares, or invalid when it declares none.

Every rejection below exists because Int() on the alternative is a Type Mismatch that takes down the Settings screen the moment the user OPENS that setting. Both shapes were measured on a Stick 4K rather than reasoned about, and neither is exotic:

  • ONE bound declared. Relaxing this to "honor whichever bound is present" makes the other one Int(invalid). (The weaker reason — the dialog text names both ends, so one bound alone would read "the supported range of 1 to invalid" — is true, and is not the one that matters.)
  • A bound written as a STRING. Int("1") faults the same way, and it is the likelier typo of the two: default in the SAME entry is conventionally a string ("16"), so "min": "1" reads as consistent while being the one spelling that crashes.

A malformed entry therefore degrades to "declares no range" — the setting still saves, it is just unbounded — rather than taking the screen down. It does not pass silently either: tests/scripts/unit/settings-schema.test.js fails the PR on every shape rejected here, so this guard is the runtime floor and the schema test is the enforcement.

Lives here rather than in the settings screen so it can be unit-tested without the component, alongside findConfigTreeKey which reads the same tree.

Parameters:
NameTypeDescription
entryobject
Returns:
Type: 
object

(static) unsetSetting(key) → {void}

Parameters:
NameTypeDescription
keydynamic
Returns:
Type: 
void

(static) unsetUserSetting(key) → {void}

Parameters:
NameTypeDescription
keystring
Returns:
Type: 
void

(static) valueToString(value) → {string}

Convert dynamic value to string for registry storage

Parameters:
NameTypeDescription
valuedynamic
Returns:
Type: 
string