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:
- 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(")")]
- Logo extraction: The logo column contains an HTML
<img>tag. The constructor extracts the value of thesrcattribute (lines 104-106):
self.logo = parts[4].strip()
self.logo = self.logo[self.logo.find('src="')+5:self.logo.rfind('"')]
- 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, andgroup-title - Appends
tvg-countryif a country code was supplied via theCOUNTRY_CODESmap - Includes
tvg-chnoif a channel number is present - Adds
tvg-idonly 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:
playlist.m3u8– A unified playlist containing every channel from all source filesplaylists/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.pyscans thelists/directory for Markdown files (excludingREADME.md) to find channel source data- Valid channel rows are identified by the
[>]marker and parsed by theChannelclass constructor - The constructor splits pipe-delimited lines, extracts URLs from parentheses, parses logo
srcattributes 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.m3u8and per-country playlists in theplaylists/directory, each prefixed with EPG headers fromepglist.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →