# zhimin-dev/iptv-checker probes your own playlist and filters the dead streams out

> iptv-checker validates M3U, M3U8 and TXT playlist files you already hold, running HTTP and FFmpeg probes behind a web admin so scheduled jobs can export only the channels that answer. It checks one operator's list and supplies none of it, and its build plumbing does not survive a fresh clone.

**zhimin-dev/iptv-checker** — IPTV checker tool for Docker && CMD, check your playlist is available

- Repository: https://github.com/zhimin-dev/iptv-checker
- Stars: 2,051 · Forks: 203
- Language: Makefile
- License: MIT
- Published: 2026-10-14 · Updated: 2026-10-14 · Language: en
- Canonical page: https://hysenlabs.com/projects/zhimin-dev-iptv-checker

## The target is a playlist you already hold, not a channel catalogue

Every channel in an M3U file is a URL with an expiry date, and playlists rot quietly. The file still parses, the player still loads it, and a growing share of the streams inside are dead. iptv-checker targets that gap and nothing wider: it reads M3U, M3U8 and TXT playlist formats, reports which channels answer, and lets you export the survivors. The scenarios named for it are periodic validity checks on your own sources, filtering dead links, visual management of check tasks through a web interface, batch export of working IPv4 and IPv6 channel lists, and automating the same check inside a CI/CD pipeline. Nothing in the repository root ships channel data. What lives there is a `dockerfile`, a compose file and a `Makefile`, so the input is always a list you hand it.

## Two probe paths decide whether a channel counts as alive

Detection splits in two, and that split is the design. An HTTP check asks whether the URL responds. An FFmpeg forced check makes a client actually pull media, which covers the case where a server answers a request for a URL that never delivers a playable stream. The feature list credits the combination with more accurate results, yet nothing states which path a given run takes by default, or how to force one from the configuration. Around those two probes sit concurrency control, scheduled tasks, task import and export. Timeout, custom sorting, keyword matching and a filter that keeps the fastest network results are configurable too. The web admin is where those choices are made, so the split between cheap validation and forced decode is something you discover through the interface rather than through a documented flag.

## Pull the published image and reach the admin panel on port 8081

The container route is the documented first run, and both commands below are given with their original comments intact.

```bash
# 拉取镜像
docker pull zmisgod/iptvchecker

# 启动容器
docker run -d \
  -p 8081:8089 \
  --name iptv-checker \
  zmisgod/iptvchecker
```

The port mapping sends 8081 on the host to 8089 inside the container, so the panel is not served on the container port and a second instance needs a different host side. Once the container is up, the browser goes to 127.0.0.1:8081 for the management interface. Images are published under the name `zmisgod/iptvchecker` on Docker Hub, which is also where the README says you can list every available tag. Note what this route does not give you: no volume is declared and no environment variables are passed, so anything the job produces stays inside the container filesystem until you export it.

## The compose file committed in the repository disagrees with the README copy

The compose file that ships in the repository root is not the file the README tells you to write. Here is the committed version, unchanged:

```yaml
version: "3"
services:
  website:
    image: zmisgod/iptvchecker
    ports:
      - "8081:8089"
    restart: always
```

The README example names the same image under a different service key, `iptv-checker`, with the same `8081:8089` mapping. On its face that is cosmetic, but compose treats the service name as the container key, so a reader who pastes the README file next to the repository version ends up with two definitions of the same image under two names, and commands that address one of them leave the other untouched. Both files set `restart: always` and neither declares a volume, so a reboot brings the container back with whatever state it still holds and no trace of that state survives removing the container. Starting either one comes down to:

```bash
docker-compose up -d
```

Pick the repository file if you want the names to match what you can read in the tree.

## The Makefile builds from a sibling checkout the repository does not contain

Most of the source is not here, and the repository says so in an unusual way: its primary language is a Makefile. That file declares a `migrate` target and an `all` target that depends on it:

```make
.PHONY: migrate

all: migrate

migrate:
	rm -rf ../iptv-checker-rs/web/
	cp -rf ../iptv-checker-ui-interface/dist/ ../iptv-checker-rs/web/
```

The target deletes `../iptv-checker-rs/web/` and copies `../iptv-checker-ui-interface/dist/` into it. Both paths point outside the repository, into two sibling checkouts the repository does not contain, and neither the README nor the file list describes a submodule or clone step that would put them there. Run make in a fresh clone and the copy source is missing, so the checker binary, the web interface and the container image are all assembled somewhere else and a consumer of the published artifacts never touches this file. Even the root carries a committed `.DS_Store`, which tells you the tree is maintained from a desktop macOS session. Licensing is MIT, so a local copy carries no source disclosure obligation, and the last push was on 2026-09-25.

