How Channel Numbers Are Assigned and Used in the Free‑TV IPTV Playlist

Channel numbers in the Free‑TV IPTV playlist are defined manually in Markdown tables, parsed by make_playlist.py, and exported as the tvg-chno attribute in the generated M3U‑8 file, allowing IPTV clients to display channels in numeric order and support direct channel selection.

The Free‑TV/IPTV repository maintains a collaborative collection of public streaming channels organized by country. Understanding how channel numbers are assigned and used in the IPTV playlist requires examining the build pipeline that transforms human-editable Markdown tables into machine-readable M3U‑8 format.

How Channel Numbers Are Defined in Source Markdown

Channel numbers originate in the lists/*.md files that populate the repository. Each Markdown file represents a country or region and contains a table where the first column (#) holds the channel number.

The Markdown Table Structure

In the source files, the # column is treated as the authoritative channel identifier. For example, in lists/usa.md, the table defines channels with specific numeric assignments:

| #   | Channel        | Link  | Logo | EPG id |

|:---:|:--------------:|:-----:|:----:|:------:|
| 1   | Buzzr Ⓖ        | [>](https://buzzrota-ono.amagi.tv/playlist1080.m3u8) | <img src=".../Buzzr_logo.svg.png"/> | Buzzr.us |
| 2   | Retro TV       | [>](https://bcovlive-a.akamaihd.net/…) | <img src="https://i.imgur.com/PNTYOgg.png"/> | RetroTVEast.us |

Only rows containing the [>] link marker are processed into the final playlist, as noted in the repository documentation.

How make_playlist.py Processes Channel Numbers

The transformation from Markdown table to M3U entry occurs in make_playlist.py, which iterates through the Markdown files and extracts the channel metadata.

Parsing and Normalization

When processing each line of a Markdown table, the script splits the row on the pipe character (|). According to lines 99-101 of make_playlist.py, the second field (parts[1]) represents the channel number:


# From make_playlist.py (lines 99-101)

parts = line.split('|')
chno = parts[1].strip()

The script normalizes the extracted value at lines 107-108. If the field is empty or contains 0, the channel is treated as unnumbered:


# Normalization logic (lines 107-108)

if chno == '' or chno == '0':
    chno = None

Generating the tvg-chno Attribute

The Channel class encapsulates the channel data, and its to_m3u_line() method (lines 15-18) conditionally includes the tvg-chno attribute only when a number is present:


# Channel.to_m3u_line() builds the EXTINF line

if self.chno:
    m3u_line += f' tvg-chno="{self.chno}"'

Additionally, the script derives the tvg-country attribute from the filename (e.g., usa.md becomes US) and determines the group-title from either the filename or an <h1> tag in the Markdown file (lines 44-46).

The Role of tvg-chno in IPTV Clients

The tvg-chno attribute in the final playlist.m3u8 serves as a stable numeric identifier that IPTV clients utilize for:

  • Numeric ordering: Sorting the channel list according to the author's defined sequence rather than alphabetical order.
  • Direct access: Allowing users to jump to specific channels by entering the number (e.g., typing "001" to access the first channel).
  • Persistent identification: Maintaining a consistent reference even if the channel name or stream URL changes.

When a channel lacks a number (empty or 0 in the source), the generated M3U entry omits the tvg-chno attribute entirely:

#EXTINF:-1 tvg-name="Some Channel" tvg-logo="https://example.com/logo.png" tvg-country="US" group-title="USA",Some Channel
https://example.com/stream.m3u8

In contrast, a numbered channel includes the attribute as shown in this excerpt from the generated playlist:

#EXTINF:-1 tvg-name="Buzzr Ⓖ" tvg-logo="https://upload.wikimedia.org/.../Buzzr_logo.svg.png" tvg-chno="1" tvg-country="US" group-title="USA",Buzzr Ⓖ
https://buzzrota-ono.amagi.tv/playlist1080.m3u8

Summary

  • Channel numbers are defined manually in the # column of Markdown tables within lists/*.md.
  • The make_playlist.py script parses these tables, treating empty or 0 values as unnumbered channels.
  • Valid channel numbers are exported as the tvg-chno attribute in the M3U‑8 format via the Channel.to_m3u_line() method.
  • IPTV clients use tvg-chno to enable numeric channel surfing and ordered channel lists.
  • Unnumbered channels simply omit the tvg-chno attribute in the final playlist.

Frequently Asked Questions

What happens if a channel number is set to 0 or left empty?

According to the normalization logic in make_playlist.py (lines 107-108), if the # column contains 0 or is empty, the script sets chno = None. This results in the tvg-chno attribute being omitted from the final M3U entry, and the IPTV client treats the channel as unnumbered.

Where does the channel number appear in the final M3U file?

The channel number appears as the tvg-chno attribute within the #EXTINF line of the M3U‑8 playlist. For example, tvg-chno="1" indicates channel number 1. This is generated by the Channel.to_m3u_line() method in make_playlist.py only when a valid number exists.

Can I assign duplicate channel numbers in the same playlist?

While the make_playlist.py script does not enforce uniqueness constraints on the # column values, duplicate channel numbers may cause unpredictable behavior in IPTV clients. Players typically expect unique tvg-chno values for direct channel access features to function correctly.

How does the script handle the country code for grouped channels?

The script derives the tvg-country attribute from the Markdown filename (e.g., usa.md becomes US) and determines the group-title from either the filename or an <h1> tag present in the Markdown file (lines 44-46). This ensures channels are properly categorized by geographic region in the final playlist.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →