Skip to main content

Overview

After a successful authentication, PlaylistTask downloads a server-generated M3U8 playlist and passes the parsed channel and category data to global state. MainScreen then reads this data to render the channel grid.

PlaylistTask

Downloads the M3U8 playlist via HTTP and writes results to m.global

M3UParser

Parses raw M3U8 text into structured channel and category arrays

m.global.channelList

Global state field where the parsed channel array is stored

MainScreen

Consumes m.global.channelList and m.global.categories to render the channel grid

Playlist endpoint

The playlist URL is built from AppConstants.PATH_PLAYLIST_TPL:
At runtime, PlaylistTask builds the URL with the current credentials URL-encoded:
The endpoint both authenticates the request and returns the playlist in a single round-trip, eliminating the need for a separate session token.

PlaylistTask fetch flow

1

Server selection

PlaylistTask reads m.global.activeServer (set by AuthTask during login). If that is empty, it calls GTV_FindActiveServer() to probe the server list again.
2

HTTP fetch with retry

The task fetches the playlist using GTV_HttpGet with up to RETRY_MAX = 3 attempts. On network-level failures (httpCode = -1), it advances to the next server in the request sequence (GTV_ServerBuildRequestSequence). On non-network HTTP errors, it stops immediately:
3

Auth failure detection

If the HTTP response is not OK, PlaylistTask passes the response body through GTV_AuthClassifyFailure — the same classifier used by AuthTask. A 401/470/471 response triggers the appropriate credential or inactive error message.
4

Parsing

On a successful response, the task passes the raw body to GTV_ParseM3U:
The parser returns a result object with channels, categories, droppedMissingNumber, droppedDuplicateNumber, and fallbackAssigned counts.
5

Global state update

Valid results are written to global state:
m.top.result and m.top.done are then set, signalling MainScene to proceed to MainScreen.

M3U8 format and parser

What M3U8 looks like

The server returns a standard M3U8 extended playlist. Each channel entry is a pair of lines:

Parser: GTV_ParseM3U

M3UParser.brs implements GTV_ParseM3U(text). For each #EXTINF line it:
  1. Extracts tvg-id, tvg-name, tvg-logo, group-title, and channel-number attributes using GTV_ExtractAttr.
  2. Falls back to the comma-separated display name if tvg-name is absent.
  3. Reads the URL from the next non-comment, non-empty line.
  4. Normalizes hls:// and hlss:// URL schemes to http:// and https:// respectively.
  5. Attempts to extract a stream ID (hex string ≥ 12 characters) from the URL path segments and stores it in streamId / contentId.

Strict channel number mode

PARSER_STRICT_CHANNEL_NUMBER = true is the production default. In strict mode:
  • Channels with no valid channel-number attribute are dropped (counted in droppedMissingNumber).
  • Channels with a duplicate number are dropped (counted in droppedDuplicateNumber).
  • No auto-numbering fallback is applied.
In strict mode, any channel entry in the M3U8 that lacks a positive integer channel-number attribute will be silently dropped. Ensure the backend always populates this field.
When PARSER_STRICT_CHANNEL_NUMBER = false, missing numbers are assigned sequentially starting from 1 (fallbackAssigned count increases).

Channel object structure

Each item in the parsed channels array has the following fields:

Channel sorting

After parsing, channels are sorted ascending by number using insertion sort (GTV_SortChannels).

Category extraction

Every unique group-title value becomes an entry in the categories array. Grouping is case-insensitive (keyed by LCase(ch.group)).

Global state fields


How MainScreen consumes the channel list

MainScreen reads m.global.channelList and m.global.categories after PlaylistTask signals completion. It uses these arrays to populate the channel grid. When the user selects a channel, MainScreen passes the channel index to PlayerScreen via m.global.currentChannelIndex. PlayerScreen.init() also reads m.global.channelList directly:
When AdsPollingTask needs to resolve the current stream position, it also reads from m.global.channelList:

Parse diagnostics

PlaylistTask logs a warning for each category of dropped or fallback-assigned channel: These warnings appear as GTV_Warn entries and are always printed, regardless of build type.