> ## 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.

# Global state

> All m.global fields defined in AppState.brs: auth, channels, player, ads, device, and deep links.

`m.global` is a special `roSGNode` provided by the Roku SceneGraph runtime. Unlike local `m` fields, `m.global` is shared across all components and tasks simultaneously — reads and writes are thread-safe and changes are immediately visible to any observer registered with `observeField`.

`GTV_InitGlobalState()` in `source/AppState.brs` calls `m.global.addFields({...})` once at startup inside `MainScene.init()` to declare every field with its initial value.

## Reading and writing global fields

**From a component** (render thread):

```brightscript theme={null}
' Write
m.global.playerState = "playing"
m.global.currentChannelIndex = 3

' Read
channels = m.global.channelList
if m.global.isAuthenticated = true
    ' ...
end if
```

**From a Task node** (background thread) — Tasks access `m.global` through the same `m.global` reference available in their script scope:

```brightscript theme={null}
' Task writes result back to a task field, not directly to m.global
' MainScene then reads the task field and updates m.global
m.global.activeServer  ' readable from tasks
```

**Observing changes** — components can react to global field changes:

```brightscript theme={null}
' In PlayerScreen.init()
m.global.observeField("channelList", "OnChannelListChanged")
```

<Info>
  Tasks should write to their own output fields (e.g., `done`, `result`) rather than writing directly to `m.global`. MainScene reads task output and updates `m.global` on the render thread. This avoids race conditions between the render thread and task threads.
</Info>

## Field reference

### Server and connectivity

| Field          | Type    | Default     | Description                                                                                                                                                   |
| -------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `activeServer` | string  | `""`        | Base URL of the currently active backend server, e.g. `"https://admin.globaltv.lat"`. Set by `HandshakeTask` or when the user applies a server from Settings. |
| `realtimeMode` | string  | `"polling"` | Reserved for future realtime transport selection. Currently always `"polling"`.                                                                               |
| `isOnline`     | boolean | `true`      | Whether a network interface is available (`GTV_IsOnline()`). Updated by PlayerScreen on each `netCheckTimer` fire.                                            |
| `hasInternet`  | boolean | `true`      | Whether the network interface has verified internet reachability. Updated by `ConnectivityTask` and PlayerScreen.                                             |

### Authentication

| Field                | Type    | Default | Description                                                                                                                        |
| -------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `isAuthenticated`    | boolean | `false` | Set to `true` by `AuthTask` on success; cleared to `false` on logout, session invalidation, or playlist failure.                   |
| `username`           | string  | `""`    | The authenticated user's username. Written by MainScene after successful login.                                                    |
| `rokuEmail`          | string  | `""`    | Roku account email obtained via `ChannelStore.getUserData`. Written by `LoginScreen` and passed to MainScene.                      |
| `userId`             | string  | `""`    | Backend user ID returned by the auth endpoint.                                                                                     |
| `subscriberActive`   | boolean | `false` | Whether the subscriber account is active. Used by `GTV_AuthLooksInactive()` to classify inactive-account failures.                 |
| `userInactiveReason` | string  | `""`    | Human-readable message shown in the inactivity or session-issue dialog.                                                            |
| `authNotice`         | string  | `""`    | One-time notice displayed on the `LoginScreen` after a forced logout. Consumed and cleared immediately on login init.              |
| `authReasonCode`     | integer | `0`     | Current failure classification: `0` = none, `460` = network down, `401` = credentials, `470` = inactive, `471` = password changed. |

<Tip>
  `authReasonCode` constants are defined in `AppConstants()` as `AUTH_REASON_NONE`, `AUTH_REASON_NETWORK_DOWN`, `AUTH_REASON_CREDENTIALS`, `AUTH_REASON_INACTIVE`, and `AUTH_REASON_PASSWORD_CHANGED`.
</Tip>

### Channel list and selection

| Field                          | Type    | Default | Description                                                                                                                                                              |
| ------------------------------ | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `channelList`                  | array   | `[]`    | Flat array of channel objects parsed from the M3U8 playlist. Each entry contains `contentId`, `name`, `number`, `url`, `group`, and `logo`.                              |
| `categories`                   | array   | `[]`    | Array of category labels derived from M3U8 `group-title` tags.                                                                                                           |
| `currentChannelIndex`          | integer | `0`     | Zero-based index into `channelList` for the channel currently being played. Written by `PlayerScreen.PlayChannel()` and updated on every channel switch.                 |
| `currentChannelId`             | string  | `""`    | `contentId` of the current channel. Used by the ad system to match snapshots and by MetricsTask for event attribution.                                                   |
| `overlaySelectedChannelIndex`  | integer | `-1`    | Flat index of the channel highlighted in the overlay grid. Kept in sync by `MainScreen.UpdateOverlaySelectedFlatIndex()`.                                                |
| `overlayHasManualContext`      | boolean | `false` | `true` when the user has manually navigated to a different category in the overlay. Used by MainScene to restore the exact category and row when re-opening the overlay. |
| `overlaySelectedCategoryIndex` | integer | `0`     | Category tab index remembered for overlay restoration.                                                                                                                   |
| `overlaySelectedRowIndex`      | integer | `-1`    | Row index within the selected category, remembered for overlay restoration.                                                                                              |

### Player state

| Field             | Type    | Default     | Description                                                                                                                                                            |
| ----------------- | ------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `playerState`     | string  | `"stopped"` | Mirror of `m.video.state`. Updated on every `OnVideoStateChanged()` event. Values: `"stopped"`, `"buffering"`, `"playing"`, `"paused"`, `"finished"`, `"error"`.       |
| `isOverlayActive` | boolean | `false`     | `true` while the channel-selection overlay is visible on top of the player. Written by `SetOverlayActive()` in MainScene and read by PlayerScreen to route key events. |

### Ad state

| Field            | Type    | Default | Description                                                                                                                  |
| ---------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `adsVersion`     | integer | `0`     | Version counter incremented by `AdsPollingTask` on each successful poll response. Cleared to `0` on logout or session reset. |
| `adsNextCheckAt` | integer | `0`     | Unix timestamp (seconds) of the next scheduled ad poll. Used by the task to enforce `ADS_POLL_MS` intervals.                 |

### Device info

| Field            | Type    | Default | Description                                                                 |
| ---------------- | ------- | ------- | --------------------------------------------------------------------------- |
| `deviceId`       | string  | `""`    | Roku device serial or unique identifier. Populated during handshake.        |
| `deviceModel`    | string  | `""`    | Roku model name, e.g. `"3900X"`.                                            |
| `osVersion`      | string  | `""`    | Roku OS version string.                                                     |
| `rida`           | string  | `""`    | Roku ID for Advertisers (RIDA). Used by the ad system for targeting.        |
| `lat`            | boolean | `false` | Limit Ad Tracking flag. `true` means the user has opted out of ad tracking. |
| `deviceIp`       | string  | `""`    | LAN IP address of the device.                                               |
| `deviceTimezone` | integer | `0`     | UTC offset in seconds.                                                      |

### Deep links and beacons

| Field                 | Type       | Default   | Description                                                                                                                                                    |
| --------------------- | ---------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `launchDeepLink`      | assocarray | `invalid` | The raw deep link args from `Main(args)` when `mediaType = "live"`. Set on `MainScene` (not `m.global`) and triggers `onLaunchDeepLink()` via `onChange`.      |
| `inputDeepLink`       | assocarray | `invalid` | Warm-start deep link received as `roInputEvent`. Set on `MainScene` (not `m.global`) and triggers `onInputDeepLink()` via `onChange`.                          |
| `appLaunchBeaconSent` | boolean    | `false`   | Prevents duplicate `AppLaunchComplete` beacons. Set to `true` the first time `MainScreen.SignalAppLaunchCompleteOnce()` fires. Never reset during the session. |

<Note>
  `launchDeepLink` and `inputDeepLink` are fields on `m.top` (the MainScene node), not on `m.global`. They are declared in `MainScene.xml` with `onChange` handlers and appear here for completeness.
</Note>

## Complete initialisation listing

The full `addFields` call from `source/AppState.brs`:

