# How Are EPG IDs (tvg-id) Assigned for Electronic Program Guide Integration in the Free-TV/IPTV Repository

> Learn how Free-TV/IPTV assigns EPG IDs (tvg-id) by extracting them from markdown channel tables and parsing them into M3U playlists with the Channel class.

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

---

**In the Free-TV/IPTV repository, EPG IDs are extracted from the sixth column of pipe-separated channel tables in markdown files, then parsed by the `Channel` class in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) and injected as `tvg-id` attributes into generated M3U playlists.**

The Free-TV/IPTV repository generates standard M3U playlists from curated markdown channel lists stored in the `lists/` directory. For electronic program guide (EPG) integration to function, each channel must map to a specific identifier recognized by XMLTV providers. This mapping is controlled through a deterministic parsing pipeline that converts plain-text table entries into IPTV-standard metadata attributes.

## Channel Definition Format in Markdown Lists

Channel data is stored in markdown files (e.g., [`lists/portugal.md`](https://github.com/Free-TV/IPTV/blob/main/lists/portugal.md)) using a pipe-separated table format. Each row represents a single channel and follows a strict six-column structure:

```markdown
| <number> | <channel name> | <stream URL> | <logo HTML> | <EPG ID> |

```

The **sixth column** (index 5 when split) contains the optional EPG identifier. This value must correspond exactly to the `channel id` used by the XMLTV provider. If the column is omitted or empty, the channel will be generated without EPG linking.

## Parsing EPG IDs in make_playlist.py

The `Channel` class defined in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) handles the extraction logic during the playlist build process.

### The Channel.__init__ Method

As implemented in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 94–102), the constructor splits each markdown line on the pipe character (`|`) and assigns the sixth element to the `epg` instance variable:

```python

# make_playlist.py, lines 94-102

parts = md_line.split("|")
...
if len(parts) > 6:
    self.epg = parts[5].strip()
else:
    self.epg = None

```

The condition `len(parts) > 6` ensures the line contains enough fields to include an EPG ID, accounting for the leading empty string and potential trailing pipe character inherent to markdown table syntax.

## Generating M3U Output with tvg-id Attributes

When rendering the final playlist, the `Channel.to_m3u_line` method conditionally includes the `tvg-id` attribute only when `self.epg` is populated.

### Conditional Attribute Injection

According to lines 118–120 in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), the method constructs the `#EXTINF` line using an f-string that omits `tvg-id` when the value is `None`:

```python

# make_playlist.py, lines 118-120

if self.epg is None:
    return (f'#EXTINF:-1 tvg-name="{self.name}" tvg-logo="{self.logo}"{chno}{country} 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}"{chno}{country} group-title="{self.group}",{self.name}\n{self.url}')

```

This ensures that only valid identifiers are exposed to IPTV clients, preventing empty attribute errors in players like VLC, Kodi, or TVHeadend.

## EPG Source Configuration via epglist.txt

The repository links playlists to external guide data through the [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) file. During playlist generation, [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) reads this file (lines 31–34) and injects the URLs as the `x-tvg-url` header:

```python

# make_playlist.py, lines 31-34

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'

```

The `tvg-id` values assigned to individual channels must match the identifiers used by the XMLTV services listed in this file for schedule data to populate correctly.

## Complete Workflow Example

Consider a channel defined in [`lists/united_kingdom.md`](https://github.com/Free-TV/IPTV/blob/main/lists/united_kingdom.md):

```markdown
| 101 | BBC News | (http://example.com/stream.m3u8) | <img src="http://example.com/logo.png"> | bbcnews

```

The `Channel` parser extracts `bbcnews` as `self.epg`. When `to_m3u_line` is called, it generates:

```text
#EXTINF:-1 tvg-name="BBC News" tvg-logo="http://example.com/logo.png" tvg-id="bbcnews" group-title="United Kingdom",BBC News
http://example.com/stream.m3u8

```

The resulting M3U file begins with the aggregated EPG sources:

```text
#EXTM3U x-tvg-url="http://epg.provider1.com/xmltv.xml, http://epg.provider2.com/xmltv.xml"

```

When a compatible player loads this playlist, it uses the `tvg-id="bbcnews"` value to query the specified EPG endpoints for scheduling information.

## Summary

- **Source Location**: EPG IDs originate from the sixth column of pipe-separated tables in markdown files within the `lists/` directory.
- **Parsing Logic**: The `Channel.__init__` method in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 94–102) extracts the identifier from `parts[5]` when the field exists.
- **M3U Generation**: The `Channel.to_m3u_line` method (lines 118–120) injects the value as the `tvg-id` attribute only when present.
- **EPG Linking**: The [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) file defines XMLTV source URLs that [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 31–34) embeds as the `x-tvg-url` header in generated playlists.

## Frequently Asked Questions

### What happens if a channel does not have an EPG ID defined?

If the sixth column is missing or the line has fewer than seven pipe-separated elements, `self.epg` remains `None`. The `to_m3u_line` method consequently omits the `tvg-id` attribute from the `#EXTINF` line, and the channel will not have program guide data in IPTV clients.

### How do I verify that an EPG ID matches the provider's data?

The string in the sixth column must exactly match the `channel id` attribute or internal identifier used by the XMLTV feeds listed in [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt). Consult your EPG provider's XMLTV file to confirm the correct identifier formatting, as mismatches will result in missing guide data.

### Where are the EPG source URLs configured in the repository?

The root-level file [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) contains the URLs of XMLTV providers. The script reads these entries (lines 31–34 of [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py)) and combines them into a comma-separated list assigned to the `x-tvg-url` attribute in the M3U header.

### Can multiple EPG providers be used for a single playlist?

Yes. The build script aggregates all non-empty lines from [`epglist.txt`](https://github.com/Free-TV/IPTV/blob/main/epglist.txt) into a single `x-tvg-url` value containing comma-separated URLs. IPTV clients supporting multiple EPG sources can query all listed endpoints to resolve program data for channels based on their assigned `tvg-id` values.