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

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 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. According to the source code in make_playlist.py, the script reads this file and joins the URLs into a single attribute value.

#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 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:

#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:

#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, which processes markdown tables from files like lists/albania.md to construct Channel objects and emit formatted M3U8 content.

Generating the Header

The script aggregates EPG sources from epglist.txt to populate the x-tvg-url attribute:

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:

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 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 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →