# Complete M3U8 File Structure and EXTINF Attributes in Free-TV IPTV

> Explore the complete M3U8 file structure generated by Free-TV IPTV. Understand EXTINF attributes, EPG URLs, and channel entries for seamless IPTV playlist creation.

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

---

**The Free-TV IPTV repository generates HLS-style M3U8 playlists with a canonical header containing aggregated EPG URLs, followed by channel entries defined by `#EXTINF` lines that carry specific `tvg-*` attributes and end with a display name and stream URL.**

The Free-TV/IPTV project automates the creation of IPTV playlists by processing markdown files in the `lists/` directory using the [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script. Understanding the complete M3U8 file structure and EXTINF attributes is essential for developers integrating these streams or customizing the playlist generation. Each generated file follows the canonical M3U8 format specification while incorporating project-specific metadata extensions.

## M3U8 Header Structure

Every generated playlist begins with a single header line that declares the file format and provides electronic program guide (EPG) configuration.

The header starts with `#EXTM3U` and includes the `x-tvg-url` attribute, which contains a comma-separated list of all EPG XML sources defined in [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt). According to the source code in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), the script reads this file and joins the URLs into a single attribute value.

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

```

## Channel Entry Structure

Each channel in the playlist is represented by a **pair of lines**: an `#EXTINF` metadata line followed by the actual stream URL.

### EXTINF Attribute Specifications

The `#EXTINF` line is constructed by the `Channel.to_m3u_line()` method in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) and includes the following attributes in a fixed order:

- **tvg-name**: The full channel name extracted from the markdown table (stored in `self.name`)
- **tvg-logo**: Absolute URL of the channel logo parsed from the `<img src="…">` tag in the markdown (stored in `self.logo`)
- **tvg-id**: Optional EPG identifier present only when the markdown row supplies a tv-guide ID column (stored in `self.epg`)
- **tvg-chno**: Channel number retained when the markdown *Number* column is non-zero (stored in `self.chno`)
- **tvg-country**: ISO-3166-1-alpha-2 country code looked up via the `COUNTRY_CODES` dictionary using the filename (stored in `self.country_code`)
- **group-title**: Human-readable group name derived from the filename, such as `Albania` or `Argentina` (stored in `self.group`)

Following the attributes, a comma separates the metadata from the display name that appears in IPTV players.

### Special Display Name Symbols

The display name portion after the comma may contain special Unicode symbols that indicate stream characteristics:

- **Ⓢ** – Indicates an SD (Standard Definition) channel
- **Ⓖ** – Indicates the stream is geo-IP blocked
- **Ⓨ** – Indicates a YouTube live channel

Example entry with full attributes:

```text
#EXTINF:-1 tvg-name="Kanali 7 Ⓢ" tvg-logo="https://i.imgur.com/rL2v9pM.png" tvg-id="Kanali7.al" tvg-country="AL" group-title="Albania",Kanali 7 Ⓢ
https://fe.tring.al/delta/105/out/u/1200_1.m3u8

```

When no EPG ID is provided, the `tvg-id` attribute is omitted entirely:

```text
#EXTINF:-1 tvg-name="ABC News Albania Ⓣ" tvg-logo="https://i.imgur.com/aObcudw.png" tvg-country="AL" group-title="Albania",ABC News Albania Ⓣ
https://www.twitch.tv/abcnewsal

```

## Code Implementation Details

The playlist generation logic resides in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), which processes markdown tables from files like [`lists/albania.md`](https://github.com/Free-TV/IPTV/blob/main/lists/albania.md) to construct `Channel` objects and emit formatted M3U8 content.

### Generating the Header

The script aggregates EPG sources from [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) to populate the `x-tvg-url` attribute:

```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()]
processed_epg_list = ", ".join(epg_urls)
head_playlist = f'#EXTM3U x-tvg-url="{processed_epg_list}"\n'

```

### Building EXTINF Lines

The `Channel.to_m3u_line()` method conditionally includes attributes based on data availability:

```python
def to_m3u_line(self):
    country = f' tvg-country="{self.country_code}"' if self.country_code else ""
    chno = f' tvg-chno="{self.chno}"' if self.chno else ""
    if self.epg is None:
        return (f'#EXTINF:-1 tvg-name="{self.name}" tvg-logo="{self.logo}"{chno}{country} '
                f'group-title="{self.group}",{self.name}\n{self.url}')
    else:
        return (f'#EXTINF:-1 tvg-name="{self.name}" tvg-logo="{self.logo}" tvg-id="{self.epg}"'
                f'{chno}{country} group-title="{self.group}",{self.name}\n{self.url}')

```

## File Termination

The playlist terminates after the last URL line without requiring an explicit `#EXT-X-ENDLIST` tag. Because these files represent static channel lists rather than dynamic HLS media playlists, the absence of the end tag complies with standard M3U8 conventions for simple media playlists.

## Summary

- The **M3U8 file structure** in Free-TV IPTV consists of a header with aggregated EPG URLs followed by paired `#EXTINF` and URL lines for each channel.
- **EXTINF attributes** include `tvg-name`, `tvg-logo`, `tvg-id`, `tvg-chno`, `tvg-country`, and `group-title`, with conditional inclusion based on data availability in the source markdown.
- The [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script processes `lists/*.md` files to generate both the global `playlist.m3u8` and country-specific files in `playlists/playlist_*.m3u8`.
- Special symbols in display names (**Ⓢ**, **Ⓖ**, **Ⓨ**) communicate stream quality and access restrictions to end users.
- No `#EXT-X-ENDLIST` tag is appended because these are static channel lists, not live HLS event playlists.

## Frequently Asked Questions

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

The `x-tvg-url` attribute provides a comma-separated list of XMLTV EPG source URLs that IPTV players can use to fetch program guide data. The script populates this by reading [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) and joining all non-empty lines into a single attribute value attached to the `#EXTM3U` header line.

### How does the Channel.to_m3u_line() method handle missing EPG IDs?

When `self.epg` is `None`, the method omits the `tvg-id` attribute entirely from the generated string. This conditional logic ensures that only channels with explicit tv-guide IDs in the markdown source receive the `tvg-id` metadata, keeping the output clean for channels without EPG mappings.

### What do the special symbols Ⓢ, Ⓖ, and Ⓨ indicate in channel names?

These Unicode symbols appended to channel names indicate specific stream characteristics: **Ⓢ** marks SD (Standard Definition) streams, **Ⓖ** indicates geo-IP blocked content, and **Ⓨ** identifies YouTube live channels. These visual markers help users quickly identify stream properties in their IPTV player interfaces.

### Why is there no #EXT-X-ENDLIST tag in the generated playlists?

The generated M3U8 files serve as static channel lists rather than dynamic HLS media playlists. According to the M3U8 specification, `#EXT-X-ENDLIST` is optional for static playlists and is typically only required for VOD or event playlists to signal the end of a variable-length stream. The Free-TV IPTV outputs terminate naturally after the final URL entry.