How Country, Language, and Category Generators Work in IPTV-ORG

The IPTV-ORG repository uses dedicated TypeScript generator classes that implement a simple Generator interface to group streaming data by country, language, and category, outputting organized M3U playlists to the public/ directory.

The iptv-org/iptv repository maintains thousands of publicly available streaming channels, organized into consumable playlists. Understanding how these country language category generators function requires examining the TypeScript automation scripts that transform raw stream data into structured M3U files.

The Generator Interface and Core Workflow

Every generator in the pipeline adheres to a minimal contract defined in [scripts/generators/generator.ts](https://github.com/iptv-org/iptv/blob/master/scripts/generators/generator.ts):

export interface Generator {
  generate(): Promise<void>
}

Data Loading and Preparation

The orchestration begins in [scripts/commands/playlist/generate.ts](https://github.com/iptv-org/iptv/blob/master/scripts/commands/playlist/generate.ts), which executes a five-step workflow:

  1. Load raw data – loadData() pulls the full list of streams, countries, languages, and categories from the @iptv-org/sdk repository.
  2. Parse M3U files – The PlaylistParser processes all .m3u files in the streams/ directory into a collection of Stream model objects.
  3. Deduplicate and sort the stream collection to ensure data integrity.
  4. Instantiate generators for countries, languages, and categories, invoking each generate() method sequentially.
  5. Write outputs to the public/ directory and append audit entries to generators.log.

How the Country Generator Works

The CountriesGenerator ([scripts/generators/countriesGenerator.ts](https://github.com/iptv-org/iptv/blob/master/scripts/generators/countriesGenerator.ts)) groups streams by broadcast country codes retrieved via stream.getBroadcastCountries():

for (const countryCode in streamsGroupedByCountryCode) {
  const playlist = new Playlist(countryStreams, { public: true })
  const filepath = `countries/${countryCode.toLowerCase()}.m3u`
  await this.storage.save(filepath, playlist.toString())
  this.logFile.append(JSON.stringify({ type: 'country', filepath, count: playlist.streams.count() }) + EOL)
}
  • International streams (those returning true from isInternational()) are routed to countries/int.m3u.
  • Streams lacking country metadata are collected into countries/undefined.m3u.

How the Language Generator Works

The LanguagesGenerator ([scripts/generators/languagesGenerator.ts](https://github.com/iptv-org/iptv/blob/master/scripts/generators/languagesGenerator.ts)) first compiles a unique set of languages from the filtered SFW streams:

streams.forEach(stream =>
  stream.getLanguages().forEach(language => languages.add(language))
)

For each detected language, it filters the stream collection and persists a dedicated playlist:

const languageStreams = streams.filter(stream => stream.hasLanguage(language))
const playlist = new Playlist(languageStreams, { public: true })
const filepath = `languages/${language.code}.m3u`
await this.storage.save(filepath, playlist.toString())

Streams without language metadata are aggregated into languages/undefined.m3u.

How the Category Generator Works

The CategoriesGenerator ([scripts/generators/categoriesGenerator.ts](https://github.com/iptv-org/iptv/blob/master/scripts/generators/categoriesGenerator.ts)) enhances playlists with group titles that concatenate all categories assigned to a stream. This metadata helps IPTV players visually organize channels:

const groupTitle = stream
  .getCategories()
  .map(cat => cat.name)
  .sort()
  .join(';')
if (groupTitle) stream.groupTitle = groupTitle

Each category receives its own categories/<id>.m3u file, with uncategorized streams defaulting to categories/undefined.m3u.

Logging and Audit Trail

Every generator appends structured JSON lines to generators.log, creating an auditable record of the build process. The command persists this file via logStorage.saveFile(logFile) after all generators complete. Each log entry follows this schema:

{
  "type": "country",
  "filepath": "countries/us.m3u",
  "count": 342
}

Practical Implementation Examples

Running a Single Generator Manually

You can instantiate and run individual generators outside the main pipeline:

import { Storage, File } from '@freearhey/storage-js'
import { Collection } from '@freearhey/core'
import { Stream } from './models'
import { CountriesGenerator } from './generators/countriesGenerator'

const logFile = new File('generators.log')
const generator = new CountriesGenerator({
  streams: allStreams,
  countries: allCountries,
  logFile,
})

await generator.generate()

Chaining Generators in the Build Pipeline

The main command executes generators sequentially to build the complete playlist hierarchy:

// Inside scripts/commands/playlist/generate.ts
await new CategoriesGenerator({ categories, streams, logFile }).generate()
await new LanguagesGenerator({ streams, logFile }).generate()
await new CountriesGenerator({ countries, streams, logFile }).generate()

Summary

  • The Generator interface in scripts/generators/generator.ts defines a single generate() method that all playlist builders implement.
  • CountriesGenerator uses stream.getBroadcastCountries() to create geographic playlists like countries/us.m3u, with special handling for international and undefined streams.
  • LanguagesGenerator filters streams using stream.hasLanguage() to produce language-specific files such as languages/eng.m3u.
  • CategoriesGenerator constructs group titles by joining category names with semicolons, aiding player-side organization.
  • All generators write to public/ and log activity to generators.log for transparency and debugging.

Frequently Asked Questions

What file format do the generators output?

The generators produce M3U playlist files, a standard format supported by virtually all IPTV players and media applications. Each file contains a curated list of stream URLs along with metadata like group titles and channel names.

How are international streams handled by the country generator?

Streams flagged as international via stream.isInternational() are aggregated into a special file named countries/int.m3u rather than being assigned to specific country codes. This ensures borderless channels remain accessible without geographic restriction.

What happens to streams missing metadata?

Streams lacking country, language, or category information are never discarded. Instead, each generator routes these entries to dedicated fallback files: countries/undefined.m3u, languages/undefined.m3u, or categories/undefined.m3u, ensuring complete data coverage.

Where can I find the generated playlist files?

After running the generation command, all playlist files reside in the public/ directory, organized into subdirectories by type (countries/, languages/, categories/). These files are typically committed to the repository and served via CDN or GitHub Pages for public consumption.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →