# How Free-TV/IPTV Loads EPG URLs from epglist.txt into M3U8 Headers

> Learn how Free-TV/IPTV loads EPG URLs from epglist.txt to enrich your M3U8 headers. Discover the process and enhance your playlist experience.

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

---

**The Free-TV/IPTV generator reads EPG URLs from [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt), aggregates them into a comma-separated string, and injects them into the `x-tvg-url` attribute of the `#EXTM3U` header in every generated playlist.**

The Free-TV/IPTV repository automatically builds IPTV playlists by aggregating channel streams and Electronic Program Guide (EPG) metadata. Understanding how EPG URLs are loaded from [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) and added to M3U8 headers reveals the mechanism that ensures media players can retrieve program data for all channels.

## The EPG Loading Pipeline in make_playlist.py

According to the Free-TV/IPTV source code, the entire workflow is implemented in [[`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py)](https://github.com/Free-TV/IPTV/blob/master/make_playlist.py), where the script transforms raw EPG URLs into properly formatted M3U8 headers through a four-step sanitization and injection process.

### Step 1: Reading and Sanitizing epglist.txt (Lines 31-33)

First, the script opens [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) with UTF-8 encoding and creates a list containing each non-empty line with surrounding whitespace removed. This ensures clean URL processing while filtering out blank lines.

```python
with open(os.path.join(base_dir, "epglist.txt"), encoding='utf-8') as epg_file:
    epg_urls = [line.strip() for line in epg_file if line.strip()]

```

### Step 2: Aggregating URLs into a Comma-Separated String (Line 34)

The cleaned list of URLs is joined into a single string using `", "` as the delimiter. This produces a comma-separated value format required by the M3U8 specification for multiple EPG sources.

```python
processed_epg_list = ", ".join(epg_urls)

```

### Step 3: Constructing the M3U8 Header (Lines 34-35)

The aggregated string is embedded into the `x-tvg-url` attribute of the `#EXTM3U` directive using an f-string. This attribute tells media players where to fetch EPG data for the channels in the playlist.

```python
head_playlist = f'#EXTM3U x-tvg-url="{processed_epg_list}"\n'

```

### Step 4: Writing to the Master Playlist (Lines 36-38)

Finally, the complete header is written to `playlist.m3u8` before any channel entries are appended. This ensures the EPG metadata appears at the top of the file where media players expect to find it.

```python
with open(os.path.join(base_dir, "playlist.m3u8"), "w", encoding='utf-8') as playlist:
    playlist.write(head_playlist)

```

## Complete Implementation Example

Here is the complete workflow for generating the master playlist with EPG headers, as implemented in the repository:

```python
import os

base_dir = os.path.abspath(os.path.dirname(__file__))
epg_path = os.path.join(base_dir, "epglist.txt")

# Load and clean EPG URLs from epglist.txt

with open(epg_path, encoding="utf-8") as f:
    epg_urls = [line.strip() for line in f if line.strip()]

# Build the M3U8 header with x-tvg-url attribute

header = f'#EXTM3U x-tvg-url="{", ".join(epg_urls)}"\n'

# Write the header to the final playlist

with open(os.path.join(base_dir, "playlist.m3u8"), "w", encoding="utf-8") as out:
    out.write(header)
    # Channel entries would be appended here...

```

## Resulting M3U8 Header Format

The generated `playlist.m3u8` file begins with a single header line containing all EPG sources. The output follows this exact structure:

```

#EXTM3U x-tvg-url="https://epgshare01.online/epgshare01/epg_ripper_AL1.xml.gz, https://epgshare01.online/epgshare01/epg_ripper_ALJAZEERA1.xml.gz, https://epgshare01.online/epgshare01/epg_ripper_ALL_SOURCES1.xml.gz, ..."

```

All subsequent `#EXTINF` channel entries and stream URLs are appended after this header, ensuring media players parse the EPG metadata before processing individual channels.

## Summary

- **[`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt)** serves as the canonical source for all Electronic Program Guide URLs in the Free-TV/IPTV ecosystem.
- The **[`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py)** script processes this file at lines 31-33, stripping whitespace and filtering empty lines to create a clean URL list.
- URLs are aggregated into a comma-separated string at line 34, then injected into the **`x-tvg-url`** attribute of the `#EXTM3U` header.
- Every generated playlist file begins with this EPG-enriched header, ensuring compatibility with media players that support XMLTV program guides.

## Frequently Asked Questions

### What is the purpose of the x-tvg-url attribute in M3U8 files?

The `x-tvg-url` attribute is a non-standard but widely supported extension that specifies the location of XMLTV-formatted EPG data. Media players like VLC, Kodi, and IPTV apps read this attribute to download program schedules and display TV guide information alongside live channels.

### How does the script handle empty lines or whitespace in epglist.txt?

The list comprehension `[line.strip() for line in epg_file if line.strip()]` first checks if a line contains non-whitespace characters with `if line.strip()`, then removes leading and trailing whitespace with `line.strip()`. This ensures no empty entries or malformed URLs reach the final playlist header.

### Can I use multiple EPG sources in a single playlist?

Yes. The script explicitly supports multiple EPG sources by joining all URLs with `", "` (comma-space) into a single string. According to the M3U8 specification used by most IPTV players, multiple XMLTV sources can be listed in the `x-tvg-url` attribute as comma-separated values, allowing the player to fetch program data from multiple repositories.

### Where is the generated playlist.m3u8 file saved?

The script writes `playlist.m3u8` to the repository's root directory, specifically at `os.path.join(base_dir, "playlist.m3u8")`, where `base_dir` is the directory containing [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py). This master playlist contains all aggregated channels from the repository's country-specific subdirectories.