How to Test IPTV Stream URLs Before Adding Them to the Playlist

The Free-TV/IPTV repository automatically validates every stream URL through a CI pipeline that runs m3u-linter for syntax checking and iptv-checker with FFmpeg to verify stream availability, latency, and geo-blocking before any pull request can be merged.

Testing IPTV stream URLs before adding them to a playlist prevents broken links and geo-restricted content from reaching end users. The Free-TV/IPTV repository implements a robust validation workflow that contributors can replicate locally to ensure their submissions meet quality standards. This guide explains how the automated testing works and how to run the same checks on your local machine.

Automated CI Validation in Free-TV/IPTV

Every pull request that modifies a .md file in the lists/ directory triggers the Test IPTV file quality workflow defined in .github/workflows/test_playlist.yml. This CI pipeline performs two distinct validation layers before allowing a merge.

M3U Syntax Validation

The workflow uses the m3u-linter tool to parse the generated playlist.m3u8 file and enforce strict formatting rules. The linter checks for proper header syntax, attribute quoting, and playlist structure according to the project's M3u-linter.config.json configuration. If the generated playlist contains malformed entries, the CI job fails immediately.

Stream Availability Testing

The iptv-checker utility probes each URL using FFmpeg to verify actual stream accessibility. According to lines 47-51 in .github/workflows/test_playlist.yml, the checker runs with a concurrency of 100 workers and a 120-second timeout per stream:


# As implemented in the CI workflow

iptv-checker -o output -p 100 -t 120000 playlist.m3u8

The tool reports dead streams, measures latency, identifies GeoIP-blocking issues, and writes a detailed JSON summary to the output/ directory. Line 49 of the same workflow file ensures FFmpeg is installed as a prerequisite for the checker.

Local Testing Workflow for Contributors

You can replicate the exact CI validation locally before submitting a pull request. This ensures that changes to lists/*.md files will pass automated testing.

  1. Clone the repository and generate the playlist:

    git clone https://github.com/Free-TV/IPTV.git && cd IPTV
    python3 make_playlist.py

    This script processes the markdown tables in lists/*.md and builds playlist.m3u8, injecting country codes and filtering only entries marked with [>] (as handled in make_playlist.py lines 53-55).

  2. Validate M3U syntax:

    npm install -g m3u-linter
    m3u-linter -c M3u-linter.config.json playlist.m3u8
  3. Install FFmpeg and the stream checker:

    sudo apt-get install -y ffmpeg  # Required by iptv-checker
    
    npm install -g iptv-checker
  4. Test all stream URLs:

    iptv-checker -o output -p 100 -t 120000 playlist.m3u8

    This probes every URL with 100 concurrent workers and a 120-second timeout per stream, matching the CI configuration.

  5. Inspect the results:

    cat output/report.json | jq '.'

    Look for entries with "status":"dead" or "geoBlocked":true to identify problematic URLs.

  6. Fix and iterate:

    Update failing URLs in the appropriate lists/*.md file, regenerate the playlist with python3 make_playlist.py, and repeat steps 2-5 until the report shows only healthy streams.

Quick Ad-Hoc Testing for Single URLs

For rapid verification of a single stream without running the full suite, use FFprobe to check codec and stream metadata:

ffprobe -v error -show_entries stream=codec_name,width,height -of default=noprint_wrappers=1:nokey=1 "$URL"

If the command returns codec information and exits with status 0, the stream is reachable and playable. A non-zero exit code indicates network errors, 404 responses, or authentication failures.

Understanding the Test Results

The output/report.json file generated by iptv-checker contains detailed metadata for each tested URL. Healthy streams show "status":"online" with low latency values, while failed entries indicate specific failure modes:

  • Dead streams: URLs that timeout or return connection errors
  • Geo-blocked streams: URLs that reject connections based on source IP location (marked "geoBlocked":true)
  • Slow streams: URLs exceeding the 120-second threshold

According to the contribution guidelines in README.md lines 174-176, any URL flagged as unreachable or geo-blocked must be resolved before the PR can be merged, maintaining the repository's policy of providing working, freely accessible content.

Summary

  • Free-TV/IPTV automatically tests every URL via GitHub Actions in .github/workflows/test_playlist.yml using m3u-linter and iptv-checker.
  • Run python3 make_playlist.py locally to generate playlist.m3u8 from the lists/*.md source files before testing.
  • Use iptv-checker -o output -p 100 -t 120000 playlist.m3u8 to replicate CI testing with 100 concurrent workers and 120-second timeouts.
  • Check output/report.json for "status":"dead" or "geoBlocked":true" entries to identify problematic streams.
  • Use ffprobe for quick single-URL validation without installing the full checker suite.

Frequently Asked Questions

How long does the stream testing take?

The iptv-checker processes URLs concurrently using 100 workers, so a typical playlist of several hundred channels completes in 5-10 minutes depending on network latency. Individual streams timeout after 120 seconds if unresponsive.

Can I test streams without installing Node.js?

While m3u-linter and iptv-checker require Node.js, you can validate single URLs using FFprobe (which only requires FFmpeg). For full playlist validation, the Node.js tools are necessary to replicate the exact CI environment used by Free-TV/IPTV.

What does the [>] marker mean in the markdown files?

The make_playlist.py script (lines 53-55) only includes channels marked with [>] in the generated playlist.m3u8. This marker indicates the stream is confirmed working and should be included in public playlists, while unmarked entries are treated as draft or unverified.

Why does my stream pass locally but fail in the CI?

The CI environment may use different network routing or IP geolocation that triggers geo-blocking rules not present in your local network. Additionally, some streams block datacenter IPs (common for GitHub Actions runners) while allowing residential connections. Check the geoBlocked field in the JSON report to confirm.

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 →