How make_playlist.py Parses Markdown Channel Data into M3U8 Format

The make_playlist.py script in the Free-TV/IPTV repository converts pipe-delimited Markdown tables into standards-compliant M3U8 playlists by scanning for rows marked with [>], parsing them into Channel objects, and serializing them into EXTINF entries with TVG metadata.

The Free-TV/IPTV repository uses a unique Markdown-based workflow to maintain IPTV channel lists. At the center of this pipeline sits make_playlist.py, which transforms human-readable Markdown tables into machine-ready M3U8 playlists. Understanding how this script parses markdown channel data into M3U8 format reveals a lightweight but robust approach to IPTV playlist generation.

Stage 1: Collecting Markdown Source Files

The script begins by discovering source files in the lists/ directory. According to the source code in make_playlist.py (lines 38-44), it iterates through every *.md file while explicitly excluding README.md from processing.

This collection phase establishes the group-title context for each channel based on the filename or an <h1> heading found within the Markdown file.

Stage 2: Parsing Channel Rows with the Channel Class

Once files are collected, the script processes each line individually to identify valid channel entries.

Identifying Valid Channel Rows

The parser filters for rows containing the specific token [>]. As implemented in lines 52-55 of make_playlist.py, only lines beginning with this marker are considered valid channel data:


# From make_playlist.py

if line.startswith("[>]"):
    # Process as channel row

Extracting Fields from Pipe-Delimited Data

When a valid row is found, it is passed to the Channel class constructor (lines 94-106). The Channel.__init__ method splits the line on the pipe character (|) and trims whitespace from each column:

class Channel:
    def __init__(self, group, md_line, country_code=""):
        # Split the pipe-separated line

        parts = md_line.strip().split("|")
        self.number = parts[1].strip()
        self.name = parts[2].strip()

Handling URL and Logo Extraction

The script handles two special formatting requirements in the Markdown source:

  1. URL extraction: The URL column is wrapped in parentheses in the Markdown table. The constructor strips these delimiters to isolate the raw stream URL (lines 103-104):
self.url = parts[3].strip()
self.url = self.url[self.url.find("(")+1:self.url.rfind(")")]
  1. Logo extraction: The logo column contains an HTML <img> tag. The constructor extracts the value of the src attribute (lines 104-106):
self.logo = parts[4].strip()
self.logo = self.logo[self.logo.find('src="')+5:self.logo.rfind('"')]
  1. Optional EPG identifier: If a sixth column exists, it is stored as the optional EPG identifier (lines 108-112):
self.epg = parts[5].strip() if len(parts) > 6 else None

Stage 3: Generating M3U8 Output

After parsing, the script serializes each Channel object into M3U8 format.

Constructing EXTINF Metadata

The Channel.to_m3u_line() method (lines 114-121) builds a single EXTINF line following the M3U8 specification:

  • Always adds tvg-name, tvg-logo, and group-title
  • Appends tvg-country if a country code was supplied via the COUNTRY_CODES map
  • Includes tvg-chno if a channel number is present
  • Adds tvg-id only when an EPG identifier exists

The resulting output is a two-line block: the EXTINF metadata line followed by the raw stream URL.

Writing Country-Specific and Master Playlists

The script aggregates channels into two destinations:

  1. playlist.m3u8 – A unified playlist containing every channel from all source files
  2. playlists/playlist_<country>.m3u8 – Individual country-specific playlists

Both playlist types begin with a header containing EPG URLs sourced from epglist.txt (lines 31-35), ensuring IPTV clients can retrieve program guide data.

Summary

  • make_playlist.py scans the lists/ directory for Markdown files (excluding README.md) to find channel source data
  • Valid channel rows are identified by the [>] marker and parsed by the Channel class constructor
  • The constructor splits pipe-delimited lines, extracts URLs from parentheses, parses logo src attributes from HTML tags, and handles optional EPG identifiers
  • The to_m3u_line() method generates standards-compliant EXTINF entries with TVG metadata attributes
  • Output is written to both a master playlist.m3u8 and per-country playlists in the playlists/ directory, each prefixed with EPG headers from epglist.txt

Frequently Asked Questions

What format does the Markdown source data use?

The source uses pipe-delimited tables where each channel occupies one row marked with [>]. A typical row contains: marker, channel number, name, URL in parentheses, logo image tag, and an optional EPG ID. This format balances human readability with structured data extraction.

How does the script handle optional EPG identifiers?

The Channel class checks if a sixth column exists after splitting the pipe-delimited line. If present, the value is stored in self.epg and later rendered as the tvg-id attribute in the M3U8 output. If absent, the attribute is omitted from the EXTINF line entirely.

Where does the group-title attribute come from?

The group-title attribute derives from the source Markdown filename or an <h1> heading within the file. This value is passed to the Channel constructor and included in every M3U8 entry, enabling IPTV clients to organize channels by country or category.

Can I run make_playlist.py standalone?

Yes. Executing python3 make_playlist.py from the repository root processes all Markdown files in lists/ and generates both the master playlist and country-specific variants. The script requires no external dependencies beyond standard Python libraries.

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 →