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.globalM3UParser
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 gridPlaylist endpoint
The playlist URL is built fromAppConstants.PATH_PLAYLIST_TPL:
PlaylistTask builds the URL with the current credentials URL-encoded:
PlaylistTask fetch flow
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.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: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.Parsing
On a successful response, the task passes the raw body to The parser returns a result object with
GTV_ParseM3U:channels, categories, droppedMissingNumber, droppedDuplicateNumber, and fallbackAssigned counts.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:
- Extracts
tvg-id,tvg-name,tvg-logo,group-title, andchannel-numberattributes usingGTV_ExtractAttr. - Falls back to the comma-separated display name if
tvg-nameis absent. - Reads the URL from the next non-comment, non-empty line.
- Normalizes
hls://andhlss://URL schemes tohttp://andhttps://respectively. - 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-numberattribute are dropped (counted indroppedMissingNumber). - Channels with a duplicate number are dropped (counted in
droppedDuplicateNumber). - No auto-numbering fallback is applied.
PARSER_STRICT_CHANNEL_NUMBER = false, missing numbers are assigned sequentially starting from 1 (fallbackAssigned count increases).
Channel object structure
Each item in the parsedchannels array has the following fields:
| Field | Source | Description |
|---|---|---|
id | tvg-id | Provider channel ID |
name | tvg-name or comma field | Display name |
logo | tvg-logo | Logo URL |
group | group-title | Category name (defaults to General) |
number | channel-number | Integer channel number |
url | Next non-comment line | Normalized HLS stream URL |
streamId | Extracted from URL path | Hex stream identifier (if detectable) |
contentId | streamId or number | Used for ad targeting |
Channel sorting
After parsing, channels are sorted ascending bynumber using insertion sort (GTV_SortChannels).
Category extraction
Every uniquegroup-title value becomes an entry in the categories array. Grouping is case-insensitive (keyed by LCase(ch.group)).
Global state fields
| Field | Type | Set by | Consumed by |
|---|---|---|---|
m.global.channelList | Array | PlaylistTask | PlayerScreen, MainScreen, AdsPollingTask |
m.global.categories | Array | PlaylistTask | MainScreen |
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:
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:
| Event | Log message |
|---|---|
| Channel dropped — missing number | event=parser_drop reason=missing_channel_number count=N |
| Channel dropped — duplicate number | event=parser_drop reason=duplicate_channel_number count=N |
| Fallback number assigned | event=parser_fallback reason=missing_channel_number count=N |
GTV_Warn entries and are always printed, regardless of build type.