# How are channel logos resolved and embedded in M3U8 metadata for IPTV?

> Learn how Free-TV/IPTV embeds channel logos in M3U8 metadata. Discover the simple extraction from HTML img tags and direct insertion as tvg-logo attributes.

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

---

**Channel logos in the Free-TV/IPTV project are resolved by extracting the `src` attribute from HTML `<img>` tags embedded in Markdown tables and embedded directly into the M3U8 playlist as the `tvg-logo` attribute without downloading or validating the images.**

The **Free-TV/IPTV** repository generates standardized IPTV playlists from human-readable Markdown tables. Understanding how channel logos are resolved and embedded in M3U8 metadata requires examining the [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script, which transforms these tables into structured playlist files that IPTV players can consume.

## How Logo Resolution Works in IPTV Playlist Generation

The playlist generation process involves three distinct steps to handle channel logos: parsing the Markdown source, extracting the image URL, and injecting it into the M3U8 metadata.

### Parsing the Markdown Source

Each channel entry originates from a Markdown file stored in the `lists/` directory (e.g., [`usa.md`](https://github.com/Free-TV/IPTV/blob/main/usa.md), [`spain.md`](https://github.com/Free-TV/IPTV/blob/main/spain.md)). These files contain pipe-separated tables where the **Logo** column holds an HTML `<img>` tag.

A typical table row looks like this:

```markdown
| 1 | Buzzr Ⓖ | [>](https://buzzrota-ono.amagi.tv/playlist1080.m3u8) | <img height="20" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Buzzr_logo.svg/768px-Buzzr_logo.svg.png"/> | Buzzr.us

```

The `Channel` class in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) processes these rows by splitting the line on pipe characters (`|`). The logo data resides in the fourth field (`parts[4]`), which contains the raw HTML tag.

### Extracting the Logo URL

The script performs **string slicing** to extract the URL from the HTML tag rather than using an HTML parser. According to the source code in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 94-106), this implementation uses find operations to locate the `src` attribute:

```python

# From make_playlist.py - Channel class initialization

self.logo = parts[4].strip()

# Extract URL from <img src="..."> tag

self.logo = self.logo[self.logo.find('src="')+5:self.logo.rfind('"')]

```

This approach extracts the URL between the quotes of the `src` attribute, converting `<img height="20" src="https://example.com/logo.png"/>` into `https://example.com/logo.png`.

### Embedding into M3U8 Metadata

Once extracted, the URL is embedded into the **M3U8** playlist using the `tvg-logo` attribute. In [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 115-121), the `to_m3u_line()` method constructs the `#EXTINF` directive:

```python

# Constructing the EXTINF line with tvg-logo attribute

f'#EXTINF:-1 tvg-name="{self.name}" tvg-logo="{self.logo}" tvg-chno="{self.number}" group-title="{self.group}",{self.name}'

```

The resulting output in the playlist file appears as:

```text
#EXTINF:-1 tvg-name="Buzzr Ⓖ" tvg-logo="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Buzzr_logo.svg/768px-Buzzr_logo.svg.png" tvg-chno="1" group-title="USA",Buzzr Ⓖ
https://buzzrota-ono.amagi.tv/playlist1080.m3u8

```

## Code Implementation Details

The logo resolution mechanism relies on simple string manipulation rather than external HTTP requests or image processing libraries.

### Extracting Logo URLs from Markdown

Here is the extraction logic as implemented in the repository:

```python
def extract_logo_from_markdown(md_line):
    """Extract logo URL from a Markdown table row"""
    parts = md_line.split('|')
    raw_logo = parts[4].strip()  # Get the <img> tag field

    
    # Extract src attribute using string slicing

    start = raw_logo.find('src="') + 5
    end = raw_logo.rfind('"')
    logo_url = raw_logo[start:end]
    
    return logo_url

# Example usage

md_line = '| 1 | Buzzr Ⓖ | [>](https://buzzrota-ono.amagi.tv/playlist1080.m3u8) | <img height="20" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Buzzr_logo.svg/768px-Buzzr_logo.svg.png"/> | Buzzr.us'
print(extract_logo_from_markdown(md_line))

# Output: https://upload.wikimedia.org/wikipedia/commons/thumb/d/d6/Buzzr_logo.svg/768px-Buzzr_logo.svg.png

```

### Building M3U8 Entries with Logo Metadata

The following function demonstrates how the final M3U8 entry is constructed with the `tvg-logo` attribute:

```python
def to_m3u_line(name, url, logo, chno=None, group=''):
    """Build an M3U8 entry with embedded logo metadata"""
    parts = [f'#EXTINF:-1 tvg-name="{name}" tvg-logo="{logo}"']
    
    if chno:
        parts.append(f' tvg-chno="{chno}"')
    parts.append(f' group-title="{group}",{name}')
    
    header = ''.join(parts)
    return f'{header}\n{url}'

```

## Repository Structure and Key Files

The logo resolution workflow depends on these specific files within the Free-TV/IPTV repository:

- **[`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py)** – The core Python script that parses Markdown tables, extracts logo URLs using string slicing, and generates the final M3U8 playlists. The critical logic resides in the `Channel` class (lines 94-106 for parsing, lines 115-121 for M3U8 generation).

- **`lists/*.md`** – Country-specific Markdown files containing channel definitions. Each row includes an HTML `<img>` tag in the Logo column pointing to external image URLs (e.g., Imgur, Wikimedia Commons).

- **`playlists/`** – The output directory containing generated `.m3u8` files. These files contain the processed `tvg-logo` attributes that IPTV players use to display channel thumbnails.

## Summary

- **Logo resolution** in the Free-TV/IPTV project extracts image URLs from HTML `<img>` tags embedded in Markdown table rows using string slicing operations.
- The extraction occurs in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) (lines 94-106) by parsing the `src` attribute from the fourth column of pipe-separated Markdown tables.
- **No image validation or downloading** occurs during playlist generation; the script only copies the external URL into the M3U8 metadata.
- The `tvg-logo` attribute is injected into `#EXTINF` directives (lines 115-121 of [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py)), making logos available to any IPTV player that supports standard M3U8 attributes.
- This approach works uniformly across all country-specific lists in the `lists/` directory because they follow the same table schema.

## Frequently Asked Questions

### Why does the script use string slicing instead of an HTML parser to extract logos?

The script uses string slicing (`find('src="')` and `rfind('"')`) because the Markdown table format is highly predictable and consistent across all list files. This approach avoids dependencies on external HTML parsing libraries like BeautifulSoup, keeping the [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) script lightweight and self-contained. The method reliably extracts the URL from the `<img src="...">` pattern present in every Logo column entry.

### Are logo images downloaded or validated during playlist generation?

No. The **Free-TV/IPTV** playlist generator does not download, cache, or validate the image files. It performs a simple text extraction of the URL from the Markdown source and inserts it directly into the `tvg-logo` attribute. The actual image fetching and rendering is handled by the IPTV player software (such as VLC, Kodi, or IPTV Smarters) when it consumes the generated M3U8 file.

### Can I use relative URLs or local image paths for channel logos?

The current implementation in [`make_playlist.py`](https://github.com/Free-TV/IPTV/blob/main/make_playlist.py) extracts whatever URL is present in the `src` attribute of the `<img>` tag. While the repository typically uses absolute URLs (HTTPS links to Imgur or Wikimedia), the string extraction logic would preserve a relative path if one were provided in the Markdown table. However, most IPTV players require absolute URLs to resolve images correctly, so absolute paths are recommended for compatibility.

### Where is the `tvg-logo` attribute defined in the M3U8 specification?

The `tvg-logo` attribute follows the **de facto** IPTV standard used by popular players like VLC, Perfect Player, and IPTV Smarters. While M3U8 files technically follow the HTTP Live Streaming specification, the `tvg-` prefix attributes (including `tvg-name`, `tvg-logo`, `tvg-id`, and `tvg-chno`) are extended metadata attributes that IPTV players parse to display channel information and thumbnails alongside the stream URLs.