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:
PATH_PLAYLIST_TPL : "/auth/{user}/{pass}/playlist/m3u8/hls"
At runtime, PlaylistTask builds the URL with the current credentials URL-encoded:
encUser = GTV_UrlEncode(username)
encPass = GTV_UrlEncode(password)
url = server + "/auth/" + encUser + "/" + encPass + "/playlist/m3u8/hls"
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:
c = AppConstants()
while retries < c.RETRY_MAX
    resp = GTV_HttpGet(url, c.TIMEOUT_HTTP)
    if resp.ok then exit while
    if resp.code <> -1  ' non-network error — stop
        exit while
    end if
    ' advance to next server for network errors
    retries = retries + 1
end while
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:
parsed = GTV_ParseM3U(resp.body)
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.global.channelList = parsed.channels
m.global.categories  = parsed.categories
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:
#EXTINF:-1 tvg-id="123" tvg-name="Canal Example" tvg-logo="https://..." group-title="Noticias" channel-number="12",Canal Example
https://stream.example.com/live/abc123def456/playlist.m3u8

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.
PARSER_STRICT_CHANNEL_NUMBER : true
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:
FieldSourceDescription
idtvg-idProvider channel ID
nametvg-name or comma fieldDisplay name
logotvg-logoLogo URL
groupgroup-titleCategory name (defaults to General)
numberchannel-numberInteger channel number
urlNext non-comment lineNormalized HLS stream URL
streamIdExtracted from URL pathHex stream identifier (if detectable)
contentIdstreamId or numberUsed for ad targeting

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

FieldTypeSet byConsumed by
m.global.channelListArrayPlaylistTaskPlayerScreen, MainScreen, AdsPollingTask
m.global.categoriesArrayPlaylistTaskMainScreen

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:
sub init()
    m.channels = m.global.channelList
    ...
end sub
When AdsPollingTask needs to resolve the current stream position, it also reads from m.global.channelList:
function GTV_GetCurrentStreamPosition() as String
    idx = m.global.currentChannelIndex
    channels = m.global.channelList
    if channels <> invalid and idx >= 0 and idx < channels.Count()
        ch = channels[idx]
        if ch <> invalid and ch.number <> invalid
            return GTV_AdsSafeTrim(ch.number.ToStr())
        end if
    end if
    return ""
end function

Parse diagnostics

PlaylistTask logs a warning for each category of dropped or fallback-assigned channel:
EventLog message
Channel dropped — missing numberevent=parser_drop reason=missing_channel_number count=N
Channel dropped — duplicate numberevent=parser_drop reason=duplicate_channel_number count=N
Fallback number assignedevent=parser_fallback reason=missing_channel_number count=N
These warnings appear as GTV_Warn entries and are always printed, regardless of build type.