```brightscript theme={null}
sub GTV_InitGlobalState()
    m.global.addFields({
        activeServer        : "",
        realtimeMode        : "polling",
        isOnline            : true,
        hasInternet         : true,

        isAuthenticated     : false,
        username            : "",
        rokuEmail           : "",
        userId              : "",
        subscriberActive    : false,
        userInactiveReason  : "",
        authNotice          : "",
        authReasonCode      : 0,

        channelList         : [],
        categories          : [],
        currentChannelIndex : 0,
        currentChannelId    : "",
        overlaySelectedChannelIndex : -1,
        overlayHasManualContext : false,
        overlaySelectedCategoryIndex : 0,
        overlaySelectedRowIndex : -1,

        playerState         : "stopped",
        isOverlayActive     : false,

        adsVersion          : 0,
        adsNextCheckAt      : 0,

        deviceId            : "",
        deviceModel         : "",
        osVersion           : "",
        rida                : "",
        lat                 : false,
        deviceIp            : "",
        deviceTimezone      : 0,

        launchDeepLink      : invalid,
        inputDeepLink       : invalid,

        appLaunchBeaconSent : false
    })
end sub
```

## Registry fields

Some state is persisted across sessions in the Roku Registry (`roRegistrySection` named `"GlobalTV"`). These values survive channel updates and device reboots.

| Registry key (`AppConstants`) | Constant             | Description                                                                                                                                                                             |
| ----------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `REG_KEY_USER`                | `"username"`         | Saved username for auto-login. Written by MainScene after successful login; cleared on logout or credential failure.                                                                    |
| `REG_KEY_PASS`                | `"password"`         | Saved password for auto-login. Stored alongside username; cleared on the same conditions.                                                                                               |
| `REG_KEY_SERVER`              | `"lastServer"`       | Base URL of the last successfully used server. Written by HandshakeTask and by the server-apply action in Settings.                                                                     |
| `REG_KEY_CHANNEL`             | `"lastChannelIndex"` | Zero-based index of the last channel played. Written by `PlayerScreen.PlayChannel()` on every channel switch. Used by `ResolveStartupChannelIndex()` to resume where the user left off. |
| `REG_KEY_SAFE_MARGIN`         | `"uiSafeMarginPct"`  | Integer percentage (0–10) for the UI safe-area margin. Written by `SettingsScreen.AdjustSafeArea()` and read by `GTV_GetSafeMarginPct()` on every layout pass.                          |
| `REG_KEY_SERVER_PROFILES`     | `"serverProfiles"`   | JSON-encoded array of server profile objects. Managed by `ServerManager.brs` functions (`GTV_LoadServerProfiles`, `GTV_SaveServerProfiles`).                                            |

```brightscript theme={null}
' Reading registry credentials
creds = GTV_RegLoadCredentials()
if creds <> invalid
    ' creds.username and creds.password are available
    RunAutoLogin(creds.username, creds.password)
end if

' Saving after successful login
GTV_RegSaveCredentials(creds.username, creds.password)
GTV_RegSaveLastServer(serverUrl)
GTV_RegSaveLastChannelIndex(channelIndex)
```

<Warning>
  Credentials are stored in plaintext in the Roku Registry. The registry is device-local and not accessible to other channels, but you should not store sensitive data beyond what is required for auto-login.
</Warning>

## State lifecycle

The following events reset or clear portions of global state:

| Event                                   | Fields cleared                                                                                                                                                                                                  |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Logout                                  | `isAuthenticated`, `userId`, `rokuEmail`, `channelList`, `categories`, `currentChannelIndex`, `currentChannelId`, `playerState`, `adsVersion`, `adsNextCheckAt`, `subscriberActive`, `username`, `activeServer` |
| Session invalidation (credentials)      | Same as logout; also clears registry credentials                                                                                                                                                                |
| Session invalidation (inactive account) | Same as logout; registry credentials preserved to allow retry                                                                                                                                                   |
| Server switch (Settings → Apply now)    | `activeServer`, `realtimeMode`, `hasInternet`, `isOnline`, `authReasonCode`, `userInactiveReason`; then restarts handshake                                                                                      |
| Playlist refresh                        | `channelList` and `categories` are replaced with fresh data; `currentChannelIndex` is re-resolved by content ID                                                                                                 |
