Version: 1.26.1741204 |
Author: PiratesIRC | Last Updated: Jun 23 2026, 22:27 UTC |
A Dispatcharr Plugin that goes through a playlist to check IPTV channels
iptv-checker-latest.zip4de0eceChecksums:
MD5: e16b4dc3bc9953f27fca31ce4f4569af
SHA256: f55095d46511ecf6f5a759b47d9738c76167690acc52240df14250f23ed53c4c
| Version | Download | Built | Commit | MD5 | SHA256 |
|---|---|---|---|---|---|
1.26.1741204 |
Download | Jun 23 2026, 22:27 UTC | 4de0ece |
e16b4dc3bc9953f27fca31ce4f4569af | f55095d46511ecf6f5a759b47d9738c76167690acc52240df14250f23ed53c4c |
1.26.1721834 |
Download | Jun 21 2026, 19:40 UTC | 7ae9fe2 |
ed2f2081ffcde94a28229f838beb91db | 280a80ef503627be5ab94c675cb9a757844dea34f4d6daa249d171fc14106dbd |
1.26.1582047 |
Download | Jun 08 2026, 00:13 UTC | 78654d4 |
61646995883517cc4fe9caeec47dffb5 | e25a02db89ace1b05366b2c361f20c56876deabddba59ee3dd8b3817240dced1 |
1.26.1421301 |
Download | May 22 2026, 14:18 UTC | c53b209 |
49d562cb3128c2335d654a82abd93d7f | d6ece367d7f57c3149756d3841d558ccaffc3df0dcf443cb918b923d22c5f03a |
1.26.1362003 |
Download | May 16 2026, 20:45 UTC | 5d5ad16 |
a4b56a82858b7b976d87c703f80e163e | 820e7db19aeb50460d51e5d9069acacd413bc8f573bf9db2f83cac3dae44ce7b |
1.26.1221101 |
Download | May 02 2026, 17:57 UTC | aa662b3 |
e82d4b95df6c089471ca0547e8b2791c | 75b2b8379912cd82ca2026a0ab58d875e99af65308253c163adc1557464113f4 |
1.26.1161403 |
Download | Apr 26 2026, 14:39 UTC | 740b4ee |
cc0be9f97a30b0e9d88119f59e726119 | 731abcffedf0b4959982109c05649cd30d64aa119193666ba6b2465606f2b75f |
1.26.1081815 |
Download | Apr 18 2026, 19:11 UTC | f7bd820 |
004cdba61f06fb24e7cb201e3ddd5568 | 31027fb0ca94093489d169e853a363f90f6f4cd1fddc50c9a48b45159d558df4 |
0.8.0 |
Download | Apr 05 2026, 21:33 UTC | 33d258c |
1a6cc492b8003baeac68ab16d21958d9 | 094436b6389e35bfa80a29ae12e7917d21e816e21aef02a672a819963ea3f17a |
Source: Browse Plugin
Metadata: View full manifest
Before installing or using this plugin, it is highly recommended that you create a backup of your Dispatcharr database. This plugin makes significant changes to your channel and stream assignments.
Click here for instructions on how to back up your database.
ffmpeg blackdetect pass and is marked Dead (Black Screen) if it’s essentially all black. Blank channels are their own category — own rename tag ([Blank]) and own group (Black Screens), separate from regular dead (v1.26.1721554+). Fail-open: any ffmpeg problem leaves the stream Alive (v1.26.1702112+)[DEAD]/[Slow]/[Blank]/quality) and move it back to its exact original group — captured automatically when it was first moved. Available as a manual action and a scheduler toggle that runs first each scheduled check (v1.26.1721554+)US-*, *Sports*, or Movies-??ffprobe_monitoring_seconds column, and the audit preamble’s FFprobe Flags: line now reports the flags actually used (v1.26.1741204+).video_bitrate (kbps) captured via ffprobe per-packet data and stored in Dispatcharr’s stream_stats, so the channel-menu UI can display it. Live MPEG-TS / HLS streams almost never expose container-level bit_rate, so the plugin computes the average from packets[].size / packets[].duration_time for the video stream. Rounded to the nearest whole kbps before write (v1.26.1220052+). Probes that capture fewer than 30 video packets (≈1s of 30fps video) leave video_bitrate unset rather than persist a noisy average — short samples were producing wildly inflated values (observed: 22924 kbps from 2 packets) that polluted the channel-menu display (v1.26.1221035+). The default ffprobe_analysis_duration was bumped from 5 s → 8 s in v1.26.1221101 to give slow-start streams enough room to clear the 30-packet trust gate; verified runs jumped from 97% to 100% bitrate coverage on alive streams with median packet count rising from 200–400 → 662 (default-only change, existing deployments keep their saved value). The default ffprobe_flags was changed to -show_streams,-show_packets,-loglevel error in v1.26.1211342 — passing both -show_frames and -show_packets makes ffprobe emit a combined packets_and_frames array instead of separate packets[], which silently breaks the bitrate calc. If you’ve customized ffprobe_flags, do not include -show_frames._past_window_end() so transient-error retries cannot overshoot the window boundary (v1.26.1212238+; previously observed up to 14 minutes of overrun).No Video Stream) instead of Dead, so rename/move/delete actions leave them alone (v1.26.1741204+). Skipped now has three triggers: Streamlink-only hosts, HTTP 429 rate-limiting, and audio-only streams.[Slow] — only genuinely choppy streams qualify (v1.26.1741204+)./data/iptv_checker_scheduler.pid ensures exactly one of them hosts the cron scheduler. Prior versions could fire each cron N times in parallel (v1.26.1181126+). Module-reload duplicate-thread protection added in v1.26.1191257 (Django/uwsgi could re-import the plugin module within the elected process and spawn additional scheduler threads, defeating the PID lock). Cross-worker UI-restart protection added in v1.26.1220951: any UI button click landed in whichever uwsgi worker the load balancer picked, and update_schedule_action / Plugin.run() previously called _start_background_scheduler directly without checking the PID lock — so non-owner workers spawned rogue scheduler threads (observed: '59 23 * * *' fired twice 27 ms apart on 2026-05-02). Non-owners now write a /data/iptv_checker_scheduler_reload.flag file that the owner’s scheduler loop polls every 30 s; the owner re-reads settings via _fresh_settings and swaps its cron expressions in place.This plugin requires ffmpeg and ffprobe to be installed in the Dispatcharr container for stream analysis. The scheduler feature requires pytz (usually included).
Default Locations:
/usr/local/bin/ffprobe (plugin default, configurable)/usr/local/bin/ffmpegVerify Installation:
docker exec dispatcharr which ffprobe
docker exec dispatcharr which ffmpeg
No API credentials are needed — the plugin runs inside Dispatcharr with direct database access.
To update the plugin:
docker restart dispatcharr
| Setting | Type | Default | Description |
|---|---|---|---|
| Group(s) to Check | string | (empty = all) | Comma-separated group names. Supports wildcards: US-*, *Sports* |
| Group(s) to EXCLUDE | string | (empty) | Comma-separated groups to skip, applied after the include filter. Supports wildcards. With a blank “Group(s) to Check” this means “all groups except these”. If a group matches both fields, exclude wins. (v1.26.1721733+) |
| Check Alternative Streams | boolean | true | Check all alternative/backup streams for each channel |
| Connection Timeout | number | 10 | Seconds to wait for stream connection |
| Probe Timeout | number | 20 | Seconds to wait for FFprobe stream analysis |
| Dead Connection Retries | number | 3 | Number of retry attempts for failed streams |
| Setting | Type | Default | Description |
|---|---|---|---|
| Dead Channel Rename Format | string | {name} [DEAD] |
Format for renaming dead channels (excludes black/blank — see below) |
| Move Dead Channels to Group | string | Graveyard |
Group to move dead channels to (excludes black/blank) |
| Blank-Screen Channel Rename Format | string | {name} [Blank] |
Format for renaming channels detected as a blank screen |
| Move Blank-Screen Channels to Group | string | Black Screens |
Group to move blank-screen channels to |
| Low Framerate Rename Format | string | {name} [Slow] |
Format for renaming low FPS channels (<24fps — 25fps PAL and 24fps film are not flagged) |
| Move Low Framerate Group | string | Slow |
Group to move low framerate channels to |
| Video Format Suffixes | string | UHD, FHD, HD, SD, Unknown |
Formats to add as suffixes |
Blank-screen is a separate category (v1.26.1721554+). When blank-screen detection is on, blank channels are renamed/moved by the blank actions (
[Blank]/Black Screens) and are excluded from the regular Dead rename/move so they aren’t double-tagged. They remainstatus=Dead, so Delete Dead Channels still deletes them. Existing users who relied on blank streams getting[DEAD]/Graveyard should enable the new blank rename/move toggles (or set the Blank-Screen Rename Format to{name} [DEAD]).
| Setting | Type | Default | Description |
|---|---|---|---|
| FFprobe Path | string | /usr/local/bin/ffprobe |
Full path to the ffprobe executable |
| FFprobe Analysis Flags | string | -show_streams,-show_packets,-loglevel error |
Comma-separated FFprobe flags. Do not add -show_frames — it makes ffprobe emit a combined packets_and_frames array that breaks the bitrate calc. |
| FFprobe Analysis Duration | number | 8 | Seconds of stream to analyze |
| Streamlink-Only Hosts | string | youtube.com, youtu.be, twitch.tv, kick.com |
Comma-separated host suffixes ffprobe cannot validate (served via Streamlink). Streams matching these hosts are marked Skipped instead of Dead, so rename/move/delete actions leave them alone. Blank falls back to defaults. |
Optional second pass that decodes a few seconds of each alive stream with ffmpeg’s blackdetect filter and marks it Dead (Black Screen) if it is a pure black picture. Off by default. Requires ffmpeg in the container (see Requirements). Adds ~5–10 s per alive stream when enabled; dead/skipped streams are unaffected.
| Setting | Type | Default | Description |
|---|---|---|---|
| Detect Blank-Screen Streams | boolean | false | Master toggle. When on, every alive stream is decoded with ffmpeg blackdetect; pure-black streams become Dead with error_type = Black Screen. Fail-open: if ffmpeg is missing or errors, the stream stays Alive. Very-dark-but-not-black “no signal” slates are not detected. |
| Blank-Screen Sample (seconds) | number | 6 | How many seconds of video to decode when testing for black. Longer = more reliable but slower. |
| Continuous Blank Required (seconds) | number | 3 | Minimum continuous run of black video (within the sample) required to flag. Keep a few seconds below the sample to allow for connection/keyframe latency. |
| Blank-Screen ffmpeg Timeout (seconds) | number | 20 | Hard wall-clock cap on the ffmpeg decode (connection + sampling). If exceeded, the stream is left Alive. |
| FFmpeg Path | string | /usr/local/bin/ffmpeg |
Full path to the ffmpeg executable (under Advanced). |
| Setting | Type | Default | Description |
|---|---|---|---|
| Enable Parallel Checking | boolean | true | Check multiple streams simultaneously |
| Number of Parallel Workers | number | 2 | How many streams to check at once. Keep below your provider’s concurrent-connection limit. |
| Per-Stream Cooldown (seconds) | number | 2 | Each worker waits this long after finishing a check before picking up the next. Prevents provider rate-limiting / slot-reuse errors. Retry passes wait 3× this value. |
| Setting | Type | Default | Description |
|---|---|---|---|
| Webhook URL | string | (empty) | HTTP POST URL for notifications after scheduled checks |
| Setting | Type | Default | Description |
|---|---|---|---|
| Scheduled Check Times | string | (empty) | Cron expression (e.g., 0 4 * * * for daily at 4 AM). When Use Windowed Schedule is on, this becomes the window start trigger. |
| Use Windowed Schedule | boolean | false | When on, each cron-fire opens a run window. The check runs until the configured end-of-window, then halts cleanly between streams. The next time the window opens, the run resumes from where it left off — already-checked streams are skipped. |
| Window End Mode | select | duration |
duration = run for N hours; time = run until a specific HH:MM (wraps past midnight if earlier than the start). |
| Window Duration (hours) | number | 4 | Used when Window End Mode = duration. Decimals allowed (e.g. 3.5). |
| Window End Time | string | 04:00 |
Used when Window End Mode = time. 24-hour format in Dispatcharr’s timezone (see note below). |
Timezone (v1.26.1721651+): the scheduler no longer has its own timezone setting — it uses Dispatcharr → Settings → General → Time Zone. Set your timezone there and all scheduled/windowed run times follow it. Falls back to
UTConly if Dispatcharr’s timezone can’t be read. (If you previously chose a plugin timezone that differed from Dispatcharr’s, your scheduled times now follow Dispatcharr’s — adjust the Dispatcharr setting if needed.) | Export CSV for Scheduled Checks | boolean | false | Auto-export results to CSV after scheduled checks | | Restore Recovered Channels | boolean | false | Auto-restore channels that are Alive again but were previously marked — strips plugin tags and moves them back to their original group. Runs first, before re-marking. | | Rename Dead Channels | boolean | false | Auto-rename dead channels after scheduled checks | | Rename Low Framerate Channels | boolean | false | Auto-rename slow channels after scheduled checks | | Rename Blank-Screen Channels | boolean | false | Auto-rename blank-screen channels after scheduled checks | | Add Video Format Suffix | boolean | false | Auto-add format suffix after scheduled checks | | Move Dead Channels | boolean | false | Auto-move dead channels after scheduled checks | | Move Low Framerate Channels | boolean | false | Auto-move slow channels after scheduled checks | | Move Blank-Screen Channels | boolean | false | Auto-move blank-screen channels after scheduled checks | | Delete Dead Channels | boolean | false | Auto-delete dead channels after scheduled checks | | Send Webhook Notification | boolean | false | Send webhook after scheduled checks (payload gains arestoredcount) |
| Setting | Type | Default | Description |
|---|---|---|---|
| Auto-Delete Confirmation | string | (empty) | Type DELETE to enable auto-delete of dead channels |
US-*)[Blank] format to channels detected as a blank screenBlack Screens group/data/iptv_checker_channel_state.json. If the original group was deleted, the name is still restored.Use shell-style wildcards in the Group(s) to Check field:
US-* — matches US-Movies, US-Sports, US-News, etc.*Sports* — matches any group containing “Sports”Movies-?? — matches Movies-US, Movies-UK, etc.US-*, UK-*, *Sports* (comma-separated)Use the Group(s) to EXCLUDE field to skip groups that would otherwise be checked. Same comma-separated wildcard syntax. Exclude is applied after the include filter, so it composes:
US-* but skip the pay-per-view groups → Check US-*, Exclude US-PPV-*Adult0 4 * * *)For overnight or off-peak runs, enable Use Windowed Schedule. The cron expression becomes the window start; the check halts cleanly when the window closes and resumes from the same place the next time the window opens.
Example — Sun–Thu, 00:00 → 04:00 (in Dispatcharr’s timezone):
| Setting | Value |
|---|---|
| Scheduled Check Times | 0 0 * * 0-4 |
| Dispatcharr Time Zone (Settings → General) | America/Chicago |
| Use Windowed Schedule | ✅ on |
| Window End Mode | duration |
| Window Duration (hours) | 4 |
What happens:
/data/iptv_checker_pending_resume.json.View Check Progress keeps the percentage and ETA honest through retry passes — it no longer snaps to 100% at the end of the first pass.Most IPTV providers cap concurrent connections per account (often 1–4). Two settings let you stay under the cap while still running checks in parallel:
3× this value.If you see a lot of “Server Error” or “Stream Unreachable” results that turn alive on retry, raise the cooldown or drop the worker count.
DELETE in the confirmation field AND confirming via dialogBlank-Screen Sample (seconds) with ffmpeg -vf blackdetect=d=<min>:pic_th=0.98. If a continuous black run of at least Continuous Blank Required (seconds) is found, the stream is reclassified Dead with error_type = Black Screen and its stream_stats are cleared — so it flows through rename/move/delete, CSV, and webhook exactly like any other dead stream.Blank-Screen ffmpeg Timeout (seconds), the stream is left Alive — a tooling glitch never falsely kills a working channel.[Blank] tag, Black Screens group) and are excluded from the regular Dead rename/move so they aren’t double-tagged. They stay status=Dead, so Delete Dead Channels still removes them.[DEAD]/[Slow]/[Blank] and exiled to a Graveyard / Slow / blank-screen group, there was no automatic way back when the stream recovered. It stayed tagged and stranded./data/iptv_checker_channel_state.json the moment a Move action exiles the channel (it never records a managed destination group as the “original”, and never overwrites an existing capture).[HD] quality suffix and was never marked is not touched.restored count.discord.com / discordapp.com) and sends a native message Discord renders directly. No need to append /slack or edit the URL.{plugin, event, total, alive, dead, skipped, timestamp}; scheduled runs with restore enabled also include restored)User-Agent header so Cloudflare-fronted services (like Discord) don’t silently reject the requesturllib)For any plugin issues, try refreshing your browser (F5) and then restarting the Dispatcharr container:
docker restart dispatcharr
“Plugin not found” Errors:
Scheduler Not Running:
pytz is installed in the containerdocker logs dispatcharr | grep -i schedulerdocker logs dispatcharr | grep "Background scheduler thread started"Stream Check Failures:
docker restart dispatcharrProgress Stuck or Not Updating:
/data/iptv_checker_results.json/data/iptv_checker_loaded_channels.json/data/iptv_checker_progress.json/data/iptv_checker_channel_state.json/data/iptv_checker_settings.json/data/exports/iptv_checker_results_YYYYMMDD_HHMMSS.csvThis plugin uses calver 1.26.{DDD}{HHMM} (UTC day-of-year + UTC hour-minute), matching the Lineuparr / Channel-Mapparr / EPG-Janitor cohort. Releases prior to 1.26.1081815 used semver (0.X.Y). See the release notes for full changelogs.
When reporting issues:
docker logs dispatcharr | grep "IPTV Checker")Pull requests welcome. To submit changes:
1.26.{DDD}{HHMM}, UTC) with python bump_version.py — it updates iptv_checker/plugin.json, iptv_checker/plugin.py, and the “Current Version” line in CLAUDE.md in one shot, and verifies they agree.python -m pytest tests -q && python -m ruff check .git add -A && git commit -m "v<version> — <summary>"
git tag <version> && git push origin main --tags
CI (.github/workflows/ci.yml) re-runs the checks, builds iptv_checker-v<version>.zip (excluding __pycache__), and attaches it to the GitHub release automatically when the tag lands. See DEVELOPMENT.md for the full workflow.
Updates also need to be PR’d to Dispatcharr/Plugins so the plugin updates in users’ Dispatcharr UIs. The repo’s GitHub Actions validator enforces strict rules — failing any blocks the merge:
| Check | Requirement |
|---|---|
| PR title | Must match [iptv-checker]: <description>. The validate-title job fails on any other format. Most common trip-up. |
| Version bump | plugin.json version must be greater than the version on upstream main for any code/asset change. Metadata-only edits are exempt. |
Required plugin.json fields |
name, version, description, author, license (SPDX). |
| Authorship | PR author’s GitHub username must appear in author or maintainers, or the close-unauthorized job auto-closes the PR. |
| Folder name | plugins/iptv-checker/ (lowercase-kebab) — note this differs from the iptv_checker/ snake_case used inside this repo’s zip. |
Workflow:
# In your fork of Dispatcharr/Plugins:
git fetch upstream && git checkout main && git merge upstream/main --ff-only && git push origin main
git checkout -b iptv-checker-v<version>
cp <this-repo>/plugin.{py,json} plugins/iptv-checker/
git commit -am "[iptv-checker]: ..."
git push -u origin iptv-checker-v<version>
gh pr create --repo Dispatcharr/Plugins --base main \
--title "[iptv-checker]: Bump to v<version> — <summary>" \
--body "..."
On merge, upstream automation builds the zip + checksums and updates manifest.json on the releases branch — do not touch that branch manually.