> ## Documentation Index
> Fetch the complete documentation index at: https://globaliptv.misidev.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Utilities

> Reference for all utility modules in source/utils/. These are plain BrightScript function libraries included via <script> tags — they have no SceneGraph node overhead.

<AccordionGroup>
  <Accordion title="Logger.brs">
    **File:** `source/utils/Logger.brs`

    ### Purpose

    Three-level logging facade. `GTV_Log` is gated on the `IS_DEV_BUILD` compile-time constant; `GTV_Warn` and `GTV_Error` always print.

    ### Functions

    ```brightscript theme={null}
    sub GTV_Log(tag as String, msg as String)
        ' Prints only when IS_DEV_BUILD = true (bs_const in manifest)
        #if IS_DEV_BUILD
            print "[GTV][" + tag + "] " + msg
        #end if
    end sub

    sub GTV_Warn(tag as String, msg as String)
        print "[GTV][WARN][" + tag + "] " + msg
    end sub

    sub GTV_Error(tag as String, msg as String)
        print "[GTV][ERROR][" + tag + "] " + msg
    end sub
    ```

    ### Output format examples

    | Function                                                       | Example output                                               |
    | -------------------------------------------------------------- | ------------------------------------------------------------ |
    | `GTV_Log("PlaylistTask", "Parsed 42 channels")`                | `[GTV][PlaylistTask] Parsed 42 channels`                     |
    | `GTV_Warn("MainScene", "Auto-login failed")`                   | `[GTV][WARN][MainScene] Auto-login failed`                   |
    | `GTV_Error("HandshakeTask", "event=handshake_failed code=-1")` | `[GTV][ERROR][HandshakeTask] event=handshake_failed code=-1` |

    See [Logging](/reference/logging) for full details on enabling dev logging and viewing output.
  </Accordion>

  <Accordion title="HttpClient.brs">
    **File:** `source/utils/HttpClient.brs`

    ### Purpose

    HTTP request wrapper for all backend calls. Handles TLS certificates, platform headers, user-agent, async execution, and timeout cancellation. The function signature defaults to `15 000 ms`; callers pass `TIMEOUT_HTTP = 12 000 ms` or `TIMEOUT_AUTH = 15 000 ms` explicitly.

    ### Functions

    ```brightscript theme={null}
    function GTV_HttpGet(
        url       as String,
        timeoutMs as Integer = 15000
    ) as Object
    ```

    Performs an async GET. Returns:

    ```brightscript theme={null}
    { code: Integer, body: String, ok: Boolean }
    ' ok = (code >= 200 and code < 300)
    ' code = -1 on timeout or connection failure
    ```

    ***

    ```brightscript theme={null}
    function GTV_HttpPost(
        url       as String,
        jsonBody  as String,
        timeoutMs as Integer = 15000
    ) as Object
    ```

    Performs an async POST with a JSON body. Same return shape as `GTV_HttpGet`.

    ***

    ```brightscript theme={null}
    function GTV_BuildQuery(params as Object) as String
    ```

    Builds a URL-encoded query string from an AssocArray. Values are escaped with `GTV_UrlEncode`.

    ```brightscript theme={null}
    function GTV_UrlEncode(s as String) as String
    ```

    Escapes a string using `roUrlTransfer.Escape()`.

    ***

    ```brightscript theme={null}
    function GTV_BuildUserAgent(di = invalid as Dynamic) as String
    ```

    Builds the `User-Agent` header string.

    Example: `GlobalTV_Roku/1.0.7 (roku; Roku Express; 12.5.0)`

    ***

    ```brightscript theme={null}
    function GTV_GetAppVersion() as String
    function GTV_GetAppIdentity() as String
    function GTV_GetBrandedDeviceModel(di = invalid as Dynamic) as String
    ```

    ### Request headers set on every call

    | Header         | Value                         |
    | -------------- | ----------------------------- |
    | `Content-Type` | `application/json`            |
    | `X-Platform`   | `roku` (`PLATFORM` constant)  |
    | `User-Agent`   | `GTV_BuildUserAgent()` result |

    ### Timeout constants (from AppConstants)

    | Constant         | Value     | Used for                                        |
    | ---------------- | --------- | ----------------------------------------------- |
    | `TIMEOUT_HTTP`   | 12 000 ms | Standard requests (playlist, metrics, ads poll) |
    | `TIMEOUT_AUTH`   | 15 000 ms | Auth requests                                   |
    | `TIMEOUT_HEALTH` | 2 500 ms  | Health pings only                               |
  </Accordion>

  <Accordion title="RegistryManager.brs">
    **File:** `source/utils/RegistryManager.brs`

    ### Purpose

    Thin wrapper around `roRegistrySection` for persistent key-value storage. All reads/writes use the registry section `"GlobalTV"` (`REG_SECTION` constant). Flush is called automatically on every write.

    ### Core functions

    ```brightscript theme={null}
    function GTV_RegRead(key as String, defaultVal = "" as String) as String
    sub     GTV_RegWrite(key as String, value as String)
    sub     GTV_RegDelete(key as String)
    ```

    ### Credential functions

    ```brightscript theme={null}
    function GTV_RegHasCredentials() as Boolean
    function GTV_RegLoadCredentials() as Object
        ' Returns { username: String, password: String } or invalid
    sub     GTV_RegSaveCredentials(username as String, password as String)
    sub     GTV_RegClearCredentials()
    ```

    ### Server / cache functions

    ```brightscript theme={null}
    function GTV_RegLoadLastServer() as String
    sub     GTV_RegSaveLastServer(serverUrl as String)
    sub     GTV_RegClearCache()
        ' Deletes REG_KEY_SERVER and REG_KEY_CHANNEL
    ```

    ### Channel index functions

    ```brightscript theme={null}
    function GTV_RegLoadLastChannelIndex() as Integer
    sub     GTV_RegSaveLastChannelIndex(idx as Integer)
    ```

    ### Safe-area margin functions

    ```brightscript theme={null}
    function GTV_RegLoadSafeMarginPct() as Integer
        ' Clamped to [UI_SAFE_MARGIN_MIN_PCT, UI_SAFE_MARGIN_MAX_PCT]
    sub     GTV_RegSaveSafeMarginPct(pct as Integer)
    ```

    ### Registry key names (from AppConstants)

    | Constant                  | Key string           | Stores                           |
    | ------------------------- | -------------------- | -------------------------------- |
    | `REG_KEY_USER`            | `"username"`         | Saved username                   |
    | `REG_KEY_PASS`            | `"password"`         | Saved password                   |
    | `REG_KEY_SERVER`          | `"lastServer"`       | Last active server URL           |
    | `REG_KEY_CHANNEL`         | `"lastChannelIndex"` | Last viewed channel index        |
    | `REG_KEY_SAFE_MARGIN`     | `"uiSafeMarginPct"`  | UI safe-area margin percentage   |
    | `REG_KEY_SERVER_PROFILES` | `"serverProfiles"`   | JSON-encoded server profile list |
  </Accordion>

  <Accordion title="ServerManager.brs">
    **File:** `source/utils/ServerManager.brs`

    ### Purpose

    Server list management. Maintains a list of server profiles (LAN and WAN), builds probe sequences with LAN-first retry behavior, resolves the active server, and provides CRUD operations on profiles.

    ### Key functions

    ```brightscript theme={null}
    function GTV_LoadServerProfiles() as Object
        ' Returns array of sanitized server profiles from registry,
        ' falling back to GTV_DefaultServerProfiles() if none saved.

    sub GTV_SaveServerProfiles(profiles as Object)

    function GTV_ServerList() as Object
        ' Returns array of baseUrl strings from current profiles.

    function GTV_FindActiveServer(forcedServer = "" as String) as String
        ' Probes the auto-probe list and returns the first reachable server URL.
        ' Saves it to registry via GTV_RegSaveLastServer().

    function GTV_ServerBuildAutoProbeList() as Object
        ' Builds ordered probe list: LAN servers repeated SERVER_LAN_FORCE_RETRIES
        ' times, then WAN servers.

    function GTV_ServerBuildRequestSequence(preferredBaseUrl = "" as String) as Object
        ' Like GTV_ServerBuildAutoProbeList() but puts preferredBaseUrl first.

    function GTV_ServerInferType(baseUrl as String) as String
        ' Returns "lan" for RFC-1918 addresses (172.17.*, 192.168.*, 10.*,
        ' localhost), "wan" for everything else.

    function GTV_ServerNormalizeBaseUrl(raw as Dynamic) as String
        ' Strips trailing slash, validates scheme (http/https).
        ' Returns "" for invalid URLs.

    function GTV_ServerGetProfileById(profiles as Object, profileId as String) as Dynamic
    function GTV_ServerGetProfileByBaseUrl(profiles as Object, baseUrl as String) as Dynamic

    function GTV_ServerUpsertProfile(profiles as Object, profile as Object) as Object
    function GTV_ServerDeleteProfile(profiles as Object, profileId as String) as Object
    function GTV_ServerCanDelete(profiles as Object, profileId as String) as Boolean
        ' Returns false if deleting would leave the list empty.

    function GTV_ServerCreateProfile(name as String, baseUrl as String, existing = invalid as Dynamic) as Dynamic
    function GTV_ServerDescribe(profile as Dynamic) as String
        ' Returns a human-readable string: "Local [LAN] - http://172.17.11.2:3333"
    ```

    ### Default server profiles

    ```brightscript theme={null}
    [
        {
            id      : "local_default"
            name    : "Local"
            baseUrl : "http://172.17.11.2:3333"
            type    : "lan"
            builtIn : true
            enabled : true
        },
        {
            id      : "public_default"
            name    : "Publico"
            baseUrl : "https://admin.globaltv.lat"
            type    : "wan"
            builtIn : true
            enabled : true
        }
    ]
    ```

    ### LAN retry behavior

    `SERVER_LAN_FORCE_RETRIES = 3` — the LAN server URL is inserted 3 times at the head of the probe list before WAN servers are tried. This prioritizes the local server without completely blocking on a single failure.
  </Accordion>

  <Accordion title="M3UParser.brs">
    **File:** `source/utils/M3UParser.brs`

    ### Purpose

    M3U8 playlist parser. Extracts channel metadata from `#EXTINF` lines and builds sorted channel and category arrays. Enforces or relaxes channel number requirements via `PARSER_STRICT_CHANNEL_NUMBER`.

    ### Main function

    ```brightscript theme={null}
    function GTV_ParseM3U(text as String) as Object
    ```

    Parses an M3U8 string. Returns:

    ```brightscript theme={null}
    {
        channels               : [],   ' sorted by channel number
        categories             : [],   ' deduplicated group-title values
        droppedMissingNumber   : 0,    ' entries dropped due to missing channel-number
        droppedDuplicateNumber : 0,    ' entries dropped due to duplicate number
        fallbackAssigned       : 0     ' entries assigned an auto-index (non-strict mode only)
    }
    ```

    ### Strict mode (`PARSER_STRICT_CHANNEL_NUMBER = true`)

    When enabled (the default), any `#EXTINF` entry that does not have a valid `channel-number` attribute greater than 0 is **dropped** and counted in `droppedMissingNumber`. Duplicate channel numbers are also dropped.

    When disabled, entries without a `channel-number` receive an auto-incremented fallback index and are counted in `fallbackAssigned`.

    ### Attribute extraction

    ```brightscript theme={null}
    function GTV_ExtractAttr(src as String, attrName as String) as String
        ' Extracts attrName="value" or attrName='value' from an #EXTINF line.
        ' Handles both double-quoted and single-quoted values.
    ```

    ### Supported `#EXTINF` attributes

    | Attribute        | Channel field                               |
    | ---------------- | ------------------------------------------- |
    | `tvg-id`         | `id`                                        |
    | `tvg-name`       | `name` (falls back to trailing comma label) |
    | `tvg-logo`       | `logo`                                      |
    | `group-title`    | `group` (default: `"General"`)              |
    | `channel-number` | `number`                                    |

    ### URL normalization

    ```brightscript theme={null}
    function GTV_NormalizeChannelUrl(rawUrl as String) as String
        ' hls://  -> http://
        ' hlss:// -> https://
        ' All others passed through unchanged.
    ```

    ### Stream ID extraction

    ```brightscript theme={null}
    function GTV_ExtractStreamIdFromChannelUrl(rawUrl as String) as String
        ' Walks URL path segments in reverse, looking for a hex segment
        ' of >= 12 characters (likely a stream UUID). Used to populate
        ' channel.streamId and channel.contentId.
    ```

    ### Sorting

    Channels are sorted ascending by `channel.number` using an insertion sort (`GTV_SortChannels`).
  </Accordion>

  <Accordion title="HealthCheck.brs">
    **File:** `source/utils/HealthCheck.brs`

    ### Purpose

    Single-function HTTP health prober. Used by `ServerManager`, `ConnectivityTask`, and all tasks that need to determine whether a server is reachable before making a real request.

    ### Function

    ```brightscript theme={null}
    function GTV_PingServer(baseUrl as String) as Boolean
    ```

    Sends an async GET to `baseUrl + PATH_HEALTH` (`/health`) with a timeout of `TIMEOUT_HEALTH = 2 500 ms`.

    Returns `true` if the HTTP response code is in the range `200–499` (i.e., the server is up even if it returns a client error). Returns `false` on timeout, connection failure, or HTTP 5xx.

    ### TLS

    For `https://` URLs, `CERTS = "common:/certs/ca-bundle.crt"` is applied automatically.

    ### Usage pattern

    ```brightscript theme={null}
    if GTV_PingServer("https://admin.globaltv.lat")
        ' server is reachable
    end if
    ```
  </Accordion>

  <Accordion title="NetworkDetector.brs">
    **File:** `source/utils/NetworkDetector.brs`

    ### Purpose

    Device-level network state detection. Uses `roDeviceInfo.GetConnectionType()` to check whether the Roku device has any network interface active, without making an outbound HTTP request.

    ### Functions

    ```brightscript theme={null}
    function GTV_IsOnline() as Boolean
        ' Returns true if roDeviceInfo.GetConnectionType() is non-empty.
        ' An empty string means no network interface is connected.
    ```

    ```brightscript theme={null}
    function GTV_GetConnectionType() as String
        ' Returns the raw connection type string from roDeviceInfo
        ' (e.g. "WiFiConnection", "WiredConnection", "").
    ```

    ```brightscript theme={null}
    function GTV_HasInternet(activeServer as String) as Boolean
        ' Combines GTV_IsOnline() with GTV_PingServer() for a full
        ' two-stage connectivity check:
        '   1. Device has a network interface (GTV_IsOnline)
        '   2. The active server health endpoint is reachable (GTV_PingServer)
    ```

    ### Difference from HealthCheck

    `GTV_IsOnline()` is a cheap local check (no network I/O). `GTV_PingServer()` makes an outbound HTTP call. `ConnectivityTask` uses both: `GTV_IsOnline()` writes to `m.global.isOnline` and `GTV_PingServer()` writes to `m.global.hasInternet`.
  </Accordion>
</AccordionGroup>
