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

> Learn to generate merged IPTV playlists from the iptv-org/iptv repository. This guide explains how the playlist generate command simplifies stream parsing, deduplication, and organization for your index.m3u file.

- Repository: [iptv-org/iptv](https://github.com/iptv-org/iptv)
- Tags: how-to-guide
- Published: 2026-02-25

---

**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`](https://github.com/iptv-org/iptv/blob/main/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`](https://github.com/iptv-org/iptv/blob/main/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:

```typescript
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`](https://github.com/iptv-org/iptv/blob/main/scripts/core/playlistParser.ts) processes individual stream files:

```typescript
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:

```typescript
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:

```typescript
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

### Using npm (Recommended)

Execute the generation pipeline via the predefined script:

```bash
npm run playlist:generate

```

This command, defined in [`package.json`](https://github.com/iptv-org/iptv/blob/main/package.json), invokes:

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

```

### Direct Execution with tsx

Run the TypeScript file directly without npm:

```bash
npx tsx scripts/commands/playlist/generate.ts

```

### Programmatic Integration

Integrate the generation logic into custom automation:

```typescript
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`](https://github.com/iptv-org/iptv/blob/main/scripts/commands/playlist/generate.ts) | Coordinates the full generation pipeline from data loading to file output. |
| **Playlist Parser** | [`scripts/core/playlistParser.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/core/playlistParser.ts) | Reads raw `.m3u` files and converts them into `Stream` model objects. |
| **Stream Model** | [`scripts/models/stream.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/models/stream.ts) | Encapsulates stream properties with methods like `getId()`, `isSFW()`, and `getVerticalResolution()`. |
| **Index Generator** | [`scripts/generators/indexGenerator.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/generators/indexGenerator.ts) | Produces the merged `index.m3u` containing all deduplicated streams. |
| **Category Generator** | [`scripts/generators/categoriesGenerator.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/generators/categoriesGenerator.ts) | Creates individual playlists for each content category. |
| **Constants** | [`scripts/constants.ts`](https://github.com/iptv-org/iptv/blob/main/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`](https://github.com/iptv-org/iptv/blob/main/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`](https://github.com/iptv-org/iptv/blob/main/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.