diff --git a/plugins/iptv-checker/README.md b/plugins/iptv-checker/README.md index 670a21c1..6359b5d8 100644 --- a/plugins/iptv-checker/README.md +++ b/plugins/iptv-checker/README.md @@ -15,454 +15,102 @@ ![Last Commit](https://img.shields.io/github/last-commit/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin) ![License](https://img.shields.io/github/license/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin) +## Warning: back up your database first -## Warning: Backup Your Database -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.](https://dispatcharr.github.io/Dispatcharr-Docs/troubleshooting/?h=backup#how-can-i-make-a-backup-of-the-database)** - -## Features - -- **Stream Status Checking:** Verify if IPTV streams are alive or dead with smart retry logic -- **Blank-Screen Detection (opt-in):** Catch streams that pass ffprobe (valid resolution/codec/bitrate) but decode to a pure black picture. When enabled, each alive stream gets a second `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+) -- **Restore Recovered Channels (self-healing):** When a previously marked channel comes back **Alive**, strip the plugin's name tags (`[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+) -- **Wildcard Group Matching:** Target groups using patterns like `US-*`, `*Sports*`, or `Movies-??` -- **Automated Scheduler:** Schedule stream checks using cron expressions with timezone support -- **Post-Check Automation:** Automatically rename, move, delete, export, and webhook after scheduled checks -- **Metadata Synchronization:** Sync technical stream data (codecs, bitrate, sample rate) back to Dispatcharr -- **Background Processing:** Stream checks run in background threads with cancellation support -- **Alternative Streams:** Option to check backup/alternative streams associated with channels -- **Technical Analysis:** Extract resolution, framerate, and video format information -- **Configurable FFprobe:** Custom path, analysis flags, and analysis duration settings -- **Direct ORM Integration:** Runs inside Dispatcharr with direct database access — no API credentials needed -- **Channel Management:** Automated renaming, moving, and deletion of channels based on results -- **Group-Based Operations:** Work with existing Dispatcharr channel groups -- **Smart Loading:** Asynchronous loading for large channel lists to prevent interface timeouts -- **Real-Time Progress Tracking:** Live ETA calculations with adaptive WebSocket notifications -- **Smart Retry System:** Timeout streams queued and retried after other streams for better success rates -- **Enhanced Error Categorization:** Detailed error types (Timeout, 404, 403, Connection Refused, etc.) -- **Webhook Notifications:** Send HTTP POST notifications after scheduled checks complete -- **Auto-Delete Dead Channels:** Permanently remove dead channels with safety confirmation gate -- **CSV Exports:** Export results with comprehensive statistics and URL masking. Scheduled sessions emit a CSV every time a run ends — including windowed runs that close mid-list — so each window has its own audit record (v1.26.1191257+; the hoist regressed via the subfolder/root sync drift and was re-applied in v1.26.1212238). The header no longer duplicates the `ffprobe_monitoring_seconds` column, and the audit preamble's `FFprobe Flags:` line now reports the flags actually used (v1.26.1741204+). -- **Video Bitrate Reporting:** Per-stream `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`. -- **Window-Aware Retry Pass:** When a windowed run closes mid-list, the parallel/sequential retry passes now also bail on `_past_window_end()` so transient-error retries cannot overshoot the window boundary (v1.26.1212238+; previously observed up to 14 minutes of overrun). -- **Adaptive Rate-Limit Guard:** Detects upstream HTTP 429 responses, classifies them as **Skipped (Rate Limited)** instead of Dead so destructive actions never act on a throttled stream, and applies an exponentially-doubling cooldown when 429s spike (v1.26.1181025+). The cooldown counter is shared across the whole container — Dispatcharr's multiple worker processes can no longer reset it independently (v1.26.1181126+). -- **Audio-Only / Radio Streams Skipped:** Streams that ffprobe validates but that carry **no video track** (e.g. radio stations like BBC Radio 1) are classified **Skipped (`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. -- **PAL-Safe Low-Framerate Threshold:** The low-framerate flag now triggers below **24 fps** (was 30 fps), so 25 fps PAL/European broadcasts and 24 fps film-rate feeds are no longer mis-tagged `[Slow]` — only genuinely choppy streams qualify (v1.26.1741204+). -- **Single-Scheduler Election:** Dispatcharr runs ~9 separate Python processes; a file-based PID lock at `/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 renames, moves and can permanently delete channels. Before using it, +**[make a backup of your Dispatcharr database](https://dispatcharr.github.io/Dispatcharr-Docs/troubleshooting/?h=backup#how-can-i-make-a-backup-of-the-database)**. -## Requirements +## What it does -### System Dependencies -This plugin requires **ffmpeg** and **ffprobe** to be installed in the Dispatcharr container for stream analysis. The scheduler feature requires **pytz** (usually included). +Probes every stream behind your channels with `ffprobe`, records what it finds, and lets you act on +the result. -**Default Locations:** -- **ffprobe:** `/usr/local/bin/ffprobe` (plugin default, configurable) -- **ffmpeg:** `/usr/local/bin/ffmpeg` +**It answers three questions per stream**, and keeps them apart because acting on the wrong one +deletes channels that work: -**Verify Installation:** -```bash -docker exec dispatcharr which ffprobe -docker exec dispatcharr which ffmpeg -``` +- **Alive.** The stream plays. Resolution, framerate, codecs and bitrate are recorded and synced + back into Dispatcharr so the channel menu can show them. +- **Dead.** The stream does not play, or it plays but shows nothing worth watching: a blank picture, + a frozen picture, silence, or a fixed-duration placeholder file. Those last four are opt-in. +- **Skipped.** The checker could not judge it. That covers a provider rate-limit response, a + radio station with no video track, and hosts `ffprobe` cannot read at all. **Skipped is never + treated as dead**, so nothing destructive touches a stream that was merely throttled. -### Dispatcharr Setup -- Active Dispatcharr installation (v0.20.0+) with configured channels and groups -- Channel groups containing IPTV streams to analyze +**A channel is judged by all of its streams, never by one of them.** Most channels carry a primary +and one or more backups, and Dispatcharr fails over between them. A channel is only reported dead +when **every** stream failed, so one dead backup never marks a working channel for deletion. -No API credentials are needed — the plugin runs inside Dispatcharr with direct database access. +Other things it does: -## Installation +- **Scheduled checks**, including overnight windows that pause at a set time and resume where they + left off on the next window. +- **An HTML report** written to `/config/iptv_checker/report.html`, grouped by what you should do + about each finding, and optionally emailed through the + [Newsflasharr](https://github.com/PiratesIRC) plugin. +- **CSV export** with a full settings preamble, so every run leaves an audit record. +- **Rename, move, restore and delete** actions, each with its own confirmation. +- **Self-healing**: a channel that comes back to life is renamed back and moved to its original + group automatically. -1. Log in to Dispatcharr's web UI -2. Navigate to **Plugins** -3. Click **Import Plugin** and upload the plugin zip file -4. Enable the plugin after installation +## Requirements + +- Dispatcharr v0.20.0 or newer, with channels and groups already configured. +- **`ffprobe`** in the container. **`ffmpeg`** as well if you enable blank-screen, frozen-video or + silent-audio detection. +- `pytz` for the scheduler, which is normally already present. -### Updating the Plugin - -To update the plugin: - -1. **Remove Old Plugin** - * Navigate to **Plugins** in Dispatcharr - * Click the trash icon next to the old plugin - * Confirm deletion - -2. **Restart Dispatcharr** - * Log out of Dispatcharr - * Restart the Docker container: - ```bash - docker restart dispatcharr - ``` - -3. **Install Updated Plugin** - * Log back into Dispatcharr - * Navigate to **Plugins** - * Click **Import Plugin** and upload the new plugin zip file - * Enable the plugin after installation - -4. **Verify Installation** - * Check that the plugin appears in the plugin list - * Reconfigure your settings if needed - -## Settings Reference - -### Core Settings - -| 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 | - -### Channel Management Settings - -| 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 remain `status=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]`). - -### FFprobe Settings - -| 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. | - -### Blank-Screen Detection - -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**). | - -### Parallel Checking - -| 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. | - -### Webhook - -| Setting | Type | Default | Description | -|---------|------|---------|-------------| -| Webhook URL | string | *(empty)* | HTTP POST URL for notifications after scheduled checks | - -### Scheduler Settings - -| 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 `UTC` only 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 a `restored` count) | - -### Destructive Settings - -| Setting | Type | Default | Description | -|---------|------|---------|-------------| -| Auto-Delete Confirmation | string | *(empty)* | Type `DELETE` to enable auto-delete of dead channels | - -## Usage Guide - -### Step-by-Step Workflow - -1. **Configure Preferences** - - Set your **Group(s) to Check** (supports wildcards like `US-*`) - - Optionally set **Group(s) to EXCLUDE** to skip groups (applied after the include filter) - - Configure checking preferences (Alternative Streams, Timeouts, Retries) - - Optionally enable **Parallel Checking** for faster processing - - Click **Save Settings** - -2. **Validate Settings** *(Recommended)* - - Click **Run** on **Validate Settings** - - Verifies group names, FFprobe path, and configuration - -3. **Configure Schedule** *(Optional)* - - Set **Scheduled Check Times** using cron format - - Set your timezone in **Dispatcharr → Settings → General → Time Zone** (the scheduler uses it automatically) - - Enable post-check automation options as desired - - Click **Run** on **Update Schedule** to activate - -4. **Load Channel Groups** - - Click **Run** on **Load Group(s)** - - Review available groups and channel counts - - Large lists (>100 channels) load in the background - -5. **Check Streams** - - Click **Run** on **Start Stream Check** - - Processing runs in the background - - Returns immediately with estimated completion time - - Metadata is automatically synced to the database during checks - -6. **Monitor Progress** - - Click **View Check Progress** for real-time status with ETA - - Use **Cancel Stream Check** to stop a running check - - Progress updates continue even if browser times out - -7. **View Results** - - Click **View Last Results** for summary when complete - - Shows alive/dead counts and format distribution - - Use **View Results Table** for detailed tabular format - -8. **Manage Channels** - - Use channel management actions based on results - - All destructive operations include confirmation dialogs - - GUI automatically refreshes after changes - -9. **Export Data** - - Click **Export Results to CSV** to save analysis data - - CSV includes comprehensive header comments with settings and stats - -## Action Reference - -### Setup & Validation -- **Validate Settings:** Verify configuration, group names, and FFprobe path -- **Update Schedule:** Apply schedule settings and restart the scheduler -- **Check Scheduler Status:** View current scheduler state and next run time - -### Core Stream Checking -- **Load Group(s):** Load channels from specified groups (async for large lists) -- **Start Stream Check:** Begin checking all loaded streams in background thread -- **View Check Progress:** View current progress and ETA of the running check -- **Cancel Stream Check:** Stop the currently running stream check (confirmation dialog; queued and in-flight streams abort, already-probed results are kept) -- **View Last Results:** View summary of the last completed stream check, including the date/time the check was produced - -### Channel Management -- **Rename Dead Channels:** Apply rename format to dead streams (excludes black/blank) -- **Move Dead Channels to Group:** Relocate dead channels (excludes black/blank) -- **Delete Dead Channels:** Permanently remove dead channels (requires confirmation; includes black/blank) -- **Rename Blank-Screen Channels:** Apply `[Blank]` format to channels detected as a blank screen -- **Move Blank-Screen Channels to Group:** Relocate blank-screen channels to the `Black Screens` group -- **Rename Low Framerate Channels:** Apply rename format to slow streams (<24fps; PAL 25fps / film 24fps excluded) -- **Move Low Framerate Channels:** Relocate slow channels -- **Add Video Format Suffix:** Apply format tags ([UHD], [FHD], [HD], [SD]) -- **Restore Recovered Channels:** For channels Alive again but previously marked, strip all plugin tags from the name and move them back to their **exact original group** (captured when they were first moved). Original group remembered in `/data/iptv_checker_channel_state.json`. If the original group was deleted, the name is still restored. - -### Data & Maintenance -- **View Results Table:** Detailed tabular format for copy/paste -- **Export Results to CSV:** Save analysis data with comprehensive statistics -- **Clear CSV Exports:** Delete all CSV files in /data/exports/ -- **Cleanup Orphaned Tasks:** Clean up stale background tasks - -## Advanced Features - -### Wildcard Group Matching -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. -- Multiple patterns: `US-*, UK-*, *Sports*` (comma-separated) - -### Excluding Groups (v1.26.1721733+) -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: -- Check `US-*` but skip the pay-per-view groups → Check `US-*`, Exclude `US-PPV-*` -- Check everything except one group → leave Check blank, Exclude `Adult` -- Matching is case-sensitive (same as the include field). If a group matches both fields, **exclude wins**. If the filters leave nothing, the load reports an error rather than silently checking all groups. - -### Automated Scheduling -- **Cron Support:** Configure checks using standard cron syntax (e.g., `0 4 * * *`) -- **Timezone Aware:** Schedules run according to **Dispatcharr's** configured timezone (Settings → General → Time Zone); no separate plugin timezone to keep in sync (v1.26.1721651+) -- **Post-Check Automation:** Chain any combination of rename, move, delete, export, and webhook actions -- **Conflict Prevention:** Scheduler queues jobs if a manual check is already running - -#### Windowed Schedules with Resume - -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: - -- The window opens at midnight Sun–Thu and runs for 4 hours. -- Per-stream progress is persisted to `/data/iptv_checker_pending_resume.json`. -- If the window closes before the channel list is finished, post-check actions (rename / move / delete / webhook) are **deferred** to the window that completes the list. -- If the container restarts mid-window, the original window end is preserved and the check resumes immediately rather than waiting for the next cron fire. -- Click **Reset Window Progress** to wipe pending state and start fresh on the next window. - -### Metadata Synchronization -- **Database Sync:** Automatically updates Dispatcharr with technical stream details from FFprobe analysis -- **Synced Fields:** Video/Audio Codecs, Resolution, Bitrates, Sample Rates, Audio Channels, Stream Types - -### Smart Retry System -- Timeout streams queued and retried after processing other streams (not immediately) -- Provides server recovery time between retry attempts -- Retry queue processes every 4 streams to balance throughput and recovery -- Multiple retry attempts per stream based on configured retry count -- **Retry-aware ETA:** `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. - -### Provider Concurrency Limits -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: - -- **Number of Parallel Workers** — keep this **below** your account's cap. For a 4-stream account, 2 workers leaves headroom for viewing while a check runs. -- **Per-Stream Cooldown** — 2 s by default. Each worker waits this long after finishing before picking up the next stream, so the upstream slot has time to release. Retry passes wait `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. - -### Auto-Delete Dead Channels -- Permanently deletes channels with dead streams from the database -- **Safety gates:** Requires typing `DELETE` in the confirmation field AND confirming via dialog -- Can be automated via scheduler with the same confirmation gate - -### Blank-Screen Detection -- **The problem it solves:** some streams return a perfectly valid signal (resolution, codec, framerate, bitrate) yet only ever display a blank screen. ffprobe can't catch these — it reads metadata, not pixels — so they're reported Alive. -- **How it works:** when **Detect Blank-Screen Streams** is enabled, every stream that passes ffprobe is decoded for `Blank-Screen Sample (seconds)` with `ffmpeg -vf blackdetect=d=: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. -- **Fail-open by design:** if ffmpeg is missing, errors, or exceeds `Blank-Screen ffmpeg Timeout (seconds)`, the stream is left **Alive** — a tooling glitch never falsely kills a working channel. -- **Cost:** ~5–10 s of extra decode per **alive** stream; dead/skipped streams are skipped. Runs on both manual and scheduled checks when enabled, and respects the windowed-schedule boundary. -- **Tuning false positives:** a channel that's legitimately black for several seconds (fade-from-black intro, station slate) can trip detection. Raise **Continuous Blank Required** or **Sample** seconds if needed. Only pure black is detected; dark-grey/error-card screens are not. -- **Separate category (v1.26.1721554+):** when detection is on, blank channels are handled by the dedicated **Rename/Move Blank-Screen** actions (`[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.** - -### Restore Recovered Channels (self-healing) -- **The problem it solves:** once a channel is renamed `[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. -- **How it works:** for each channel whose latest status is **Alive** but which was previously marked by this plugin (it has a stored original group, or its name still carries a plugin status tag), the restore action strips **all** plugin name tags back to a clean base name and moves it back to its **exact original group**. The original group is captured to `/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). -- **Eligibility is conservative:** a healthy channel that merely has a `[HD]` quality suffix and was never marked is **not** touched. -- **Manual or scheduled:** run **Restore Recovered Channels** on demand, or enable the scheduler toggle — it runs **first** each scheduled check (heal before re-marking). The webhook payload gains a `restored` count. -- **Edge cases:** if the original group was deleted in the meantime, the name is still restored and the channel is left where it is (a warning is logged). Deleting a dead channel prunes its stored state. -- **Operational note:** a channel parked in a Graveyard / Slow / blank-screen group is only re-checked — and therefore only restorable — if your scan scope **includes** that group. Add the managed groups to your scheduled scan scope (or run a full-scope scan) so self-healing actually fires. - -### Webhook Notifications -- Sends an HTTP POST after scheduled checks complete -- **Discord:** paste your Discord webhook URL as-is — the plugin auto-detects Discord hosts (`discord.com` / `discordapp.com`) and sends a native message Discord renders directly. No need to append `/slack` or edit the URL. -- **Custom endpoints / integrations:** non-Discord URLs receive a machine-readable JSON payload (`{plugin, event, total, alive, dead, skipped, timestamp}`; scheduled runs with restore enabled also include `restored`) -- Sends an explicit `User-Agent` header so Cloudflare-fronted services (like Discord) don't silently reject the request -- No additional dependencies (uses Python's built-in `urllib`) - -## Troubleshooting - -### First Step: Restart Container -**For any plugin issues, try refreshing your browser (F5) and then restarting the Dispatcharr container:** ```bash -docker restart dispatcharr +docker exec dispatcharr which ffprobe +docker exec dispatcharr which ffmpeg ``` -### Common Issues - -**"Plugin not found" Errors:** -- Refresh browser page (F5) -- Restart Dispatcharr container - -**Scheduler Not Running:** -- The scheduler starts automatically on container boot — no UI action needed -- Verify `pytz` is installed in the container -- Check cron syntax (5 fields required: minute hour day month weekday) -- Use **Check Scheduler Status** to verify state -- Check logs: `docker logs dispatcharr | grep -i scheduler` -- Confirm scheduler started on boot: `docker logs dispatcharr | grep "Background scheduler thread started"` - -**Stream Check Failures:** -- Increase connection timeout and/or probe timeout for slow streams -- Adjust retry count for unstable connections -- Try enabling parallel mode for better timeout handling -- Restart container: `docker restart dispatcharr` - -**Progress Stuck or Not Updating:** -- Stream checking runs in background and continues even if browser times out -- Use **View Check Progress** to check current status -- Use **Cancel Stream Check** if needed -- Check container logs for actual processing status - -## File Locations - -- **Results:** `/data/iptv_checker_results.json` -- **Loaded Channels:** `/data/iptv_checker_loaded_channels.json` -- **Progress State:** `/data/iptv_checker_progress.json` -- **Channel State (original group for restore):** `/data/iptv_checker_channel_state.json` -- **Settings:** `/data/iptv_checker_settings.json` -- **CSV Exports:** `/data/exports/iptv_checker_results_YYYYMMDD_HHMMSS.csv` +No API credentials are needed. The plugin runs inside Dispatcharr with direct database access. -## Versioning +## Install -This 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](https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin/releases) for full changelogs. +1. Log in to Dispatcharr and go to **Plugins**. +2. Click **Import Plugin** and upload the release zip. +3. Enable the plugin. -## Contributing +**To update**, delete the old plugin in the Plugins page, restart the container +(`docker restart dispatcharr`), then import the new zip. Your settings are preserved. -When reporting issues: -1. Include Dispatcharr version information -2. Provide relevant container logs (`docker logs dispatcharr | grep "IPTV Checker"`) -3. Test with small channel groups first -4. Document specific error messages and error types -5. Note current progress from **View Last Results** +## Quick start -Pull requests welcome. To submit changes: +1. Set **Channel Groups** and **Channel Groups Mode**. Leave the box empty to check every group. +2. Click **Validate** to confirm the plugin can see your groups. It reports how many groups will be + checked. +3. Click **Load Groups**, then **Start Check**. +4. Watch **View Progress**, then read **View Results** or **Email Report**. -### To this repo (PiratesIRC/Dispatcharr-IPTV-Checker-Plugin) +## Documentation -1. Bump the version (calver `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. -2. Validate: `python -m pytest tests -q && python -m ruff check .` -3. Commit, tag, and push: +**[Full user guide](https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin/blob/main/docs/USER-GUIDE.md)** covers every setting and button, the detection modes, +scheduling and windowed runs, the HTML report and email delivery, and troubleshooting. -```bash -git add -A && git commit -m "v" -git tag && git push origin main --tags -``` +- [Development workflow](https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin/blob/main/DEVELOPMENT.md) +- [Release notes](https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin/releases) -CI (`.github/workflows/ci.yml`) re-runs the checks, builds `iptv_checker-v.zip` (excluding `__pycache__`), and attaches it to the GitHub release automatically when the tag lands. See `DEVELOPMENT.md` for the full workflow. +## Versioning -### To the upstream marketplace (Dispatcharr/Plugins) +Calver `1.26.{DDD}{HHMM}`, being the UTC day-of-year and UTC hour-minute, matching the Lineuparr, +Channel-Mapparr and EPG-Janitor plugins. Releases before `1.26.1081815` used semver. -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: +## Contributing -| Check | Requirement | -|-------|-------------| -| PR title | Must match `[iptv-checker]: `. 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. | +Issues and pull requests are welcome. When reporting a problem, please include your Dispatcharr +version, the relevant container logs +(`docker logs dispatcharr | grep "IPTV Checker"`), and the exact error text. -Workflow: +Contribution steps, the CI gates, and how updates reach the Dispatcharr plugin marketplace are in +[DEVELOPMENT.md](https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin/blob/main/DEVELOPMENT.md). -```bash -# 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 -cp /plugin.{py,json} plugins/iptv-checker/ -git commit -am "[iptv-checker]: ..." -git push -u origin iptv-checker-v -gh pr create --repo Dispatcharr/Plugins --base main \ - --title "[iptv-checker]: Bump to v" \ - --body "..." -``` +## Disclaimer + +This plugin makes bulk changes to your channel database, including permanent deletion when you +enable it. Test on a small group first, keep a database backup, and read what an action says it will +do before confirming it. + +## License -On merge, upstream automation builds the zip + checksums and updates `manifest.json` on the `releases` branch — do not touch that branch manually. +MIT. See [LICENSE](https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin/blob/main/LICENSE). diff --git a/plugins/iptv-checker/plugin.json b/plugins/iptv-checker/plugin.json index d5b1ed79..5b86f645 100644 --- a/plugins/iptv-checker/plugin.json +++ b/plugins/iptv-checker/plugin.json @@ -1,631 +1,14 @@ { "name": "IPTV Checker", - "version": "1.26.1741204", - "description": "A Dispatcharr Plugin that goes through a playlist to check IPTV channels", + "version": "1.26.2181303", + "description": "Check IPTV stream status and quality with ffprobe, then rename, move, restore or delete channels based on the result. Judges a channel by all of its streams, so a working backup never marks it dead.", "author": "PiratesIRC", + "license": "MIT", "logo": "logo.png", "icon": "logo.png", - "license": "MIT", + "source_type": "external", "repo_url": "https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin", - "min_dispatcharr_version": "v0.20.0", "help_url": "https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin", - "fields": [ - { - "id": "_section_scope", - "label": "\ud83d\udce5 Group Selection", - "type": "info", - "description": "Which Dispatcharr groups to check and whether to include backup streams." - }, - { - "id": "group_names", - "label": "\ud83d\udcc2 Group(s) to Check (comma-separated, wildcards supported)", - "type": "string", - "default": "", - "help_text": "The name of the Dispatcharr Channel Group(s) to check. Supports wildcards: US-* matches US-Movies, US-Sports, etc. Leave blank to check all groups." - }, - { - "id": "group_names_exclude", - "label": "\ud83d\udcc2 Group(s) to EXCLUDE (comma-separated, wildcards supported)", - "type": "string", - "default": "", - "help_text": "Groups to skip even if they match 'Group(s) to Check'. Supports wildcards (e.g. US-PPV-*). Applied AFTER the include filter; with a blank include this means 'all groups except these'. If a group matches both fields, exclude wins. Leave blank to exclude nothing." - }, - { - "id": "check_alternative_streams", - "label": "\ud83d\udd04 Check Alternative Streams", - "type": "boolean", - "default": true, - "help_text": "Check all alternative/backup streams for each channel in addition to the primary stream. This will significantly increase check time." - }, - { - "id": "only_visible_channels", - "label": "\ud83d\udc41\ufe0f Only Check Visible Channels", - "type": "boolean", - "default": false, - "help_text": "Only check channels that are enabled (visible) in at least one Channel Profile. Channels disabled in every profile (or not assigned to any profile) are skipped. Useful for ignoring hidden/archived channels." - }, - { - "id": "_section_check_behavior", - "label": "\ud83d\udd0d Check Behavior", - "type": "info", - "description": "Timeouts, retries, concurrency, and cooldown. Keep worker count below your provider's concurrent-connection limit." - }, - { - "id": "timeout", - "label": "\u23f1\ufe0f Connection Timeout (seconds)", - "type": "number", - "default": 10, - "help_text": "Network connection timeout. Use for detecting dead streams that refuse connections. Default: 10" - }, - { - "id": "probe_timeout", - "label": "\ud83d\udd0d Probe Timeout (seconds)", - "type": "number", - "default": 20, - "help_text": "Maximum time to wait for stream to start delivering data after connection. Increase this for streams with slow startup (5+ seconds). Default: 20" - }, - { - "id": "dead_connection_retries", - "label": "\ud83d\udd04 Dead Connection Retries", - "type": "number", - "default": 3, - "help_text": "Number of times to retry checking a stream if it appears to be dead. Default: 3" - }, - { - "id": "enable_parallel_checking", - "label": "\u26a1 Enable Parallel Stream Checking", - "type": "boolean", - "default": true, - "help_text": "Check multiple streams simultaneously for significantly faster processing. Recommended for large channel lists." - }, - { - "id": "parallel_workers", - "label": "\ud83d\udc77 Number of Parallel Workers", - "type": "number", - "default": 2, - "help_text": "Number of streams to check simultaneously when parallel checking is enabled. Default: 2. Higher values = faster but more resource-intensive. Keep BELOW your provider's max concurrent connection limit." - }, - { - "id": "stream_check_delay", - "label": "\u23f3 Per-Stream Cooldown (seconds)", - "type": "number", - "default": 2, - "help_text": "Cooldown each worker waits after finishing a stream check, before picking up the next. Prevents provider rate-limiting / slot-reuse errors when your account has a concurrent-connection limit. Default: 2. Retry passes use 3x this value." - }, - { - "id": "_section_black_screen", - "label": "\u2b1b Blank-Screen Detection", - "type": "info", - "description": "Optionally decode a few seconds of each Alive stream with ffmpeg and mark it Dead if it is a pure blank (all-black) screen. Costs extra CPU/time per Alive stream." - }, - { - "id": "black_screen_detection", - "label": "\u2b1b Detect Blank-Screen Streams", - "type": "boolean", - "default": false, - "help_text": "When ON, every stream that passes ffprobe is decoded for a few seconds with ffmpeg's blackdetect filter; pure-blank (all-black) streams are marked Dead and reported under the `Black Screen` error type so rename/move/delete actions handle them. Adds ~5-10s per Alive stream. Fail-open: if ffmpeg is missing or errors, the stream stays Alive. Very-dark-but-not-black 'no signal' slates are NOT detected." - }, - { - "id": "black_screen_sample_seconds", - "label": "\u2b1b Blank-Screen Sample (seconds)", - "type": "number", - "default": 6, - "help_text": "How many seconds of video to decode when testing for a blank screen. Longer = more reliable but slower. Default: 6" - }, - { - "id": "black_screen_min_black_seconds", - "label": "\u2b1b Continuous Blank Required (seconds)", - "type": "number", - "default": 3, - "help_text": "Minimum continuous run of blank (all-black) video (within the sample) required to flag a stream as blank. Should be a few seconds less than the sample to allow for connection/keyframe latency. Default: 3" - }, - { - "id": "black_screen_ffmpeg_timeout", - "label": "\u2b1b Blank-Screen ffmpeg Timeout (seconds)", - "type": "number", - "default": 20, - "help_text": "Hard wall-clock cap on the ffmpeg blank-screen decode (connection + sampling). If exceeded, the stream is left Alive. Default: 20" - }, - { - "id": "_section_post_check", - "type": "info", - "label": "\ud83c\udff7\ufe0f Post-Check Actions", - "description": "What to do with channels after a check completes: rename / move them by category, and restore recovered ones. Configure the tags and destination groups below; enable auto-apply on a schedule in 'Scheduling & Automation'." - }, - { - "id": "_section_dead", - "label": "\u270f\ufe0f Dead Channel Handling", - "type": "info", - "description": "How to rename and relocate channels that fail every stream check." - }, - { - "id": "dead_rename_format", - "label": "\ud83d\udc80 Dead Channel Rename Format", - "type": "string", - "default": "{name} [DEAD]", - "placeholder": "[DEAD] {name}", - "help_text": "Format for renaming dead channels. Use {name} as placeholder for the original channel name. Examples: '[DEAD] {name}', '{name} [DEAD]', '[X] {name} [DEAD]'" - }, - { - "id": "move_to_group_name", - "label": "\u26b0\ufe0f Move Dead Channels to Group", - "type": "string", - "default": "Graveyard", - "help_text": "Enter the name for the group to move dead channels into." - }, - { - "id": "_section_black", - "label": "\u2b1b Blank-Screen Handling", - "type": "info", - "description": "How to rename and relocate channels detected as a pure blank (all-black) screen. Requires 'Detect Blank-Screen Streams' to be ON. These channels are handled separately from [DEAD] channels (excluded from the Dead rename/move actions)." - }, - { - "id": "black_screen_rename_format", - "label": "\u2b1b Blank-Screen Channel Rename Format", - "type": "string", - "default": "{name} [Blank]", - "placeholder": "{name} [Blank]", - "help_text": "Format for renaming blank-screen channels. Use {name} as the placeholder. Blank-screen channels are excluded from the Dead rename/move actions so they are not double-tagged." - }, - { - "id": "move_black_screen_group", - "label": "\u2b1b Move Blank-Screen Channels to Group", - "type": "string", - "default": "Black Screens", - "help_text": "Enter the name for the group to move blank-screen channels into." - }, - { - "id": "_section_low_fps", - "label": "\ud83d\udc0c Low Framerate Handling", - "type": "info", - "description": "How to rename and relocate channels whose streams deliver under 30 fps." - }, - { - "id": "low_framerate_rename_format", - "label": "\ud83d\udc0c Low Framerate Rename Format - Less than 30fps", - "type": "string", - "default": "{name} [Slow]", - "placeholder": "[SLOW] {name}", - "help_text": "Format for renaming low framerate channels. Use {name} as placeholder for the original channel name." - }, - { - "id": "move_low_framerate_group", - "label": "\ud83d\udcc1 Move Low Framerate Channels to Group", - "type": "string", - "default": "Slow", - "help_text": "Enter the name for the group to move low framerate channels into." - }, - { - "id": "_section_format", - "label": "\ud83c\udfac Format Suffixes", - "type": "info", - "description": "Append a resolution tag (e.g. [HD], [FHD]) to channel names based on probed format." - }, - { - "id": "video_format_suffixes", - "label": "\ud83c\udfac Add Video Format Suffixes - [UHD], [FHD], [HD], [SD], [Unknown]", - "type": "string", - "default": "UHD, FHD, HD, SD, Unknown", - "help_text": "A comma-separated list of formats to add as a suffix (e.g., [HD]) to channel names." - }, - { - "id": "_section_restore", - "label": "\u267b\ufe0f Restore Recovered Channels", - "type": "info", - "description": "Channels that come back Alive can be auto-cleaned: plugin name tags ([DEAD]/[Slow]/[Blank]/quality) are stripped and the channel is moved back to the original group captured when it was first moved. Use the 'Restore Recovered Channels' action or the scheduler toggle. NOTE: a channel parked in a managed group (Graveyard / Slow / blank-screen) is only re-checked (and thus restorable) if your scan scope includes that group." - }, - { - "id": "_section_webhook", - "label": "\ud83d\udd17 Webhook", - "type": "info", - "description": "Optional HTTP POST notification after every stream check completes." - }, - { - "id": "webhook_url", - "label": "\ud83d\udd17 Webhook URL", - "type": "string", - "default": "", - "placeholder": "http://localhost:9000/api/some/endpoint", - "help_text": "URL to send an HTTP POST request to after stream checks complete. Payload includes check summary (total, alive, dead counts). Leave blank to disable." - }, - { - "id": "_section_scheduling", - "label": "\u23f0 Scheduling & Automation", - "type": "info", - "description": "Run checks on a cron schedule and optionally apply rename / move / delete / webhook actions automatically afterwards." - }, - { - "id": "scheduled_times", - "label": "\u23f0 Scheduled Check Times (Cron Format)", - "type": "string", - "default": "", - "placeholder": "0 4 * * *,0 3 1 * *", - "help_text": "Comma-separated cron expressions. Format: 'minute hour day month weekday' (weekday: 0=Sunday, 6=Saturday). Examples: '0 4 * * *' (daily at 4 AM), '0 3 * * 0' (Sundays at 3 AM), '0 2 */2 * *' (every 2 days at 2 AM). Leave blank to disable scheduling. \ud83d\udcbe After editing this field, click \"\ud83d\udcbe Save Schedule\" below to apply the new schedule." - }, - { - "id": "schedule_window_enabled", - "label": "\ud83e\ude9f Use Windowed Schedule (run only during a time window)", - "type": "boolean", - "default": false, - "help_text": "When on, your Scheduled Times above mark the START of a run window. The check runs until the configured end-time / duration, then halts cleanly between streams. The next time the window opens it RESUMES from the same point (channels already checked are skipped). Group selection, alternative-stream and visible-only settings are read fresh at each window fire." - }, - { - "id": "schedule_end_mode", - "label": "\ud83d\uded1 Window End Mode", - "type": "select", - "default": "duration", - "options": [ - { - "label": "Run for a fixed duration (hours)", - "value": "duration" - }, - { - "label": "Run until a specific time (HH:MM)", - "value": "time" - } - ], - "help_text": "Choose how the window ends. Duration counts forward from the cron-fire time. Time mode supports past-midnight wrap (e.g. start 22:00, end 02:00)." - }, - { - "id": "schedule_duration_hours", - "label": "\u23f3 Window Duration (hours)", - "type": "number", - "default": 4, - "help_text": "Used when Window End Mode = duration. Decimals allowed (e.g. 3.5). Default: 4 hours." - }, - { - "id": "schedule_end_time", - "label": "\ud83d\udd53 Window End Time (HH:MM, scheduler timezone)", - "type": "string", - "default": "04:00", - "placeholder": "04:00", - "help_text": "Used when Window End Mode = time. 24-hour format in Dispatcharr's timezone. If earlier than the start time, the window is treated as wrapping past midnight." - }, - { - "id": "_section_auto_run", - "type": "info", - "label": "\u2699\ufe0f Auto-run After Scheduled Checks", - "description": "Which post-check actions to apply automatically after each scheduled check completes. (Manual actions are always available on the Actions tab.)" - }, - { - "id": "scheduler_export_csv", - "label": "\ud83d\udcbe Export CSV for Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Automatically export results to CSV after scheduled checks complete." - }, - { - "id": "scheduler_restore_channels", - "label": "\u267b\ufe0f Restore Recovered Channels After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "After each scheduled check, channels that are Alive again but were previously marked by this plugin have their name tags ([DEAD]/[Slow]/[Blank]/quality) stripped and are moved back to their original group. Runs first, before re-marking. NOTE: a channel parked in a managed group (Graveyard / Slow / blank-screen) is only re-checked (and thus restorable) if your scan scope includes that group." - }, - { - "id": "scheduler_rename_dead_channels", - "label": "\ud83d\udc80 Rename Dead Channels After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Automatically rename dead channels after scheduled checks complete." - }, - { - "id": "scheduler_rename_black_screen_channels", - "label": "\u2b1b Rename Blank-Screen Channels After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Automatically rename blank-screen channels after scheduled checks complete. Requires 'Detect Blank-Screen Streams' to be ON." - }, - { - "id": "scheduler_rename_low_framerate_channels", - "label": "\ud83d\udc0c Rename Low Framerate Channels After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Automatically rename low framerate channels after scheduled checks complete." - }, - { - "id": "scheduler_add_video_format_suffix", - "label": "\ud83c\udfac Add Video Format Suffix After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Automatically add video format suffixes to channels after scheduled checks complete." - }, - { - "id": "scheduler_move_dead_channels", - "label": "\u26b0\ufe0f Move Dead Channels After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Automatically move dead channels to the configured group after scheduled checks complete." - }, - { - "id": "scheduler_move_black_screen_channels", - "label": "\u2b1b Move Blank-Screen Channels After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Automatically move blank-screen channels to the configured group after scheduled checks complete." - }, - { - "id": "scheduler_move_low_framerate_channels", - "label": "\ud83d\udcc1 Move Low Framerate Channels After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Automatically move low framerate channels to the configured group after scheduled checks complete." - }, - { - "id": "scheduler_delete_dead_channels", - "label": "\ud83d\uddd1\ufe0f Delete Dead Channels After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "WARNING: This will PERMANENTLY DELETE dead channels from Dispatcharr after scheduled checks. This action is IRREVERSIBLE. Deleted channels and their stream associations cannot be recovered. The 'Auto-Delete Confirmation' field must also contain the word DELETE for this to take effect." - }, - { - "id": "auto_delete_confirmation", - "label": "\u26a0\ufe0f Auto-Delete Confirmation (type DELETE to enable)", - "type": "string", - "default": "", - "help_text": "SAFETY GATE: You must type the word DELETE (all caps) in this field to enable auto-deletion. This prevents accidental deletion of channels. If this field does not contain exactly DELETE, the delete function will refuse to run even if the toggle is enabled." - }, - { - "id": "scheduler_fire_webhook", - "label": "\ud83d\udd17 Send Webhook Notification After Scheduled Checks", - "type": "boolean", - "default": false, - "help_text": "Send an HTTP POST notification to the Webhook URL above after each scheduled check finishes. The payload includes total, alive, and dead channel counts. Useful for alerting Discord, Slack, or other external services. Requires a Webhook URL to be configured." - }, - { - "id": "_section_advanced", - "label": "\u2699\ufe0f Advanced", - "type": "info", - "description": "Paths and low-level overrides. Change only if your environment differs from the Dispatcharr defaults." - }, - { - "id": "ffprobe_flags", - "label": "\ud83d\udd0d FFprobe Analysis Flags", - "type": "string", - "default": "-show_streams,-show_packets,-loglevel error", - "placeholder": "-show_streams, -show_packets", - "help_text": "Comma-separated ffprobe flags for stream analysis. Options: -show_streams (basic validation), -show_packets (per-packet data; required for video bitrate calculation on live MPEG-TS streams), -loglevel error (errors only). Default: -show_streams,-show_packets,-loglevel error. Note: do not add -show_frames \u2014 when both -show_frames and -show_packets are passed, ffprobe returns a combined packets_and_frames array instead of separate packets[], which prevents the bitrate fallback from running." - }, - { - "id": "ffprobe_analysis_duration", - "label": "\u23f1\ufe0f FFprobe Analysis Duration (seconds)", - "type": "number", - "default": 8, - "help_text": "Duration to analyze stream when using -show_frames or -show_packets. Longer duration = more accurate bitrate/GOP analysis but slower checks. Probes that capture fewer than 30 video packets leave video_bitrate unset, so 8s gives slow-start streams enough room to clear the threshold. Default: 8 seconds" - }, - { - "id": "streamlink_hosts", - "label": "\u23fc Streamlink-Only Hosts (skip ffprobe)", - "type": "string", - "default": "youtube.com, youtu.be, twitch.tv, kick.com", - "placeholder": "youtube.com, twitch.tv", - "help_text": "Comma-separated host suffixes that ffprobe cannot validate (served via Streamlink). Streams on these hosts are marked 'Skipped' instead of 'Dead' so rename/move/delete actions leave them alone. Use full domains (e.g. 'youtube.com', not 'youtube'). Leave blank to use the defaults." - }, - { - "id": "ffprobe_path", - "label": "\ud83d\udccd FFprobe Path", - "type": "string", - "default": "/usr/local/bin/ffprobe", - "placeholder": "/usr/local/bin/ffprobe", - "help_text": "Full path to the ffprobe executable. Default: /usr/local/bin/ffprobe (Dispatcharr's default location)" - }, - { - "id": "ffmpeg_path", - "label": "\ud83d\udccd FFmpeg Path", - "type": "string", - "default": "/usr/local/bin/ffmpeg", - "placeholder": "/usr/local/bin/ffmpeg", - "help_text": "Full path to the ffmpeg executable, used for blank-screen detection. Default: /usr/local/bin/ffmpeg (Dispatcharr's default location)" - } - ], - "actions": [ - { - "id": "validate_settings", - "label": "\u2705 Validate Settings", - "description": "Validate all plugin settings (database connectivity, groups, etc.).", - "button_variant": "outline", - "button_color": "blue", - "button_label": "\u2705 Validate" - }, - { - "id": "update_schedule", - "label": "\ud83d\udcc5 Update Schedule", - "description": "Apply the current schedule settings. If scheduled times are empty, the schedule will be cleared and scheduler will be stopped.", - "button_variant": "filled", - "button_color": "green", - "button_label": "\ud83d\udcbe Save Schedule" - }, - { - "id": "reset_progress", - "label": "\ud83d\udd04 Reset Window Progress", - "description": "Clear the windowed-schedule resume state. The next window will start fresh from the full channel list instead of resuming where the last window left off.", - "button_variant": "outline", - "button_color": "orange", - "button_label": "\ud83d\udd04 Reset Progress" - }, - { - "id": "check_scheduler_status", - "label": "\ud83d\udd0d Check Scheduler Status", - "description": "Display scheduler thread status and diagnostic information.", - "button_variant": "outline", - "button_color": "blue", - "button_label": "\ud83e\ude7a Check Scheduler" - }, - { - "id": "load_groups", - "label": "\ud83d\udce5 Load Group(s)", - "description": "Load channels from the specified Dispatcharr group(s) (or all groups if blank).", - "button_variant": "filled", - "button_color": "green", - "button_label": "\ud83d\udce5 Load Group(s)" - }, - { - "id": "check_streams", - "label": "\u25b6\ufe0f Start Stream Check", - "description": "Start checking stream status and quality for all loaded channels.", - "button_variant": "filled", - "button_color": "green", - "button_label": "\u25b6\ufe0f Start Stream Check" - }, - { - "id": "view_progress", - "label": "\ud83d\udcca View Check Progress", - "description": "View the current progress and ETA of the running stream check.", - "button_variant": "outline", - "button_color": "blue", - "button_label": "\ud83d\udcca View Progress" - }, - { - "id": "cancel_check", - "label": "\ud83d\uded1 Cancel Stream Check", - "description": "Cancel the currently running stream check.", - "button_color": "orange", - "button_variant": "outline", - "button_label": "\ud83d\uded1 Cancel Check", - "confirm": { - "message": "Cancel the in-progress stream check? Streams already probed are kept; queued and in-flight streams are aborted (ffprobe currently running may take a few seconds to exit)." - } - }, - { - "id": "view_results", - "label": "\ud83d\udccb View Last Results", - "description": "View summary of the last completed stream check.", - "button_variant": "outline", - "button_color": "blue", - "button_label": "\ud83d\udccb View Last Results" - }, - { - "id": "rename_channels", - "label": "\u270f\ufe0f Rename Dead Channels", - "description": "Rename all channels marked as Dead in the last check using the configured rename format.", - "button_color": "red", - "confirm": { - "message": "This will rename dead channels. This action is irreversible. Continue?" - }, - "button_variant": "filled", - "button_label": "\u270f\ufe0f Rename Dead" - }, - { - "id": "move_dead_channels", - "label": "\u26b0\ufe0f Move Dead Channels to Group", - "description": "Moves all channels marked as Dead in the last check to the specified group.", - "button_color": "red", - "confirm": { - "message": "This will move dead channels to the configured group. This action is irreversible. Continue?" - }, - "button_variant": "filled", - "button_label": "\u26b0\ufe0f Move Dead" - }, - { - "id": "rename_black_screen_channels", - "label": "\u2b1b Rename Blank-Screen Channels", - "description": "Rename all channels detected as a blank screen in the last check using the configured format.", - "button_color": "red", - "confirm": { - "message": "This will rename blank-screen channels. This action is irreversible. Continue?" - }, - "button_variant": "filled", - "button_label": "\u2b1b Rename Blank" - }, - { - "id": "move_black_screen_channels", - "label": "\u2b1b Move Blank-Screen Channels to Group", - "description": "Moves all channels detected as a blank screen in the last check to the specified group.", - "button_color": "red", - "confirm": { - "message": "This will move blank-screen channels to the configured group. This action is irreversible. Continue?" - }, - "button_variant": "filled", - "button_label": "\u2b1b Move Blank" - }, - { - "id": "restore_channels", - "label": "\u267b\ufe0f Restore Recovered Channels", - "description": "For channels that are Alive again but were previously marked: strip plugin name tags and move them back to their original group.", - "button_color": "green", - "confirm": { - "message": "This will strip plugin tags from recovered channel names and move them back to their original groups. Continue?" - }, - "button_variant": "filled", - "button_label": "\u267b\ufe0f Restore Recovered" - }, - { - "id": "rename_low_framerate_channels", - "label": "\ud83d\udc0c Rename Low Framerate Channels", - "description": "Rename channels with streams under 30fps using the configured rename format.", - "confirm": { - "message": "This will rename low framerate channels. This action is irreversible. Continue?" - }, - "button_variant": "filled", - "button_color": "red", - "button_label": "\ud83d\udc0c Rename Low FPS" - }, - { - "id": "move_low_framerate_channels", - "label": "\ud83d\udcc1 Move Low Framerate Channels", - "description": "Moves channels with streams under 30fps to the specified group.", - "confirm": { - "message": "This will move low framerate channels to the configured group. This action is irreversible. Continue?" - }, - "button_variant": "filled", - "button_color": "red", - "button_label": "\ud83d\udcc1 Move Low FPS" - }, - { - "id": "add_video_format_suffix", - "label": "\ud83c\udfac Add Video Format Suffix", - "description": "Adds a format suffix like [HD] or [FHD] to alive channel names.", - "confirm": { - "message": "This will rename channels based on the last check. This action is irreversible. Continue?" - }, - "button_variant": "filled", - "button_color": "red", - "button_label": "\ud83c\udfac Add Format Suffix" - }, - { - "id": "view_table", - "label": "\ud83d\udcca View Results Table", - "description": "Display detailed results in table format. (Copy/paste into text editor for better formatting.)", - "button_variant": "outline", - "button_color": "blue", - "button_label": "\ud83d\udcca View Results Table" - }, - { - "id": "export_results", - "label": "\ud83d\udcbe Export Results to CSV", - "description": "Export the last check results to a CSV file. Will be saved in Docker container: /data/exports/", - "button_variant": "filled", - "button_color": "green", - "button_label": "\ud83d\udcbe Export CSV" - }, - { - "id": "cleanup_orphaned_tasks", - "label": "\ud83e\uddf9 Cleanup Orphaned Tasks", - "description": "Remove any orphaned Celery periodic tasks from old plugin versions.", - "button_variant": "outline", - "button_color": "orange", - "button_label": "\ud83e\uddf9 Cleanup Orphaned Tasks" - }, - { - "id": "clear_csv_exports", - "label": "\ud83d\uddd1\ufe0f Clear CSV Exports", - "description": "Delete all CSV export files created by this plugin.", - "button_color": "red", - "confirm": { - "message": "This will delete all CSV files in /data/exports/. This action cannot be undone. Continue?" - }, - "button_variant": "filled", - "button_label": "\ud83d\uddd1\ufe0f Clear CSV Exports" - }, - { - "id": "delete_dead_channels", - "label": "\ud83d\uddd1\ufe0f Delete Dead Channels", - "description": "Permanently delete all channels marked as Dead in the last check from the database.", - "button_color": "red", - "confirm": { - "message": "WARNING: This will PERMANENTLY DELETE dead channels from the database. This action CANNOT be undone. The 'Auto-Delete Confirmation' field must contain DELETE for this to work. Continue?" - }, - "button_variant": "filled", - "button_label": "\ud83d\uddd1\ufe0f Delete Dead" - } - ] + "min_dispatcharr_version": "v0.20.0", + "source_url": "https://github.com/PiratesIRC/Dispatcharr-IPTV-Checker-Plugin/releases/download/v{version}/iptv_checker-v{version}.zip" }