# How make_playlist.py Parses Markdown to Create M3U8 Playlists in Free-TV/IPTV

> Discover how make_playlist.py transforms Markdown into M3U8 playlists for Free-TV/IPTV. Learn its parsing logic for channel data and metadata generation.

- Repository: [Free TV/IPTV](https://github.com/Free-TV/IPTV)
- Tags: how-to-guide
- Published: 2026-06-17

---

**The [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script converts Markdown tables containing TV channel data into standards-compliant M3U8 playlists by scanning for `[>]` markers, parsing pipe-delimited rows into `Channel` objects, and serializing them into EXTINF entries with metadata attributes.**

The **Free-TV/IPTV** repository maintains human-readable channel lists in Markdown format that require conversion into machine-readable M3U8 playlists for IPTV clients. The [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) utility automates this transformation by parsing structured Markdown tables and extracting stream URLs, logos, and metadata to generate playable playlists.

## Overview of the Markdown-to-M3U8 Pipeline

The parsing process operates through three distinct stages defined in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py). First, the script discovers source files in the repository's `lists/` directory. Next, it extracts channel data from Markdown table rows marked with specific tokens. Finally, it serializes this data into the M3U8 format using the `Channel` class's conversion methods.

## Stage 1: Collecting Source Markdown Files

According to the source code in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 38-44), the script begins by scanning the `lists/` directory for all `*.md` files while explicitly excluding [`README.md`](https://github.com/Free-TV/IPTV/blob/main/README.md). Each discovered file represents a country or regional group containing channel definitions in Markdown table format.

## Stage 2: Extracting Channel Data from Markdown Tables

### Identifying Channel Rows with the [>] Marker

The parser identifies valid channel entries by searching for lines containing the `[>]` token (lines 52-55 in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py)). This marker distinguishes actual channel data rows from header rows or descriptive text within the Markdown tables.

### Parsing the Pipe-Delimited Format

Once identified, each channel row is passed to the `Channel` class constructor defined in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 94-106). The constructor splits the line on the pipe character (`|`) and trims whitespace from each column:

```python
class Channel:
    def __init__(self, group, md_line, country_code=""):
        parts = md_line.strip().split("|")
        self.number = parts[1].strip()
        self.name = parts[2].strip()

```

### Extracting URLs and Logos

The URL column contains the stream address wrapped in parentheses, so the constructor strips these delimiters to isolate the raw URL (lines 103-104). For the logo column, which contains an HTML `<img>` tag, the script extracts the `src` attribute value:

```python
        # Strip surrounding parentheses from URL

        self.url = parts[3].strip()
        self.url = self.url[self.url.find("(")+1:self.url.rfind(")")]
        
        # Extract src attribute from img tag

        self.logo = parts[4].strip()
        self.logo = self.logo[self.logo.find('src="')+5:self.logo.rfind('"')]

```

If a sixth column exists, the parser stores it as an optional EPG identifier (lines 108-112).

## Stage 3: Generating M3U8 Entries

The `Channel.to_m3u_line()` method (lines 114-121) constructs standards-compliant M3U8 entries. Each entry begins with an `#EXTINF` line containing metadata attributes followed by the raw stream URL.

The method always includes `tvg-name`, `tvg-logo`, and `group-title` attributes. It conditionally appends `tvg-country` (when a country code exists in the `COUNTRY_CODES` map), `tvg-chno` (for channel numbers), and `tvg-id` (when EPG data is present).

A complete M3U8 entry output by the script follows this structure:

```text
#EXTINF:-1 tvg-name="NBC" tvg-logo="https://example.com/nbc.png" tvg-id="nbcn" tvg-chno="101" tvg-country="US" group-title="Usa",NBC
http://example.com/nbc.m3u8

```

## Aggregating Playlists and Writing Output

After processing all channels, [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) writes the results to two locations. The master `playlist.m3u8` file contains every channel discovered across all source files. Additionally, the script generates country-specific playlists in the `playlists/` directory using the naming convention `playlist_<country>.m3u8`.

Both output files begin with a header section that includes EPG URLs sourced from [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) (lines 31-35), enabling electronic program guide functionality for compatible IPTV clients.

## Summary

- [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) scans the `lists/` directory for Markdown files, excluding [`README.md`](https://github.com/Free-TV/IPTV/blob/main/README.md).
- Channel rows are identified by the `[>]` marker and parsed using the `Channel` class constructor.
- The parser extracts stream URLs from parentheses, logos from `<img>` tags, and optional EPG identifiers from the sixth column.
- The `to_m3u_line()` method generates `#EXTINF` entries with metadata attributes including `tvg-name`, `tvg-logo`, and `group-title`.
- Output is written to both a master `playlist.m3u8` and country-specific files in `playlists/`, prefixed with EPG URLs from [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt).

## Frequently Asked Questions

### What format does the markdown channel data use?

The markdown files in `lists/` use pipe-delimited tables where each channel occupies one row. Valid rows begin with the `[>]` marker followed by columns for channel number, name, URL (in parentheses), logo (as an HTML `<img>` tag), and an optional EPG identifier.

### How does make_playlist.py handle the channel logo URLs?

Inside `Channel.__init__` in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), the logo column containing an HTML `<img>` tag is processed by extracting the value of the `src` attribute using string slicing operations. This isolates the raw image URL from the surrounding HTML markup.

### What is the purpose of the [>] marker in the markdown files?

The `[>]` token serves as a row identifier that distinguishes actual channel data from header rows or comments in the Markdown tables. The parser specifically checks for this substring (lines 52-55) to determine which lines to process as channel entries.

### Where does make_playlist.py output the generated playlists?

The script generates two types of output files: a master `playlist.m3u8` in the repository root containing all channels, and individual country-specific files named `playlists/playlist_<country>.m3u8` that contain only channels from that specific region.