# How to Edit IPTV Playlists Programmatically Using iptv-org/iptv

> Edit IPTV playlists programmatically with iptv-org/iptv. Parse M3U files, update stream data, and serialize results using TypeScript classes for efficient playlist management.

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

---

**You can edit IPTV playlists programmatically by leveraging the TypeScript classes in the iptv-org/iptv repository to parse M3U files, update stream metadata using the reference database, and serialize the results back to disk.**

The iptv-org/iptv repository provides a robust toolkit for managing Internet Protocol Television (IPTV) streams. If you need to edit IPTV playlists programmatically—whether to fix missing channel identifiers, batch-update metadata, or automate playlist maintenance—the repository's internal scripts expose a clean API for parsing, manipulating, and saving M3U files without manual intervention.

## Core Workflow for Programmatic Playlist Editing

The repository implements a seven-step pipeline in [`scripts/commands/playlist/edit.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/commands/playlist/edit.ts) that you can replicate or extend in your own automation.

### 1. Load Reference Data

Before modifying any playlist, the system loads the central JSON database that maps channel IDs to metadata, categories, and feeds.

- **Function**: `loadData()` in [`scripts/api.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/api.ts)
- **Output**: Populates lookup collections including `categoriesKeyById` and `feedsGroupedByChannel`
- **Source**: JSON files stored in `temp/data`

### 2. Parse the M3U File

The **PlaylistParser** class converts raw M3U text into typed **Stream** objects that you can manipulate programmatically.

- **Class**: `PlaylistParser` in [`scripts/core/playlistParser.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/core/playlistParser.ts)
- **Method**: `parseFile(filepath)`
- **Process**: Uses `iptv-playlist-parser` to extract entries, then instantiates `Stream` objects via `Stream.fromPlaylistItem`

### 3. Identify Streams Missing Metadata

The workflow filters for streams that lack a `tvg-id`, which is the unique identifier required for channel matching.

- **Logic**: `parsedStreams.filter(stream => !stream.tvgId)`
- **Location**: [`scripts/commands/playlist/edit.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/commands/playlist/edit.ts) (line 54)

### 4. Search for Matching Channels

For each unidentified stream, the system queries the loaded database using fuzzy text matching on the stream title.

- **Function**: `searchChannels(query)` in [`scripts/api.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/api.ts)
- **Engine**: Uses the same `sdk.SearchEngine` that powers the CLI's interactive selector

### 5. Update Stream Objects

Once a match is selected, the **Stream** instance is mutated in-place to add the `tvg-id` and optional feed identifier.

- **Format**: `channelId@feedId` (concatenated string)
- **Class**: `Stream` in [`scripts/models/stream.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/models/stream.ts)
- **Persistence**: The `toString()` method handles M3U serialization when writing back

### 6. Serialize and Save

The modified collection is converted back to M3U format and written to disk using the storage abstraction.

- **Class**: `Playlist` in [`scripts/models/playlist.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/models/playlist.ts)
- **Method**: `toString()` generates the M3U text
- **Storage**: `storage.saveSync(filepath, content)` in [`scripts/commands/playlist/edit.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/commands/playlist/edit.ts) (lines 81-85)

## Code Examples

### Running the Built-in CLI Command

You can invoke the repository's edit command from your own Node.js scripts to leverage the interactive workflow:

```typescript
import { execSync } from 'node:child_process';
import path from 'node:path';

const playlistPath = path.resolve('streams/us.m3u');

// Launches the interactive selector defined in edit.ts
execSync(`npm run playlist:edit -- ${playlistPath}`, { stdio: 'inherit' });

```

### Custom Script for Batch Updates

For fully automated editing without prompts, import the underlying classes directly:

```typescript
import { Storage } from '@freearhey/storage-js';
import { PlaylistParser } from './scripts/core/playlistParser';
import { loadData, searchChannels } from './scripts/api';
import { Stream } from './scripts/models/stream';

// 1. Load reference data
await loadData();

// 2. Parse the target playlist
const storage = new Storage();
const parser = new PlaylistParser({ storage });
const streams = await parser.parseFile('streams/us.m3u');

// 3. Fix missing tvg-ids automatically
for (const stream of streams.all()) {
  if (stream.tvgId) continue;

  const matches = searchChannels(stream.title);
  if (matches.count() === 0) continue;

  const chosen = matches.first();
  const feedId = chosen.feeds?.[0]?.id ?? '';
  stream.tvgId = feedId ? `${chosen.id}@${feedId}` : chosen.id;
}

// 4. Write the updated playlist back
const { Playlist } = await import('./scripts/models/playlist');
const updatedM3U = new Playlist(streams).toString();
storage.saveSync('streams/us.m3u', updatedM3U);

```

## Key Source Files and Classes

| File | Role | Key Exports |
|------|------|-------------|
| [`scripts/commands/playlist/edit.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/commands/playlist/edit.ts) | CLI entry point that orchestrates the full editing workflow | Interactive prompt logic, file I/O |
| [`scripts/api.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/api.ts) | Data layer that loads the JSON database and provides search functionality | `loadData()`, `searchChannels()`, `data` collections |
| [`scripts/core/playlistParser.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/core/playlistParser.ts) | M3U parsing engine | `PlaylistParser` class, `parseFile()` method |
| [`scripts/models/stream.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/models/stream.ts) | Domain model for individual streams | `Stream` class, `tvgId` property, `toString()` serialization |
| [`scripts/models/playlist.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/models/playlist.ts) | Container for multiple streams | `Playlist` class, `toString()` for M3U generation |

## Summary

- The **iptv-org/iptv** repository provides a complete TypeScript toolkit to edit IPTV playlists programmatically without manual M3U editing.
- The workflow involves loading reference data via `loadData()`, parsing M3U files with `PlaylistParser`, manipulating `Stream` objects to update `tvg-id` values, and serializing back through the `Playlist` class.
- You can either run the built-in CLI command (`npm run playlist:edit`) for interactive editing or import the underlying classes ([`scripts/api.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/api.ts), [`scripts/core/playlistParser.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/core/playlistParser.ts), [`scripts/models/stream.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/models/stream.ts)) to build fully automated pipelines.

## Frequently Asked Questions

### How do I parse an M3U playlist file programmatically in Node.js?

Use the `PlaylistParser` class from [`scripts/core/playlistParser.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/core/playlistParser.ts). Instantiate it with a `Storage` object, then call `parseFile(filepath)` to convert the M3U text into a collection of typed `Stream` objects that you can manipulate directly.

### What is the `tvg-id` field and why is it important for IPTV playlists?

The `tvg-id` is a unique identifier that maps a stream to a specific channel in the reference database. According to the iptv-org/iptv source code, streams lacking this ID cannot be matched to EPG data or channel metadata, which is why the [`edit.ts`](https://github.com/iptv-org/iptv/blob/main/edit.ts) command specifically targets missing `tvg-id` values for correction.

### Can I automate playlist editing without interactive prompts?

Yes. Instead of running the CLI command, import the underlying modules from [`scripts/api.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/api.ts) and [`scripts/models/stream.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/models/stream.ts) directly. Load the reference data with `await loadData()`, iterate over the parsed streams, update the `tvgId` property programmatically, and serialize the results using the `Playlist` class's `toString()` method.

### Where does the repository store its channel reference data?

The reference data is loaded from JSON files located in the `temp/data` directory (populated by the build process). The `loadData()` function in [`scripts/api.ts`](https://github.com/iptv-org/iptv/blob/main/scripts/api.ts) reads these files to create lookup collections like `categoriesKeyById` and `feedsGroupedByChannel`, which power the search functionality used during playlist editing.