## A GitHub token lifts the API ceiling from 60 to 5000 per hour

Pulling playlist sources from GitHub is the part of a typical run that meets an external limit, and the project handles it in three documented states. Unauthenticated GitHub API requests are capped at 60 per hour; with a token the ceiling is 5000 per hour. The value goes into a field named `github_token` under system settings and basic configuration in the web admin, and the save path calls the GitHub API to check it, so an invalid token is rejected with an error instead of being stored. The recommended grant is a fine-grained token with a custom expiration, `Public repositories (read-only)` for repository access, and `Contents` left at `Read-only`. With nothing configured the checker attempts the API first and drops to HTML parsing once it is rate limited, which quietly changes what a source list can resolve, and no retry window is written down.

## EPG data rewrites channel names and strips the 4K suffix

Import is more than line splitting, and the channel naming rules are where the project does its most opinionated work. On parsing it uses EPG data to normalise `tvg-name` and `tvg-id`, removing quality suffixes such as 4K from the visible name, and it keeps a local map from `tvg-name` to `group-title` so a channel the EPG does not list still lands in a sensible group instead of an unnamed one. Display is a separate choice made afterwards: the name can carry a resolution tag such as HD or FHD, a numeric label such as 720p or 1080p, and a marker where the same channel is available at more than one resolution. Exports leave as M3U or TXT and can be split by address family, so IPv4 channels and IPv6 channels land in separate output files, and that split is the step that makes a cleaned list usable as fresh input somewhere else.

## Scheduled tasks and exports ship without documented limits

Several things an operator would want before trusting a nightly run are not written down anywhere. The feature list promises concurrent checks, scheduled tasks and task import and export, and version 5.0.0 records a fix for a scheduled task that could hang plus concurrent execution of those tasks, yet no concurrency ceiling, schedule syntax, default timeout or export size limit is given. Version 5.0.1 merged checked and favourite channels behind one group filter in the player and cleaned up dead code that had taken compiler warnings from 75 to 6, while version 4.7.1 fixed a bool field validation that returned a 400 error, a missing `x-tvg-url` and a `no_check` combined with `video_quality` bug, which tells you how much of the surface is still moving. Config import and export arrived in 4.7.0 next to SSRF protection, so moving a configuration between hosts is supported, but no file format is shown for it. The wrong-tool case is the plain one: this never supplies channels, holds no built-in source catalogue, and says nothing about the content of the list you feed it.

## Conclusion

Adopt it if you already own a large M3U list and need it cleaned on a schedule; leave it alone if you expect the tool to supply channels or to stand in for a player. Before trusting a run, check what it leaves behind: `docker-compose up -d` with the repository compose file stores nothing outside the container, and the Makefile cannot rebuild the binary from a fresh clone. Decide first whether an HTTP pass alone clears your dead links, since the forced FFmpeg probe is the part that moves real bandwidth.

## FAQ

### Does zhimin-dev/iptv-checker come with a channel list?

No. The repository root holds a `dockerfile`, a compose file and a `Makefile`, with no playlist data in it, so every run depends on an M3U, M3U8 or TXT file you provide.

### How do I start zhimin-dev/iptv-checker with Docker?

Pull `zmisgod/iptvchecker` and run it with host port 8081 mapped to container port 8089, then open the management interface at 127.0.0.1:8081.

### Why does zhimin-dev/iptv-checker ask for a GitHub token?

Unauthenticated GitHub API calls are limited to 60 per hour while a token raises that to 5000 per hour. It is stored in the `github_token` field and validated against the GitHub API when you save it.

### Can I rebuild zhimin-dev/iptv-checker from a fresh clone?

Not with the Makefile as written. Its `migrate` target copies a UI build from `../iptv-checker-ui-interface` into `../iptv-checker-rs`, and neither directory exists inside the repository.

### Does zhimin-dev/iptv-checker rewrite channel names?

Yes. During parsing it normalises `tvg-name` and `tvg-id` from EPG data and removes quality suffixes such as 4K, and a local map fills `group-title` for channels the EPG does not cover.

### How does zhimin-dev/iptv-checker export the channels that still work?

Results export as M3U or TXT, and the export can be split by address family so that IPv4 and IPv6 channels go into separate output files.

## Sources

- [Issues](https://github.com/zhimin-dev/iptv-checker/issues)
- [License: MIT](https://github.com/zhimin-dev/iptv-checker/blob/main/LICENSE)
- [README](https://github.com/zhimin-dev/iptv-checker/blob/main/README.md)
- [Releases](https://github.com/zhimin-dev/iptv-checker/releases)
- [zhimin-dev/iptv-checker on GitHub](https://github.com/zhimin-dev/iptv-checker)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zhimin-dev-iptv-checker
