How to Generate Merged IPTV Playlists from the iptv-org/iptv Repository

The iptv-org/iptv repository generates a single merged index.m3u playlist alongside grouped variants by running the playlist:generate command, which parses raw stream files, deduplicates entries, and outputs organized playlists to the public directory.

The iptv-org/iptv repository maintains thousands of publicly available IPTV streams organized in the streams/ directory. To generate merged IPTV playlists from this source, the project uses a TypeScript-based build pipeline that aggregates individual stream definitions into a unified index.m3u file and categorized sub-playlists. This article explains the technical architecture and commands required to execute this generation process.

Understanding the Playlist Generation Pipeline

The generation process is orchestrated by a command-line interface that coordinates data loading, parsing, and file output operations.

Entry Point and Orchestration

The primary entry point resides in scripts/commands/playlist/generate.ts. This module implements the playlist-generate command and executes the full pipeline:

  1. Loads API metadata for categories, countries, and regions
  2. Parses all raw .m3u files from the streams/ directory
  3. Deduplicates and sorts the stream collection
  4. Invokes specialized generators to write output files

Data Loading and Stream Parsing

The pipeline relies on scripts/api.ts to fetch metadata via the loadData() function. This data provides the taxonomy used to group streams by category, language, and geographic location.

Step-by-Step Generation Process

1. Load API Metadata

Before processing streams, the system initializes reference data:

import { loadData } from './scripts/api';

await loadData();

This populates data structures for categories, countries, subdivisions, cities, and regions used during classification.

2. Parse Raw M3U Files

The PlaylistParser class in scripts/core/playlistParser.ts processes individual stream files:

import { PlaylistParser } from './scripts/core';
import { Storage } from '@freearhey/storage-js';
import { STREAMS_DIR } from './scripts/constants';

const streamsStorage = new Storage(STREAMS_DIR);
const parser = new PlaylistParser({ storage: streamsStorage });
const files = await streamsStorage.list('**/*.m3u');
let streams = await parser.parse(files);

The parser uses iptv-playlist-parser to extract stream URLs, titles, and attributes, creating a Collection<Stream> of model objects.

3. Deduplicate and Sort Streams

The pipeline removes duplicates and establishes a consistent ordering:

import uniqueId from 'lodash.uniqueid';

streams = streams
  .uniqBy((s) => s.getId() || uniqueId())
  .sortBy(
    [(s) => s.getId(), (s) => s.getVerticalResolution(), (s) => s.label],
    ['asc', 'asc', 'desc']
  );

Deduplication uses Collection.uniqBy with the stream's unique ID or a generated temporary ID. Sorting prioritizes ID, then resolution, then label.

4. Generate Output Playlists

Specialized generator classes write the final files:

import { IndexGenerator, CategoriesGenerator } from './scripts/generators';
import { File } from '@freearhey/storage-js';

const logFile = new File('generators.log');

// Generate the merged index.m3u
await new IndexGenerator({ streams, logFile }).generate();

// Generate category playlists
await new CategoriesGenerator({ categories: data.categories, streams, logFile }).generate();

The IndexGenerator creates the merged index.m3u containing all SFW streams. Additional generators produce grouped playlists in categories/, languages/, countries/, subdivisions/, cities/, regions/, and sources/ directories.

Running the Generation Command

Execute the generation pipeline via the predefined script:

npm run playlist:generate

This command, defined in package.json, invokes:

"playlist:generate": "tsx scripts/commands/playlist/generate.ts"

Direct Execution with tsx

Run the TypeScript file directly without npm:

npx tsx scripts/commands/playlist/generate.ts

Programmatic Integration

Integrate the generation logic into custom automation:

import { loadData } from './scripts/api';
import { PlaylistParser } from './scripts/core';
import { IndexGenerator } from './scripts/generators';
import { Storage } from '@freearhey/storage-js';
import { STREAMS_DIR } from './scripts/constants';

async function generateCustomPlaylist() {
  await loadData();
  
  const storage = new Storage(STREAMS_DIR);
  const parser = new PlaylistParser({ storage });
  const files = await storage.list('**/*.m3u');
  const streams = await parser.parse(files);
  
  // Custom filtering logic here
  
  await new IndexGenerator({ streams, logFile: null }).generate();
}

generateCustomPlaylist();

Key Architectural Components

Component File Path Responsibility
Command Orchestrator scripts/commands/playlist/generate.ts Coordinates the full generation pipeline from data loading to file output.
Playlist Parser scripts/core/playlistParser.ts Reads raw .m3u files and converts them into Stream model objects.
Stream Model scripts/models/stream.ts Encapsulates stream properties with methods like getId(), isSFW(), and getVerticalResolution().
Index Generator scripts/generators/indexGenerator.ts Produces the merged index.m3u containing all deduplicated streams.
Category Generator scripts/generators/categoriesGenerator.ts Creates individual playlists for each content category.
Constants scripts/constants.ts Defines STREAMS_DIR (input) and PUBLIC_DIR (output) paths.

Summary

  • The iptv-org/iptv repository generates merged playlists through a TypeScript pipeline orchestrated by scripts/commands/playlist/generate.ts.
  • The process parses raw .m3u files from the streams/ directory using PlaylistParser, then deduplicates streams by ID and sorts them by resolution and label.
  • The IndexGenerator produces the merged index.m3u, while specialized generators create grouped playlists by category, language, country, and region.
  • Execute the pipeline using npm run playlist:generate or programmatically import the generator classes for custom automation.

Frequently Asked Questions

What is the difference between index.m3u and the grouped playlists?

The index.m3u file is the merged master playlist containing every SFW (Safe For Work) stream from the repository, serving as a comprehensive single source. The grouped playlists are filtered subsets organized into separate files by category, language, country, subdivision, city, or region, allowing users to subscribe only to specific content types or geographic areas.

How does the deduplication process work?

The deduplication process uses the Collection.uniqBy method to remove duplicate streams based on their unique identifier. Each Stream object provides a getId() method that returns a stable ID; if a stream lacks an ID, the system generates a temporary unique ID using lodash.uniqueid(). This ensures that identical streams appearing in multiple raw files appear only once in the final merged output.

Can I customize the output directory for generated playlists?

Yes, you can customize the output directory by modifying the PUBLIC_DIR constant defined in scripts/constants.ts. By default, this points to .gh-pages, but you can override it via environment variables or by editing the constants file before running the generation command. The Storage class from @freearhey/storage-js uses this path when writing the final .m3u files.

How do I add new streams to the merged playlist?

To add new streams, create or edit .m3u files within the streams/ directory (or appropriate subdirectories), following the standard M3U format with #EXTINF tags and stream URLs. Once your changes are saved, run npm run playlist:generate to parse your new files, deduplicate the collection, and regenerate the merged index.m3u with your additions included.

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 →