# How Channel Numbers (tvg-chno) Are Assigned During Playlist Generation in Free-TV/IPTV

> Discover how Free-TV/IPTV assigns channel numbers (tvg-chno) during playlist generation. Learn how non-empty, non-zero values are extracted and included in your M3U playlists.

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

---

**In the Free-TV/IPTV repository, the `tvg-chno` attribute is extracted from the second column of pipe-delimited markdown lines and emitted in the M3U playlist only when the value is non-empty and not "0".**

The Free-TV/IPTV project generates M3U playlists from markdown source files stored in the `lists/` directory. Understanding how the `tvg-chno` attribute is populated requires examining the parsing logic in the playlist generation script, specifically how the `Channel` class processes raw markdown data.

## Parsing Channel Numbers from Markdown Source

### Identifying Channel Entries with [>] Markers

The playlist generator scans each markdown file (`*.md`) for lines containing the `[>]` marker, which indicates a channel definition. These lines use pipe-separated columns (`|`) to separate metadata fields.

According to the source code in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 94–101), the parser splits each line and extracts the channel number from the second column:

```python
parts = md_line.split("|")
self.number = parts[1].strip()          # ← second column → channel number

```

The `self.number` field captures the raw string value from the markdown source, which later becomes the basis for the `tvg-chno` attribute.

## Validating and Storing the Channel Number

### The Validation Logic in the Channel Class

Before generating the M3U output, the `Channel` class constructor applies validation logic to determine whether a numeric identifier should be assigned. As implemented in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 107–108), the script stores the value in `self.chno` **only if it is non-empty and not equal to `"0"`**:

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

```

This conditional assignment ensures that:
- Empty channel number columns result in `None` (no attribute emitted)
- Explicit `"0"` values are treated as unset (no attribute emitted)
- Valid numeric strings are preserved for M3U generation

## Generating the M3U Output

### Conditional Attribute Emission

When building the final M3U playlist line, the formatter checks for the presence of `self.chno` before including the `tvg-chno` attribute. The source code at [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 15–17) uses a conditional f-string:

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

```

This approach means the `tvg-chno` tag appears only when a valid channel number exists, keeping the output clean and avoiding empty attributes.

## Practical Example

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

```markdown
[>] | 101 | NBC | http://example.com/nbc.m3u8 | <img src="http://example.com/nbc.png" />

```

This generates the following M3U entry:

```

#EXTINF:-1 tvg-name="NBC" tvg-logo="http://example.com/nbc.png" tvg-chno="101" group-title="Usa",NBC
http://example.com/nbc.m3u8

```

If the second column were empty or set to `0`, the output would omit the attribute entirely:

```

#EXTINF:-1 tvg-name="NBC" tvg-logo="http://example.com/nbc.png" group-title="Usa",NBC
http://example.com/nbc.m3u8

```

## Summary

- **Source location**: Channel numbers are defined in the second column of pipe-delimited markdown lines marked with `[>]` in `lists/*.md` files.
- **Validation**: The `Channel` class accepts only non-empty values that are not `"0"`, storing them in `self.chno`.
- **Output**: The `tvg-chno` attribute appears in the generated M3U only when `self.chno` is set, using the formatting logic in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py).
- **Key file**: [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) handles parsing (lines 94–101), validation (lines 107–108), and M3U generation (lines 15–17).

## Frequently Asked Questions

### What happens if the channel number is set to 0?

The value `"0"` is treated as a sentinel indicating "no channel number." According to the validation logic in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), when the second column contains `0`, `self.chno` is set to `None` and the `tvg-chno` attribute is omitted from the final M3U output.

### Can I leave the channel number column empty?

Yes. If the second column between the pipes is empty or contains only whitespace, `parts[1].strip()` returns an empty string. The validation check `if self.number` fails, resulting in `self.chno = None`, and the generated playlist line will not include the `tvg-chno` attribute.

### Which file controls the tvg-chno assignment logic?

The [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script in the repository root controls the entire assignment flow. It parses the markdown source (lines 94–101), validates the numeric value (lines 107–108), and conditionally formats the `tvg-chno` attribute in the M3U output (lines 15–17).

### How is the channel number positioned in the markdown syntax?

The channel number must occupy the second column in the pipe-delimited format. The parser expects the structure: `[>] | number | name | url | logo`, where the value between the first and second pipe characters becomes the `tvg-chno` value in the generated playlist.