# How to Extract and Validate Stream URLs from Markdown Tables in IPTV Data

> Learn how the Free-TV/IPTV script extracts and validates stream URLs from markdown tables. It parses URLs and metadata to create standardized M3U8 playlists.

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

---

**The [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script in the Free-TV/IPTV repository scans markdown tables for the `[>]` verification marker, parses the embedded URLs and metadata using the `Channel` class, and outputs standardized M3U8 playlists.**

The Free-TV/IPTV project maintains curated television channel data in markdown tables organized by country. The [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script processes these files to extract and validate stream URLs, ensuring only verified links reach the final playlists.

## Overview of the Playlist Generation Process

The extraction pipeline operates in five distinct phases: directory scanning, validation filtering, row parsing, M3U8 formatting, and file output. According to the source code in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), the script specifically targets files in the `lists/` directory and processes only rows explicitly marked as verified.

## Step-by-Step URL Extraction and Validation

### Scanning Markdown Files in the Lists Directory

The script initiates by walking the `lists/` directory (lines 38-44 of [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py)), identifying all `*.md` files. Each file represents a country or region containing tables with channel metadata and stream URLs.

### Validating Streams with the [>] Marker

Inside the file loop, the script validates each line by checking for the `"[>]"` marker (line 53). This marker denotes a **verified** stream URL. Rows containing `[x]` or lacking any marker are ignored entirely, ensuring only manually confirmed streams enter the playlist.

### Parsing Table Rows with the Channel Class

When a line contains `"[>]"`, the script instantiates a `Channel` object (line 55). The constructor processes the markdown line through several steps:

- The line is stripped and split on the pipe character (`|`) (line 99)
- The URL column (4th position) contains a markdown link like `[>] (http://example.com/stream.m3u8)`
- The raw URL is extracted by locating the opening and closing parentheses (lines 102-103)
- The logo URL is similarly parsed from the `<img src="...">` tag (lines 104-105)

### Generating M3U8 Entries

The `Channel.to_m3u_line()` method (lines 14-21) formats the extracted data into standard `#EXTINF` syntax. Optional fields like `tvg-chno`, `tvg-country`, and `tvg-id` are appended only when present in the source data.

## Implementation Example

The following example demonstrates how a markdown table row transforms into a playlist entry:

```python

# Example markdown line from lists/united_kingdom.md

line = '| 1 | BBC News | [>] (http://example.com/bbc.m3u8) | <img src="https://i.imgur.com/logo.png" /> | EPG123 |'

# Create Channel object (as implemented in make_playlist.py)

channel = Channel(group="United Kingdom", md_line=line, country_code="GB")

print(channel.url)   # → http://example.com/bbc.m3u8

print(channel.logo)  # → https://i.imgur.com/logo.png

```

The M3U8 generation follows this parsing:

```python
m3u_line = channel.to_m3u_line()
print(m3u_line)

# Output:

# #EXTINF:-1 tvg-name="BBC News" tvg-logo="https://i.imgur.com/logo.png" tvg-id="EPG123" tvg-country="GB",BBC News

# http://example.com/bbc.m3u8

```

## File Structure and Output

The script writes each validated entry to two locations: the global `playlist.m3u8` file and a country-specific playlist in the `playlists/` directory (lines 57-58). This dual-output approach allows users to access either the complete channel list or region-specific subsets.

## Summary

- The [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script processes markdown tables in the `lists/` directory to build IPTV playlists
- Only rows marked with `[>]` are considered valid; `[x]` or unmarked rows are discarded
- The `Channel` class parses pipe-delimited lines to extract URLs from markdown link syntax and logos from HTML img tags
- Validated entries are formatted as `#EXTINF` lines and written to both global and country-specific M3U8 files

## Frequently Asked Questions

### What does the [>] marker mean in Free-TV/IPTV markdown tables?

The `[>]` marker indicates that a stream URL has been manually verified and is currently active. According to the repository's README.md, this marker distinguishes working streams from those marked with `[x]` (inactive/broken) or unverified entries.

### How does the script handle invalid or unverified stream URLs?

The script ignores any table row that does not contain the `[>]` marker. During the scanning phase (line 53 of [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py)), lines containing `[x]` or lacking markers are skipped entirely, ensuring only validated URLs reach the final M3U8 output.

### Where are the generated playlists stored?

The script generates two types of output files: `playlist.m3u8` in the repository root containing all channels, and individual files in the `playlists/` directory named `playlist_[country_code].m3u8` containing region-specific channels.

### Can I extract URLs manually without running the full script?

While manual extraction is possible by parsing the markdown syntax yourself, the `Channel` class in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) provides the authoritative implementation for handling edge cases like nested parentheses in URLs and HTML-encoded logo attributes. For production use, running the complete script ensures consistent formatting and validation.