# What Determines tvg-chno Channel Numbering in Free-TV/IPTV M3U8 Output

> Discover how tvg-chno channel numbering is determined in Free-TV IPTV M3U8 output. Learn how attributes are extracted from source Markdown files to define channel order.

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

---

**The `tvg-chno` attribute is extracted directly from the second pipe-delimited column of the source Markdown files and is omitted when the value is empty or "0".**

The Free-TV/IPTV repository generates IPTV playlists from Markdown source files. The `tvg-chno` attribute that controls channel numbering in the final M3U8 output is determined by a specific parsing logic implemented in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py).

## How the Channel Number Is Parsed from Markdown

### Column Structure in Source Files

Each channel entry in the repository's Markdown files (located in `lists/*.md`) follows a pipe-delimited format. Inside `Channel.__init__` in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), the constructor splits each line on the pipe (`|`) character:

```python
parts = md_line.split("|")

```

The mapping is explicit:
- `parts[1]` → **number** (line 100)
- `parts[2]` → name (line 101)
- `parts[3]` → URL (lines 102-104)
- `parts[4]` → logo (lines 105-106)

### The Channel Constructor Logic

The constructor stores the parsed value in `self.number`, then applies a conditional check to determine if it should become a channel number tag:

```python
self.chno = self.number if self.number and self.number != "0" else None   # line 107

```

If the field is empty or equal to the string `"0"`, `self.chno` is set to `None` and no `tvg-chno` attribute is emitted in the final output.

## How tvg-chno Is Generated in the M3U8 Output

### The Conditional Output Logic

In `Channel.to_m3u_line` (line 16), the script checks the `self.chno` attribute before constructing the M3U8 line:

```python
chno = f' tvg-chno="{self.chno}"' if self.chno else ""

```

This string is then inserted into the `#EXTINF` line between the logo tag and the group title. The resulting attribute appears only when the source Markdown contains a valid, non-zero number in the second column.

## Practical Examples

### Example 1: Valid Channel Number

Source entry in [`lists/playlist_italy.md`](https://github.com/Free-TV/IPTV/blob/main/lists/playlist_italy.md):

```markdown
|101|Rai 1|http://example.com/rai1.m3u8|<img src="http://logo.com/rai1.png"/>

```

Generated M3U8 output:

```m3u
#EXTINF:-1 tvg-name="Rai 1" tvg-logo="http://logo.com/rai1.png" tvg-chno="101" group-title="Italy",Rai 1
http://example.com/rai1.m3u8

```

### Example 2: Empty Channel Number

Source entry with empty second column:

```markdown
||Sky Sports|http://example.com/sky.m3u8|<img src="http://logo.com/sky.png"/>

```

Generated output (note the missing `tvg-chno`):

```m3u
#EXTINF:-1 tvg-name="Sky Sports" tvg-logo="http://logo.com/sky.png" group-title="Italy",Sky Sports
http://example.com/sky.m3u8

```

### Example 3: Zero as Channel Number

Source entry with "0" in the number column:

```markdown
|0|TV5MONDE|http://example.com/tv5.m3u8|<img src="http://logo.com/tv5.png"/>

```

Generated output (treated as "no number"):

```m3u
#EXTINF:-1 tvg-name="TV5MONDE" tvg-logo="http://logo.com/tv5.png" group-title="France",TV5MONDE
http://example.com/tv5.m3u8

```

## Summary

- The **second pipe-delimited field** in the Markdown source files drives the `tvg-chno` attribute.
- **Empty strings** or the value **"0"** result in the omission of the `tvg-chno` tag entirely.
- The logic resides in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), specifically in the `Channel` class constructor and `to_m3u_line` method.
- Editing the channel number in `lists/*.md` files directly controls the numbering in the generated `playlists/*.m3u8` files.

## Frequently Asked Questions

### What happens if the channel number column is empty?

If the second column is empty, the `self.chno` attribute is set to `None` in the `Channel.__init__` method, and the `tvg-chno` attribute is completely omitted from the M3U8 output line.

### Why is "0" treated differently from other numbers?

The string `"0"` is explicitly filtered out in line 107 of [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (`self.number != "0"`), treating it as equivalent to an empty value. This allows maintainers to use "0" as a placeholder to intentionally suppress channel numbering for specific entries.

### Which file contains the logic for generating tvg-chno?

The core logic resides in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) in the root of the repository. The `Channel` class handles parsing in `__init__` (lines 100-107) and M3U8 generation in `to_m3u_line` (line 16).

### Can I manually edit channel numbers?

Yes. Since the `tvg-chno` value is taken directly from the second column of the Markdown files in `lists/`, you can modify these files to change channel numbering. The next run of the playlist generator will apply these changes to the output files.