# Internal Architecture of the Channel Class in Free-TV/IPTV

> Explore the internal architecture of the Channel class in Free-TV/IPTV. Understand its three-part structure for converting Markdown to M3U playlist lines, handling metadata like EPG IDs and country codes.

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

---

**The `Channel` class in Free-TV/IPTV converts pipe-separated Markdown entries into structured M3U playlist lines through a three-part architecture: field extraction in `__init__`, M3U rendering via `to_m3u_line`, and eight normalized attributes that handle metadata including EPG IDs and country codes.**

The `Channel` class serves as the core data model in the Free-TV/IPTV repository, bridging raw Markdown channel listings and standards-compliant M3U playlists. Defined in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), this lightweight Python class parses IPTV stream metadata from table rows and generates the `#EXTINF` entries that media players consume. Understanding its internal architecture reveals how the project transforms simple text files into broadcast-ready playlists.

## Constructor and Field Parsing (`__init__`)

The constructor implements the primary parsing logic at lines 94-107 of [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), extracting structured data from a Markdown table row.

### Parsing the Markdown Row

The constructor receives three parameters: `group` (the category name), `md_line` (the raw Markdown string), and an optional `country_code` (defaulting to an empty string). The parsing process follows a strict sequence:

- **Raw string cleaning**: The input line is stripped of whitespace and split on the pipe character (`|`) into a `parts` array
- **Field extraction**: `parts[1]` becomes `number`, `parts[2]` becomes `name`, and `parts[3]` becomes the raw URL string
- **URL normalization**: The URL value is further processed to extract only the content inside parentheses, typically converting Markdown link syntax `[text](url)` or bare parentheses-wrapped URLs into clean streaming endpoints
- **Logo extraction**: The logo URL is parsed from an `<img>` tag by locating the `src="..."` attribute within `parts[4]`
- **EPG handling**: If a fifth column exists (`parts[5]`), it is stored as `self.epg`; otherwise the attribute is set to `None`

### Attribute Initialization Logic

The constructor performs two critical normalizations. First, `self.chno` mirrors `self.number` unless the number is empty or equals `"0"`, in which case it becomes `None`. This distinction allows the M3U generator to omit invalid channel numbers while preserving the original input. Second, the `country_code` parameter is stored directly as an instance attribute, enabling downstream metadata injection without re-parsing.

## M3U Rendering Engine (`to_m3u_line`)

Located at lines 114-121 of [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py), the `to_m3u_line` method transforms the parsed attributes into a valid M3U entry string. This method builds the `#EXTINF` line that precedes the actual stream URL in playlist files.

The method constructs the output through conditional attribute injection:

- **`tvg-name`**: Always present, derived from `self.name`
- **`tvg-logo`**: Always present, derived from `self.logo`
- **`tvg-id`**: Only included when `self.epg` is not `None`
- **`tvg-chno`**: Only included when `self.chno` is not `None`
- **`tvg-country`**: Only included when `self.country_code` is not empty
- **`group-title`**: Always present, derived from `self.group`

The resulting string follows the standard pattern:

```python
#EXTINF:-1 tvg-name="<name>" tvg-logo="<logo>" [tvg-id="<epg>"] [tvg-chno="<chno>"] [tvg-country="<code>"] group-title="<group>",<name>
<url>

```

## Data Model and Storage Schema

The `Channel` class maintains eight instance attributes that map directly to M3U metadata fields:

- **`group`**: Logical channel grouping (typically derived from the filename or section header)
- **`country_code`**: ISO-2 country code used for the `tvg-country` attribute
- **`number`**: Original channel number string from the Markdown column
- **`name`**: Human-readable channel name displayed in players
- **`url`**: Direct streaming URL extracted from Markdown link syntax
- **`logo`**: URL to the channel logo image (extracted from HTML `<img>` tags)
- **`chno`**: Normalized channel number (integer or `None` if invalid/zero)
- **`epg`**: Electronic Program Guide identifier (optional, `None` if not provided)

This schema ensures that optional metadata does not break the M3U generation while preserving all data necessary for modern IPTV players.

## Practical Implementation Examples

### Direct Instantiation

You can instantiate the `Channel` class directly to parse individual Markdown rows:

```python
from make_playlist import Channel

md_line = '| 101 | BBC One | https://example.com/stream.m3u8 | <img src="https://example.com/logo.png"> |'
channel = Channel(group='UK', md_line=md_line, country_code='GB')

print(channel.to_m3u_line())
print(channel.url)

```

**Output:**

```

#EXTINF:-1 tvg-name="BBC One" tvg-logo="https://example.com/logo.png" tvg-chno="101" tvg-country="GB" group-title="UK",BBC One
https://example.com/stream.m3u8

```

### Integration in Playlist Generation

The following pattern demonstrates how the class integrates into the main processing loop of [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py):

```python

# Inside the main playlist generation logic

for line in markup_file:
    if "[>]" not in line:
        continue  # Skip non-channel rows and headers

    
    channel = Channel(group, line, country_code)
    m3u_entry = channel.to_m3u_line()
    
    # Write to both global and country-specific playlists

    playlist.write(m3u_entry + '\n')
    playlist.write(channel.url + '\n')

```

This integration point shows how each Markdown entry becomes a two-line M3U entry (the `#EXTINF` metadata line followed by the URL).

## Summary

- The `Channel` class in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) serves as the primary data transformation layer between Markdown tables and M3U playlists
- The constructor (lines 94-107) implements robust parsing for pipe-separated values, HTML image tags, and optional EPG fields
- The `to_m3u_line` method (lines 114-121) generates standard-compliant `#EXTINF` entries with conditional metadata attributes
- Eight normalized attributes handle channel metadata, with special logic to distinguish between raw numbers and normalized channel numbers (`chno`)
- The architecture supports optional country codes and EPG identifiers without breaking M3U compatibility

## Frequently Asked Questions

### What file contains the Channel class definition?

The `Channel` class is defined entirely within [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) in the root of the Free-TV/IPTV repository. This single-file architecture keeps the data model co-located with the playlist generation logic, making the class responsible for both parsing and serialization.

### How does the Channel class handle missing EPG data?

When the Markdown row lacks a fifth column (the EPG field), the constructor sets `self.epg` to `None`. The `to_m3u_line` method checks this attribute before rendering and only includes the `tvg-id` parameter in the output when a valid EPG identifier is present, ensuring the generated M3U remains clean.

### What is the difference between the `number` and `chno` attributes?

The `number` attribute stores the raw string extracted from the Markdown table (e.g., `"101"` or `"0"`), while `chno` represents the normalized value used for M3U generation. If the raw number is empty or equals `"0"`, `chno` becomes `None`, which prevents invalid `tvg-chno` entries in the final playlist while preserving the original data for debugging.

### How does the Channel class extract URLs from Markdown links?

The constructor extracts the URL from `parts[3]` and applies a cleaning operation that isolates the content inside parentheses. This handles both bare URLs wrapped in parentheses and full Markdown link syntax `[text](url)`, ensuring only the actual streaming endpoint remains in `self.url` regardless of the input format.