| id | ref-cli-env-reference | ||||
|---|---|---|---|---|---|
| type | reference | ||||
| status | draft | ||||
| tags |
|
Reference for primary commands, key flags, and commonly used environment variables.
This is focused on practical operation/testing. For tester bundles and supervisor-specific lab knobs, also see:
Top-level help defaults to the smaller end-user path (Core, Guide/EPG, VOD). Use iptv-tunerr --all-commands to list the full Lab/ops surface.
Deck OIDC workflow note:
- when
IPTV_TUNERR_IDENTITY_OIDC_PLAN_FILEand the relevant Keycloak/Authentik envs are set, the deck exposes/deck/oidc-migration-audit.jsonplus an OIDC workflow surface that includes recent apply history in both the summary card and workflow modal, withall / success / failedfiltering, success/failure badges, and modal per-target outcome rows for partial or failed runs
One-shot workflow:
- refresh catalog (unless skipped)
- health-check provider (unless skipped)
- start tuner server
Common flags:
-catalog-addr-base-url-device-id-friendly-name-mode(easyorfull)-skip-index-skip-health-register-plex(apifor zero-touch Plex API registration, or a Plex data path for legacy DB-assisted registration)-register-only
Use for:
- systemd/Docker runtime
- most single-binary deployments
Serve tuner endpoints from an existing catalog.
Common flags:
-catalog-addr-base-url-device-id-friendly-name-mode
Use for:
- split workflows (external indexing)
- local endpoint tests
Validate first-run configuration and print the next safe steps for a new install. This is the operator-facing "am I actually ready to start" command.
The dedicated Control Deck exposes the same report at /deck/setup-doctor.json.
Typical flow:
cp .env.minimal.example .env
# edit .env
iptv-tunerr setup-doctor
iptv-tunerr probe
iptv-tunerr run -mode=easyWhat it checks:
- whether a real IPTV source is configured (
IPTV_TUNERR_M3U_URLor provider URL plus credentials) - whether
IPTV_TUNERR_BASE_URLis set and looks reachable - whether the chosen first-run mode matches the usual Plex wizard lane
- whether Plex API zero-touch registration is configured when you choose
-mode=full - whether the catalog path and deck auth choices are sensible
Flags:
-json- emit machine-readable output-mode easy|full- shape the first-run advice for the simple or advanced path-base-url URL- overrideIPTV_TUNERR_BASE_URLfor this check only
Exit status:
- exits
0when there are no failing checks - exits
1when at least one required first-run check fails
Fetch provider M3U/API and write catalog JSON.
Common flags:
-m3u-catalog
Use for:
- scheduled indexing
- catalog debugging without starting the server
Mount VODFS from the catalog.
Common flags:
-mount-catalog-cache
Notes:
- Linux-only (
FUSE)
Serve the VOD catalog over a read-only WebDAV surface so macOS and Windows can
mount the same synthetic Movies/ / TV/ tree without the Linux FUSE path.
Common flags:
-addr-catalog-cache
Notes:
- Cross-platform (
Linux,macOS,Windows) - The WebDAV server is read-only.
- Directory scans work without
-cache, but actual file reads need a working materializer/cache path.
Print a platform-specific mount hint and example command for the read-only VOD WebDAV surface.
Common flags:
-addr-os-target
Create or reuse Plex libraries for a mounted VODFS tree.
Default library names:
VOD-><mount>/TV(Plex TV library)VOD-Movies-><mount>/Movies(Plex Movie library)
Common flags:
-mount-plex-url-token-shows-name-movies-name-vod-safe-preset(defaulttrue)-refresh
Env fallbacks:
IPTV_TUNERR_PMS_URL(orPLEX_HOST->http://<host>:32400)IPTV_TUNERR_PMS_TOKEN(orPLEX_TOKEN)IPTV_TUNERR_MOUNT
Notes:
- Requires the VODFS mount path to be visible to the Plex server host/container.
- Creates/reuses sections idempotently by section name + path.
- If the same section name exists with a different path/type, the command returns an error instead of mutating it.
- By default, applies a per-library VOD-safe Plex preset to disable expensive analysis jobs (credits, intro/chapter/preview thumbnails, ad/voice analysis) on these virtual catch-up libraries only.
Run a reverse proxy in front of Plex Media Server for Plex Live TV operations. The command has two independent uses:
- rewrite
/media/providersand provider-scoped XML labels so multiple DVRs do not all render as the PMS server name - optionally, with
-elevate-live-tv, elevate only Live TV requests to the PMS owner token while ordinary library requests keep the user's own Plex token; elevation requires an inbound Plex token that already has access to this server
Common flags:
-listen(defaultIPTV_TUNERR_PLEX_LABEL_PROXY_LISTENor127.0.0.1:33240)-upstream(PMS origin URL)-plex-url(alias for-upstream)-token(token used to query/livetv/dvrsfor label mapping)-owner-token(owner PMS token used for Live TV elevation; defaults toIPTV_TUNERR_PMS_OWNER_TOKEN,PLEX_OWNER_TOKEN, or the resolved token)-strip-prefix(defaultiptvtunerr-)-refresh-seconds(default30)-spoof-identity(rewrite rootfriendlyNamefor Plex Web label workarounds)-elevate-live-tv(unsupported Plex workaround: replace tokens only on Live TV requests)-neutralize-owner-history(replay elevated Live TV progress/scrobble events under the original user token and remove owner-side watched marks)
Env fallbacks:
IPTV_TUNERR_PMS_URLorPLEX_HOSTIPTV_TUNERR_PMS_TOKENorPLEX_TOKENIPTV_TUNERR_PMS_OWNER_TOKENorPLEX_OWNER_TOKENIPTV_TUNERR_PLEX_LABEL_PROXY_LISTEN
Operational note:
- when
-elevate-live-tvis enabled, deploy the proxy as the only PMS front door and block public direct PMS32400and proxy33240; otherwise clients can bypass the token-elevation path. Do not DNATplex.directTLS into this HTTP proxy. See plex-live-tv-entitlement-proxy. - public access can be a named Cloudflare Tunnel, VPN frontend, or a normal
HTTPS frontend on TCP
443. See plex-live-tv-proxy-frontends. - for Tailscale, WireGuard, OpenVPN, Gluetun, NAT-PMP/static-forward, and fail-closed routing patterns, see vpn-access-patterns.
Split a VOD catalog into multiple category/region lane catalogs for separate VODFS mounts/libraries.
Built-in lane names (current):
bcastUSsportsnewskidsmusiceuroUKmenamoviestvintl
Common flags:
-catalog-out-dir(required)
Output:
<out-dir>/<lane>.json<out-dir>/manifest.json(lane counts + source catalog)
Use for:
- smaller category-scoped Plex VOD libraries
- reduced scan scope / faster targeted rescans
- operational isolation of high-churn catch-up lanes
Generate a deterministic EPG-link coverage report for live_channels in a
catalog against an XMLTV source. This is the Phase 1 workflow for improving the
long-tail unlinked channel set without changing runtime playback behavior.
Match tiers (current):
tvg-idexact- alias override exact
- normalized channel-name exact (unique only)
Common flags:
-catalog-xmltv(required; file path orhttp(s)URL)-aliases(optional JSON alias override file)-out(optional JSON full report; default is stdout)-unmatched-out(optional JSON unmatched-only list)
Alias override JSON shape:
{
"name_to_xmltv_id": {
"Nick Junior Canada": "nickjr.ca",
"Fox News Channel US": "foxnews.us"
}
}Use for:
- measuring current XMLTV coverage before changing lineups
- generating a review queue for the unlinked tail
- iterating alias mappings safely (report-only, no runtime mutation)
Generate a channel intelligence report for the current lineup.
The report scores each channel on:
- guide confidence
- stream resilience
- backup stream depth
- actionable next steps
Optional XMLTV enrichment adds EPG match provenance:
- exact
tvg-id - alias override
- normalized exact-name repair
- unmatched
Common flags:
-catalog-xmltv(optional file path orhttp(s)URL)-aliases(optional JSON alias override file)-out(optional JSON output file; otherwise stdout)
Also available live over HTTP:
GET /channels/report.json
Inspect Plex SQLite state relevant to Live TV injection.
Common flags:
-plex-data-dir-out
Use for:
- locating
media_provider_resources - confirming lineup and EPG SQLite state
- proving what IPTV Tunerr changed locally
Mine Plex Media Server logs for Live TV and grabber endpoints.
Common flags:
-plex-data-dir-out
Use for:
- discovering real client/backend Live TV endpoints
- extracting undocumented request shapes from PMS logs
Snapshot the PMS-side Live TV state and probe core endpoints.
Common flags:
-plex-url-token-tuner-base-url-include-probes-out
Use for:
- current device and DVR inventory
- provider and tuner reachability
- quick regression snapshots before and after experiments
Resolve and probe each registered Plex Live TV device URI.
Common flags:
-plex-url-token-out
Use for:
- identifying dead tuner URIs from PMS's point of view
- proving DNS or HTTP reachability failures on registered HDHR devices
- separating share-gating issues from plain bad device registration
Delete and recreate stale DVR/device rows from a TSV URI cutover map.
Common flags:
-plex-url-token-map-reload-guide-activate-do-out
Use for:
- migrating injected DVRs from dead service-DNS URIs to reachable hostnames
- replaying unsupported PMS registration manually but repeatably
- proving whether a DVR failure is fixed by re-registering against a new tuner URI
Replay an arbitrary PMS or plex.tv HTTP request.
Common flags:
-base-url-token-method-path-query-headers-body-out
Use for:
- manual endpoint replay
- undocumented API experiments
- controlled request/response capture
Delete and recreate a Plex share row, then report the observed share state.
Common flags:
-token-machine-id-plex-url-client-id-user-id-library-ids-requested-allow-tuners-allow-sync-do-out
Use for:
- reproducing the current non-Home
allowTunersclamp - confirming whether a share mutation actually changes plex.tv state
Build a neutral Live TV bundle from existing Plex DVR/device state.
Common flags:
-plex-url-token-dvr-key-tuner-url-tuner-count-include-libraries-out
Use for:
- exporting the tuner URL, XMLTV URL, friendly name, and device identity from an existing Plex DVR
- creating a portable migration artifact before converting or re-registering elsewhere
- avoiding one-off manual copy/paste of Plex device and lineup metadata
Notes:
- Requires
-dvr-keywhen Plex exposes multiple DVRs so the export does not silently pick the wrong fleet. - Produces a neutral bundle artifact; it does not write Emby/Jellyfin state directly.
- With
-include-libraries, the same bundle also captures Plex library sections and shared storage paths for later library migration planning.
Convert a saved Live TV bundle into an Emby or Jellyfin registration plan.
Common flags:
-in-target(embyorjellyfin)-host-out
Use for:
- turning a Plex-derived Live TV bundle into the concrete
TunerHostsandListingProviderspayload shape used by Emby/Jellyfin - pre-rolling registration plans before applying them to a real Emby or Jellyfin server
- keeping media-server migration work on a portable JSON artifact instead of ad hoc spreadsheets or hand edits
Notes:
- This first slice emits a registration plan, not a raw media-server DB dump.
- Target host/token stay external because Plex DVR state does not contain Emby/Jellyfin credentials.
Apply a saved Emby or Jellyfin registration plan directly to a live server.
Common flags:
-in-target(embyorjellyfin, optional override)-host-token-state-file-out
Use for:
- taking a converted migration plan and registering it without retyping tuner/guide data by hand
- pre-rolling Emby/Jellyfin Live TV while Plex stays online against the same Tunerr source
- keeping the migration lane in the binary instead of sidecar scripts
Notes:
-host/-tokencan also come fromIPTV_TUNERR_EMBY_HOST+IPTV_TUNERR_EMBY_TOKENorIPTV_TUNERR_JELLYFIN_HOST+IPTV_TUNERR_JELLYFIN_TOKEN, based on the plan target.-state-fileuses the same idempotent registration-state mechanism as runtime Emby/Jellyfin registration.
Compare a saved Live TV registration plan against a live Emby or Jellyfin server.
Common flags:
-in-target(embyorjellyfin, optional override)-host-token-out
Use for:
- checking whether the planned tuner host and XMLTV listing provider would be reused, created, or blocked by conflicts
- validating the destination server before applying a Live TV migration plan
Notes:
-host/-tokenuse the same env fallback aslive-tv-bundle-apply.- The diff reports tuner-host and listing-provider entries separately so conflicts are visible before registration.
Build or apply a multi-target Emby/Jellyfin rollout from one neutral bundle.
Common flags:
-in-targets(defaultemby,jellyfin)-emby-host-emby-token-emby-state-file-jellyfin-host-jellyfin-token-jellyfin-state-file-apply-out
Use for:
- preparing the same Plex-derived Live TV identity for more than one downstream server at once
- keeping Plex online while Emby and Jellyfin are pre-rolled from the same Tunerr source
- dry-running a migration overlap plan before actually registering anything
Notes:
- Without
-apply, the command emits a rollout plan JSON artifact. - With
-apply, the command registers only the requested non-Plex targets and intentionally leaves Plex untouched.
Compare one neutral Live TV bundle against live Emby and/or Jellyfin targets.
Common flags:
-in-targets(defaultemby,jellyfin)-emby-host-emby-token-jellyfin-host-jellyfin-token-summary(emit a compact human-readable report instead of JSON)-out
Use for:
- validating the full non-Plex Live TV overlap state in one pass from the same neutral bundle
- seeing per-target tuner-host and listing-provider reuse/create/conflict outcomes before apply
Notes:
- Uses the same target filtering and env fallback as
live-tv-bundle-rollout. - Returns one diff result per requested target instead of changing server state.
Convert bundled Plex library sections into an Emby or Jellyfin library plan.
Common flags:
-in-target-host-out
Use for:
- recreating Plex movie/show library definitions on Emby/Jellyfin against the same shared storage paths
- staging non-Live-TV migration work without hand-recreating libraries in each server UI
Notes:
- Requires a bundle created with
live-tv-bundle-build -include-libraries. - Converts names, media type, and shared paths only; it does not promise metadata DB portability.
Apply an Emby or Jellyfin library migration plan to a live server.
Common flags:
-in-target-host-token-refresh-out
Use for:
- creating or reusing target libraries from a saved migration plan
- refreshing the destination server after the shared paths are registered
Notes:
-host/-tokencan reuse the sameIPTV_TUNERR_EMBY_*orIPTV_TUNERR_JELLYFIN_*env vars as the Live TV registration flows.- This applies library definitions, not vendor-specific metadata/state translation.
Compare a library migration plan against a live Emby or Jellyfin server before applying it.
Common flags:
-in-target-host-token-out
Use for:
- seeing exactly which bundled libraries would be reused, created, or blocked by type/path conflicts
- validating overlap migrations against a live destination before touching server state
Notes:
-host/-tokencan reuse the sameIPTV_TUNERR_EMBY_*orIPTV_TUNERR_JELLYFIN_*env vars as the apply path.- Same name + same type + same path is reported as
reuse, missing libraries arecreate, and mismatched type/path cases are reported as conflicts to resolve manually.
Build or apply a multi-target Emby/Jellyfin library rollout from one bundle.
Common flags:
-in-targets(defaultemby,jellyfin)-emby-host-emby-token-jellyfin-host-jellyfin-token-refresh-apply-out
Use for:
- recreating the same Plex movie/show library definitions on both Emby and Jellyfin from one shared bundle
- pre-rolling the non-Plex library side together while Plex stays live
Notes:
- Without
-apply, the command emits a library rollout plan artifact. - With
-apply, it creates or reuses the requested non-Plex library targets and intentionally leaves Plex untouched.
Compare one bundled library rollout against live Emby and/or Jellyfin targets.
Common flags:
-in-targets(defaultemby,jellyfin)-emby-host-emby-token-jellyfin-host-jellyfin-token-out
Use for:
- seeing both destination servers' create/reuse/conflict outcomes from one neutral bundle
- validating overlap migrations across the whole non-Plex side before applying anything
Notes:
- Uses the same target selection and env fallback rules as
library-migration-rollout. - Returns one diff result per requested target instead of mutating server state.
Audit one migration bundle against live Emby and/or Jellyfin targets.
Common flags:
-in-targets(defaultemby,jellyfin)-emby-host-emby-token-jellyfin-host-jellyfin-token-out
Use for:
- getting one combined overlap-readiness report per target instead of stitching together separate Live TV and library diff commands
- validating both tuner/guide registration and bundled library/catch-up surfaces before deciding whether a target is migration-ready
Notes:
- Reuses the same host/token env fallback and target filtering as the rollout/diff commands.
- If the bundle does not carry shared libraries or attached catch-up lanes, the audit still returns Live TV results and marks the library side as skipped.
- The report now also includes
ready_to_applyat both the overall and per-target level, plus rolled-up conflict counts, so operators can answer "can I apply this now?" without manually interpreting each diff block. - The audit also reports
statusandindexed_channel_count. Current status values are:blocked_conflicts: definition conflicts existready_to_apply: no conflicts, but the target is not yet visibly convergedconverged: no conflicts, the target already exposes indexed Live TV channels, and the bundled libraries/catch-up lanes are already present when applicable
- For operator use, the audit also includes
status_reasonpluspresent_libraries/missing_librariesso a partial migration shows exactly what is still absent on the target. - When a bundled library is already being reused, the audit also exposes
populated_libraries/empty_librariesbased on the target server's current item counts. These are visibility hints only; they do not currently change readiness or convergence logic. - When the bundle carries source Plex library counts, reused libraries also report parity via
synced_libraries/lagging_libraries, plus per-librarysource_item_count,existing_item_count, andparity_statusvalues in the nested library diff rows. - When the bundle also carries sampled source Plex item titles, reused libraries report title-level sample parity via
title_synced_libraries/title_lagging_libraries, plus per-librarysource_titles,existing_titles,missing_titles, andtitle_parity_statusvalues in the nested library diff rows. This is a bounded sample hint, not a full metadata-equivalence proof. - When the target server exposes a recognizable library-refresh scheduled task, the audit also includes
library_scanwith best-effort running/state/progress fields. This is visibility only and is intentionally not required for readiness. -summaryis intended for operators and shell use. It keeps the same audit logic, but flattens the main verdicts, reasons, lagging-library hints, and bounded per-library missing-title samples into a compact text report per target.
Related env:
IPTV_TUNERR_MIGRATION_BUNDLE_FILEenables the dedicated deck's built-in migration workflow report at/deck/migration-audit.json. It points at the saved neutral migration bundle that the running process should audit against the configured Emby/Jellyfin targets.
Attach a saved catch-up publish manifest to an existing migration bundle.
Common flags:
-bundle-manifest-out
Use for:
- carrying Tunerr-generated catch-up library layouts in the same migration artifact as Live TV and shared Plex library definitions
- pre-rolling
.strm/.nfocatch-up libraries on Emby and Jellyfin from the same neutral bundle used for the rest of the migration
Notes:
- The manifest should come from
iptv-tunerr catchup-publishoutput, specifically itspublish-manifest.json. - Attached catch-up lanes are treated as movie libraries backed by generated shared paths; they do not imply metadata DB conversion.
Build a neutral Plex-user identity bundle.
Common flags:
-plex-url-token-out
Use for:
- exporting Plex users before a gradual Emby/Jellyfin cutover
- capturing visible share/tuner entitlement hints from plex.tv into one artifact
Notes:
- Requires Plex API access plus a valid Plex owner token.
- Exports account identity hints only, not passwords.
Convert a Plex-user bundle into an Emby/Jellyfin local-user plan.
Common flags:
-in-target(embyorjellyfin)-host-out
Use for:
- turning Plex-user exports into destination local-account create/reuse plans
- reviewing the derived destination usernames before diff/apply
Notes:
- Current plans are username-based and also carry additive destination policy grants when Plex share state exposes them cleanly.
- They still do not claim to clone passwords, folder-by-folder grants, or OIDC state.
Build a provider-agnostic OIDC user/group plan from a Plex-user bundle.
Common flags:
-in-issuer-client-id-out
Use for:
- deriving stable OIDC subject hints, usernames, display names, email hints, and group claims from Plex users
- feeding Authentik/Keycloak-backed automation from the same neutral migration artifact
Notes:
- This command itself does not apply anything to a live identity provider.
- Group claims are Tunerr-owned migration hints such as
tunerr:migrated,tunerr:live-tv,tunerr:sync, andtunerr:plex-shared.
Audit an OIDC migration plan against live IdP targets.
Common flags:
-in-targets(keycloak,authentik, or both)-keycloak-host-keycloak-realm-keycloak-token-keycloak-user-keycloak-password-authentik-host-authentik-token-summary-out
Use for:
- checking missing IdP users before apply
- checking whether Tunerr-owned migration groups already exist
- checking which IdP users still need migration-group membership
- checking which existing IdP users still need Tunerr-owned metadata refresh
- getting one compact provisioning-readiness report across Keycloak and Authentik
Env fallback:
IPTV_TUNERR_KEYCLOAK_HOSTIPTV_TUNERR_KEYCLOAK_REALMIPTV_TUNERR_KEYCLOAK_TOKENIPTV_TUNERR_KEYCLOAK_USERIPTV_TUNERR_KEYCLOAK_PASSWORDIPTV_TUNERR_AUTHENTIK_HOSTIPTV_TUNERR_AUTHENTIK_TOKEN
Notes:
- This is provisioning-focused readiness, not full SSO-policy parity.
- For Keycloak, username/password credentials are preferred over a static token because Tunerr will
mint a fresh
admin-clitoken for the audit/apply run. -summaryemits a compact human-readable IdP migration report instead of JSON.
Compare an OIDC migration plan against a live Authentik instance.
Common flags:
-in-host-token-out
Use for:
- seeing which OIDC-plan users are missing from Authentik
- seeing which Tunerr-owned migration groups are missing
- seeing which group memberships would be added before apply
Env fallback:
IPTV_TUNERR_AUTHENTIK_HOSTIPTV_TUNERR_AUTHENTIK_TOKEN
Apply an OIDC migration plan to a live Authentik instance.
Common flags:
-in-host-token-bootstrap-password-recovery-email-out
Use for:
- creating missing Authentik users from the OIDC plan
- creating missing Tunerr-owned migration groups
- attaching users to the required groups for staged cutover
- optionally setting a bootstrap password or triggering recovery-email onboarding
Notes:
- Current Authentik scope is user/group provisioning plus optional bootstrap/onboarding.
-bootstrap-passwordcalls Authentik's user password endpoint.-recovery-emailtriggers Authentik recovery email for users with email addresses in the OIDC plan.- New users created through this path are stamped with stable Tunerr migration metadata attributes, and existing users get those Tunerr-owned fields refreshed when they drift.
Compare an OIDC migration plan against a live Keycloak realm.
Common flags:
-in-host-realm-token-user-password-out
Use for:
- seeing which OIDC-plan users are missing from Keycloak
- seeing which Tunerr-owned migration groups are missing
- seeing which group memberships would be added before apply
Env fallback:
IPTV_TUNERR_KEYCLOAK_HOSTIPTV_TUNERR_KEYCLOAK_REALMIPTV_TUNERR_KEYCLOAK_TOKENIPTV_TUNERR_KEYCLOAK_USERIPTV_TUNERR_KEYCLOAK_PASSWORD
Notes:
- If
-userand-passwordare provided, Tunerr mints a fresh Keycloak admin token instead of relying on-token.
Apply an OIDC migration plan to a live Keycloak realm.
Common flags:
-in-host-realm-token-user-password-bootstrap-password-password-temporary-email-actions-email-client-id-email-redirect-uri-email-lifespan-sec-out
Use for:
- creating missing Keycloak users from the OIDC plan
- creating missing Tunerr-owned migration groups
- attaching users to the required groups for staged cutover
- optionally setting a bootstrap password or triggering execute-actions-email onboarding
Notes:
- Current Keycloak scope is user/group provisioning only.
- If
-userand-passwordare provided, Tunerr mints a fresh Keycloak admin token for the apply run instead of relying on-token. -bootstrap-passwordsets a password via Keycloak admin reset-password;-password-temporarydefaults totrue.-email-actionssends Keycloakexecute-actions-emailfor users with email addresses in the OIDC plan.- New users created through this path are stamped with stable Tunerr migration metadata attributes, and existing users get those Tunerr-owned fields refreshed when they drift.
Compare an identity migration plan against a live Emby/Jellyfin server.
Common flags:
-in-target-host-token-out
Use for:
- seeing which users already exist on the destination
- proving how many local users would be created before apply
- seeing which existing destination users still need additive policy updates
- seeing which destination users still are not activation-ready
Env fallback:
IPTV_TUNERR_EMBY_HOST/IPTV_TUNERR_EMBY_TOKENIPTV_TUNERR_JELLYFIN_HOST/IPTV_TUNERR_JELLYFIN_TOKEN
Apply an identity migration plan to a live Emby/Jellyfin server.
Common flags:
-in-target-host-token-out
Use for:
- creating missing destination local users while reusing ones that already exist
- overlap migrations where Plex stays online and destination accounts are pre-rolled first
- pushing the first safe additive access-policy layer (Live TV, sync/download, all-library, remote-access-for-shared-users)
Notes:
- Existing destination users are reused by case-insensitive username match.
- Current apply updates additive destination policy only when it can be inferred safely from Plex share state.
- It does not set passwords, complete invite/activation, wire OIDC, or guess folder-specific library grants.
Build or apply a multi-target Emby/Jellyfin identity rollout from one Plex-user bundle.
Common flags:
-in-targets-emby-host-emby-token-jellyfin-host-jellyfin-token-apply-out
Use for:
- pre-rolling the same Plex user set across both Emby and Jellyfin during overlap migrations
Compare one Plex-user identity bundle against live Emby/Jellyfin targets.
Common flags:
-in-targets-emby-host-emby-token-jellyfin-host-jellyfin-token-out
Use for:
- validating both non-Plex targets before creating users
- keeping dual-host overlap migrations consistent from one bundle
Audit one Plex-user identity bundle against live Emby/Jellyfin targets.
Common flags:
-in-targets-emby-host-emby-token-jellyfin-host-jellyfin-token-summary-out
Use for:
- getting one readiness report per target instead of only raw create/reuse counts
- seeing which Plex users still need destination accounts
- surfacing which existing destination users still need additive policy updates
- surfacing which destination users still have no configured password or auto-login path
- surfacing which managed/shared/tuner-entitled users still need manual post-create follow-up
Notes:
- Current status values are:
blocked_conflictsready_to_applyconverged
ready_to_applyonly means the destination is structurally unblocked; it does not mean activation, folder-specific grants, or OIDC state are complete.-summaryflattens the main verdicts plus missing-user, policy-update, activation-pending, and manual-follow-up hints into compact text output.
Related env:
-
IPTV_TUNERR_IDENTITY_MIGRATION_BUNDLE_FILEenables the dedicated deck's built-in identity migration workflow report at/deck/identity-migration-audit.json. It points at the saved Plex-user identity bundle that the running process should audit against the configured Emby/Jellyfin targets. -
IPTV_TUNERR_IDENTITY_OIDC_PLAN_FILEenables the dedicated deck's built-in OIDC migration workflow report at/deck/oidc-migration-audit.jsonand the corresponding apply surface at/deck/oidc-migration-apply.json. The deck-side apply path supports the same practical IdP onboarding knobs as the CLI: Keycloak bootstrap password, temporary-password choice, execute-actions-email actions plus optional client/redirect/lifespan hints, and Authentik bootstrap password plus recovery-email delivery. The workflow summary also carries a short recent OIDC apply history from deck activity, including per-target delta counts for successful runs and phase/error context for failed ones, and the deck can filter that history toall,success, orfailedruns. -
GET /provider/profile.json— runtime provider profile including learned tuner caps, HLS instability, Cloudflare hits, penalized upstream hosts, andremediation_hints(advisory heuristic suggestions with optional relatedIPTV_TUNERR_*env names)
Use for:
- spotting channels that are present but operationally weak
- confirming whether EPG success is coming from exact
tvg-idmatches or repairs - building a prioritized cleanup queue for aliases, backup streams, and stable guide numbers
Notes:
- each reported channel now includes a persisted
dna_id - the current Channel DNA foundation prefers real/repaired
TVGID, then falls back to normalized channel identity inputs
Generate the short-form hall-of-fame / hall-of-shame view for the lineup.
This is the fast operator surface when you do not want to read the full channel report first.
Common flags:
-catalog-xmltv(optional file path orhttp(s)URL)-aliases(optional JSON alias override file)-limit(rows per bucket; default10)-out(optional JSON output file; otherwise stdout)
Also available live over HTTP:
GET /channels/leaderboard.json
Buckets:
hall_of_famehall_of_shameguide_risksstream_risks
Generate a guide-health report for the actual merged guide output.
This answers a different question than epg-link-report:
- not only "did the channel match XMLTV?"
- but also "did real programme rows make it into the served guide?"
The report classifies channels by:
- real programme coverage
- placeholder-only fallback
- no programme rows
- optional XMLTV match provenance
Common flags:
-catalog-guide(required; file path orhttp(s)URL, usually/guide.xml)-xmltv(optional source XMLTV for deterministic match provenance)-aliases(optional JSON alias override file)-out(optional JSON output file; otherwise stdout)
Live endpoint:
GET /guide/health.json
Use for:
- proving that guide data contains real show blocks, not only channel-name placeholders
- identifying channels that are guide-linked but still have no programme coverage
- debugging tester reports like "channel names appear, but no actual what's-on data"
Run the combined EPG diagnostic workflow in one report.
This is the recommended top-level operator tool when you want one answer to:
- did the channel match XMLTV?
- did real programme rows make it into the served guide?
- is the channel only surviving on placeholders?
- what should I fix first?
Common flags:
-catalog-guide(required; file path orhttp(s)URL, usually/guide.xml)-xmltv(optional source XMLTV for deterministic match provenance)-aliases(optional JSON alias override file)-out(optional JSON output file; otherwise stdout)-write-aliases(optional JSON output file containing suggestedname_to_xmltv_idoverrides from healthy normalized-name matches)
Live endpoint:
GET /guide/doctor.jsonGET /guide/aliases.json
Use for:
- one-shot EPG triage instead of manually comparing
epg-link-reportandguide-health - prioritizing whether the real problem is matching, programme coverage, or placeholder fallback
- exporting reviewable alias overrides once a repaired match has proven it carries real programme blocks
Export grouped live-channel identity clusters from a catalog.
Common flags:
-catalog-out
Live endpoint:
GET /channels/dna.json
Observe Plex Live TV sessions over a short window, classify visible stalls with the same idle/lease heuristics as the built-in reaper, and optionally stop stale visible transcode sessions.
Common flags:
-pms-url-token-observe-poll-stop-recover-hidden dry-run|restart-machine-id-player-ip
Export remembered Autopilot decisions and the hottest channels by hit count.
Common flags:
-state-file-limit
Also available live over HTTP:
GET /autopilot/report.json
Live endpoint:
GET /plex/ghost-report.json- supports
?stop=trueto apply the same stale-visible-session stop mode as the CLI
- supports
POST /ops/actions/ghost-visible-stop- runs the same stale-visible-session stop pass for the localhost/LAN operator UI
POST /ops/actions/ghost-hidden-recover?mode=dry-run|restart- runs the guarded hidden-grab helper for the localhost/LAN operator UI
Query params:
observe=4spoll=1s
Limit:
- hidden Plex grabs that never appear in
/status/sessionsare not visible to Ghost Hunter; use the recovery runbook for those cases. - when Ghost Hunter observes zero visible sessions, it now returns:
hidden_grab_suspected=truerecommended_actionrecovery_commandrunbook
IPTV_TUNERR_GHOST_HUNTER_RECOVERY_HELPERoptionally overrides the helper script path used by the CLI-recover-hiddenhook and the operator action endpoint (default./scripts/plex-hidden-grab-recover.sh).
Runtime-only provider intelligence surface:
GET /provider/profile.json
What it exposes:
- configured tuner limit
- learned/effective tuner limit after upstream concurrency-cap signals
- forwarded auth-context / fetch headers (
Cookie,Referer,Origin,Range,If-Range) - whether provider basic auth is configured
- whether
IPTV_TUNERR_FFMPEG_HLS_RECONNECTandIPTV_TUNERR_FETCH_CF_REJECTare active - whether provider autotune is enabled and whether HLS reconnect has been auto-armed
- count and last-seen details for provider concurrency-limit signals
- count and last-seen details for Cloudflare-abuse block hits
- count and last-seen details for HLS playlist/segment instability
Related env:
IPTV_TUNERR_PROVIDER_AUTOTUNE— defaulttrue; enables conservative provider-aware runtime tuning when the operator has not explicitly set the relevant knobIPTV_TUNERR_PROVIDER_AUTOTUNE_HOST_QUARANTINE— whentrue/1/onand autotune is on, upstream hosts that exceedIPTV_TUNERR_PROVIDER_AUTOTUNE_HOST_QUARANTINE_AFTERconsecutive failure signals are skipped inwalkStreamUpstreamswhile at least one non-quarantined backup URL remains (per-host cooldownIPTV_TUNERR_PROVIDER_AUTOTUNE_HOST_QUARANTINE_SEC, default 900). Surfaced on/provider/profile.jsonasauto_host_quarantine,upstream_quarantine_skips_total(cumulative),penalized_hosts[].quarantined_until,quarantined_hosts, andremediation_hints(host_quarantine_active). WithIPTV_TUNERR_METRICS_ENABLE, Prometheusiptv_tunerr_upstream_quarantine_skips_totalmatches the same events.IPTV_TUNERR_PROVIDER_ACCOUNT_MAX_CONCURRENT— optional per-provider-account concurrent-stream cap for deduplicated multi-account channels. When set to a positive integer, live stream ordering prefers less-loaded credential sets and rejects new tunes with HDHR-style 805 / HTTP 503 when every distinct provider account for that channel is already at the cap. If unset, Tunerr still uses account-aware spreading for channels that carry multiple distinct credential sets, and now learns tighter per-account caps from upstream concurrency-limit signals when a specific credential set starts returning423/458/509/ similar limit responses.IPTV_TUNERR_PROVIDER_ACCOUNT_LIMIT_STATE_FILE— optional JSON state file used to persist learned per-account concurrency caps across restarts. If unset butIPTV_TUNERR_COOKIE_JAR_FILEis configured, Tunerr derivesprovider-account-limits.jsonin that same directory automatically.IPTV_TUNERR_PROVIDER_ACCOUNT_SHARED_LEASE_DIR— optional directory for filesystem-backed provider-account leases shared across multiple Tunerr processes. Use this when separate pods/processes share the same upstream account and must enforce one combined concurrency pool instead of one local pool per process. The current cluster deploy mounts a node-shared directory onkspls0for primary plus sports.IPTV_TUNERR_PROVIDER_ACCOUNT_SHARED_LEASE_TTL— optional stale-lease TTL forIPTV_TUNERR_PROVIDER_ACCOUNT_SHARED_LEASE_DIR. Default2m. Active streams refresh their lease heartbeat automatically; this TTL only controls cleanup of orphaned leases after a crash, forced termination, or pod replacement. In clustered Plex deployments, keep this short so dead lease files do not make/provider/profile.jsonreport moreaccount_leasesthan/debug/active-streams.jsonshows live streams.IPTV_TUNERR_PROVIDER_ACCOUNT_SHARED_LEASE_OWNER— optional human-readable owner label for shared provider-account leases. Useful in logs and runtime/debug inspection when multiple pods share one lease directory.IPTV_TUNERR_PROVIDER_ACCOUNT_LIMIT_TTL_HOURS— TTL for persisted learned per-account concurrency caps. Defaults to24; values below1are clamped up to1./provider/profile.jsonnow includesaccount_learned_limits[]so operators can see which credential set has learned a tighter cap, how many contention signals were seen, and whether that account currently has leased streams./provider/profile.jsonand/debug/runtime.jsonnow also expose the learned-limit state file path and TTL so the persistence/decay policy is visible at runtime.IPTV_TUNERR_PROGRAMMING_RECIPE_FILE— optional JSON file storing the server-side Programming Manager recipe. When set, Tunerr applies the saved category/channel selection, manual/custom order, and optional exact-backup collapse after guide/DNA intelligence and before final lineup exposure. Surfaced via/programming/categories.json,/programming/channels.json,/programming/order.json,/programming/backups.json,/programming/recipe.json,/programming/preview.json, and/debug/runtime.json.IPTV_TUNERR_RECORDING_RULES_FILE— optional JSON file storing durable server-side recording rules. Surfaced via/recordings/rules.json,/recordings/rules/preview.json,/recordings/history.json, and/debug/runtime.json.
Server-backed lineup-curation primitives for the upcoming Programming Manager UI:
GET /programming/categories.jsonPOST /programming/categories.jsonGET /programming/categories.json?category=<id>GET /programming/channels.jsonPOST /programming/channels.jsonGET /programming/order.jsonPOST /programming/order.jsonGET /programming/backups.jsonGET /programming/recipe.jsonPOST /programming/recipe.jsonGET /programming/preview.json
What they expose:
- stable category inventory built from the raw post-intelligence lineup (
group_title/source_tag) - optional per-category member listing
- bulk category include/exclude/remove mutations
- exact channel include/exclude/remove mutations
- the durable saved recipe (
selected_categories,included_channel_ids,excluded_channel_ids,order_mode,custom_order,collapse_exact_backups) - manual order mutations (
prepend,append,before,after,remove) through/programming/order.json - exact-match backup grouping reports through
/programming/backups.json - a preview of the currently curated lineup after the recipe is applied, including taxonomy bucket counts
Supported order_mode values:
source— keep source order after existing lineup intelligence and filterscustom— usecustom_orderfirst, then preserve the remaining source orderrecommended— classify channels into the server taxonomy buckets (local_broadcast,general_entertainment,news_info,sports,lifestyle_home,documentary_history,children_family,reality_specialized,premium_networks,regional_sports,religious,international) and sort by bucket, then by savedcustom_order, then by guide number/namecollapse_exact_backups: true— collapse strong exact sibling rows (sametvg_id, else samedna_id) into one visible lineup row with mergedstream_urls; inspect those candidate groups via/programming/backups.json
Notes:
POST /programming/recipe.jsonis localhost/LAN-operator guarded with the same policy as other tuner-side operator mutation endpoints.- A configured
IPTV_TUNERR_PROGRAMMING_RECIPE_FILEis required for durable writes; without it, the recipe endpoint is read-only and reports that no writable file is configured.
User-facing guide packaging surface built from the cached merged /guide.xml:
GET /guide/highlights.json
Query params:
soon=30m— future window forstarting_soon/movies_starting_soonlimit=12— max items per lane
Returned lanes:
currentstarting_soonsports_nowmovies_starting_soon
Preview/feed of future publishable near-live capsule candidates built from the cached merged guide:
GET /guide/capsules.json
Query params:
horizon=3h— how far ahead to include candidate programme windowslimit=20— max capsules returnedpolicy=healthy|strict— optional guide-quality filter; when omitted, falls back toIPTV_TUNERR_CATCHUP_GUIDE_POLICY
Returned fields include:
capsule_iddna_idlanestatepublish_atexpires_at
Current states:
in_progressstarting_soon
This endpoint is the preview/input layer for the catchup-publish command.
Summarized view of the persistent recorder state written by catchup-daemon:
GET /recordings/recorder.json
Query params:
limit=10— max items returned from each ofactive,completed, andfailed
Requirements:
- server must know the recorder state path via
IPTV_TUNERR_CATCHUP_RECORDER_STATE_FILE
Returned fields include:
statisticspublished_countinterrupted_countlanesactivecompletedfailed
Export the same capsule preview model to JSON from a catalog plus a guide/XMLTV source.
Common flags:
-catalog-xmltv— required; local file orhttp(s)URL, including your own/guide.xml-horizon-limit-out-layout-dir— optional lane-split output directory; writes<lane>.jsonfiles plusmanifest.json-guide-policy— optionaloff|healthy|strict; filters capsules using real guide-health before export-replay-url-template— optional source-backed replay URL template; when set, capsules include rendered replay URLs andreplay_mode=replay
Publish near-live guide capsules as media-server-ingestible .strm + .nfo libraries.
Common flags:
-catalog-xmltv— required; local file orhttp(s)URL, including your own/guide.xml-horizon-limit-out-dir— required; root output directory-stream-base-url— required unlessIPTV_TUNERR_BASE_URLis set; used inside generated.strmfiles-replay-url-template— optional source-backed replay URL template; when set,.strmfiles point at rendered replay URLs instead of/stream/<channel>-library-prefix— defaultCatchup-guide-policy— optionaloff|healthy|strict; filters capsules using real guide-health before publish-manifest-out-register-plex-register-emby-register-jellyfin-refresh
Output shape:
<out-dir>/sports/...<out-dir>/movies/...<out-dir>/general/...- one folder per capsule, containing:
<name>.strm<name>.nfo
publish-manifest.json
Registration behavior:
- Plex: creates/reuses one movie library per lane and applies the same VOD-safe library preset used by
plex-vod-register - Emby/Jellyfin: creates/reuses one movie library per lane via
/Library/VirtualFolders, then triggers a library refresh scan when-refresh=true
Operational note:
- without a replay template, published items are near-live launchers and each
.strmpoints back toIPTV_TUNERR_BASE_URL/stream/<channel> - with a replay template, published items become source-backed replay launchers for the programme window
- rerun the publisher on a schedule to keep the lane libraries current
Record current in-progress capsules to local TS files for sources that do not already provide replay URLs.
Common flags:
-catalog-xmltv-horizon-limit-out-dir-stream-base-url-max-duration-guide-policy-replay-url-template
Output:
- one
.tsfile per recorded in-progress capsule (written as<lane>/<sanitized-capsule-id>.partial.tsfirst, then renamed to.tswhen the transfer completes cleanly) record-manifest.json
Replay template variables:
{capsule_id}{dna_id}{channel_id}{guide_number}{channel_name}/{channel_name_query}{title}/{title_query}{start_rfc3339}/{stop_rfc3339}{start_unix}/{stop_unix}{duration_mins}{start_ymd}{start_hm}{start_xtream}/{stop_xtream}(YYYY-MM-DD:HH-MM)
Continuously scan guide-derived capsules and record eligible programmes headlessly with a persistent state file.
This is the first recorder-daemon MVP:
- records
in_progresscapsules immediately - can schedule
starting_sooncapsules within a configurable lead window - records multiple items concurrently up to a configured limit
- persists
active,completed, andfaileditems inrecorder-state.json - supports replay URLs when
-replay-url-templateis configured, otherwise records from/stream/<channel>
Common flags:
-catalog-xmltv-horizon-limit-out-dir-publish-dir-library-prefix-stream-base-url-poll-interval-lead-time-max-duration-max-concurrency-state-file-retain-completed-retain-failed-retain-completed-per-lane-retain-failed-per-lane-budget-bytes-per-lane-guide-policy-replay-url-template-lanes-exclude-lanes-channels-exclude-channels-register-plex-register-emby-register-jellyfin-refresh-defer-library-refresh— with-register-*and-refresh, defer the library scan until afterrecorded-publish-manifest.jsonis written for each successful completion-record-max-attempts— max capture tries per programme when failures look transient (default1)-record-retry-backoff— initial backoff between transient retries (default5s)-record-retry-backoff-max— max backoff between transient retries (default2m)-record-resume-partial— after transient mid-stream failures, retry with HTTPRangeagainst the same.partial.tsspool when the server supports partial responses (defaulttrue)-record-upstream-fallback— build an ordered URL list from Tunerr/stream/<id>plus catalogstream_url/stream_urlsso capture can switch upstream after failures (defaulttrue)-retain-completed-max-age— drop completed recordings whoseStoppedAtis older than this duration (72h,7d, etc.); empty means off-retain-completed-max-age-per-lane— per-lane max age for completed items (e.g.sports=72h,general=24h)-once-run-for
Output/state:
- recorded
.tsfiles under<out-dir>/<lane>/(each capture uses a.partial.tsspool path until the transfer finishes, then renames to.ts) - optional published media-server-friendly layout under
-publish-dirwith linked/copied.tsplus.nfo - persistent recorder state JSON at
<out-dir>/recorder-state.jsonunless overridden with-state-file recorded-publish-manifest.jsonunder-publish-dirwhen publishing is enabled
State file model:
active— currently scheduled or recording itemscompleted— finished recordingsfailed— interrupted or failed recordingsstatistics.lane_storage— optional per-laneused_bytesplusbudget_bytes/headroom_byteswhen byte budgets are configuredstatistics.sum_capture_http_attempts,sum_capture_transient_retries,sum_capture_bytes_resumed,sum_capture_upstream_switches— aggregate capture churn, bytes appended via HTTP Range resume, and catalog upstream advances- per completed/failed item: optional
capture_http_attempts,capture_transient_retries,capture_bytes_resumed,capture_upstream_switchesfor that programme’s capture path
Operational notes:
- this MVP dedupes by
capsule_id, which already collapses duplicate programme variants built from the samedna_id + start + title - the daemon also suppresses duplicate recordings by programme identity (
dna_idor channel fallback + start + normalized title), so duplicate provider variants do not both record if they leak into the scheduler input -channels/-exclude-channelsmatch exactchannel_id,guide_number,dna_id, orchannel_name-retain-completed-per-laneand-retain-failed-per-laneapply newer-first retention within each lane before the global completed/failed caps are enforced-budget-bytes-per-laneapplies newer-first completed-item pruning within each lane usingBytesRecordedor on-disk file sizes, with units likeMiB,GiB, or raw bytes- interrupted active items are preserved as failed
status=interruptedrecords on startup, annotated withrecovery_reason=daemon_restart, partial byte counts when available, and automatically retried if the same programme window is still eligible -onceis useful for cron-style “scan, record what is live/starting now, then exit”- without
-once, the command keeps polling until interrupted or until-run-forelapses - completed and failed recorder state is pruned by retention count, and expired completed items are deleted automatically based on capsule expiry
- when
-publish-diris combined with media-server registration flags, each completed recording can create/reuse the matching lane library and trigger a targeted refresh for that lane (or use-defer-library-refreshto refresh once after the publish manifest updates) - daemon publish-time registration reuses the same lane naming as
catchup-publish(<library-prefix> Sports,<library-prefix> Movies, etc.)
Relevant streaming env knobs for tricky HLS/CDN paths:
IPTV_TUNERR_FFMPEG_HLS_HTTP_PERSISTENT(true|false, defaultfalse) — ask ffmpeg/libavformat to reuse HTTP connections across HLS fetches; leave off on ffmpeg builds that do not support the optionIPTV_TUNERR_FFMPEG_HLS_MULTIPLE_REQUESTS(true|false, defaulttrue) — allow multiple HTTP requests on a persistent connection for HLS inputIPTV_TUNERR_FFMPEG_HLS_LIVE_START_INDEX(default0) — optional ffmpeg HLS live-edge offset; keep disabled on ffmpeg builds that do not support-live_start_indexIPTV_TUNERR_HLS_PLAYLIST_RETRY_LIMIT(default2) — extra retries for playlist refreshes that fail with a learned/concurrency-style upstream limit (423,429,458,509, or matching body text)IPTV_TUNERR_HLS_PLAYLIST_RETRY_BACKOFF_MS(default1000) — base backoff for those retries; attempts use1x,2x,4xIPTV_TUNERR_UPSTREAM_RETRY_LIMIT(default2) — extra retries on the same stream URL when response indicates upstream concurrency limits (423,429,458,509, or matching body text) before moving to backupsIPTV_TUNERR_UPSTREAM_RETRY_BACKOFF_MS(default1000) — base backoff for same-URL stream retries; attempts use1x,2x,4x, withRetry-Afterhonored when largerIPTV_TUNERR_HLS_RELAY_PREFER_GO_ON_PROVIDER_PRESSURE(true|false, defaulttrue) — for non-transcode HLS, prefer the Go relay over ffmpeg remux when Tunerr has seen provider concurrency pressure or the current stream host has a non-zero penalty fromIPTV_TUNERR_PROVIDER_AUTOTUNEfailure accounting (e.g. a recentffmpeg_hls_failedon that host). Setfalseto disable this entire branch (concurrency + host-penalty); useIPTV_TUNERR_HLS_RELAY_PREFER_GOto force Go relay regardless.IPTV_TUNERR_HLS_RELAY_PREFER_GO(true|false, defaultfalse) — force Go-relay preference even when the pressure/penalty signals above are absent
These are legitimate transport-parity knobs, not Cloudflare bypasses.
Summarize the persistent recorder state file without starting the daemon.
Common flags:
-state-file— required unlessIPTV_TUNERR_CATCHUP_RECORDER_STATE_FILEis set-limit-out
Returned fields include:
- aggregate recorder
statistics published_countinterrupted_count- per-lane counts
- recent
active,completed, andfaileditems
Import upstream cookies (typically cf_clearance) from a browser export into Tunerr’s persistent cookie jar.
Three input formats accepted:
Inline cookie string:
iptv-tunerr import-cookies \
-jar "$IPTV_TUNERR_COOKIE_JAR_FILE" \
-cookie "cf_clearance=<value>" \
-domain provider.example.comNetscape/Cookie-Editor export:
iptv-tunerr import-cookies \
-jar "$IPTV_TUNERR_COOKIE_JAR_FILE" \
-netscape /tmp/cookies.txtHAR file (DevTools "Save all as HAR with content"):
iptv-tunerr import-cookies \
-jar "$IPTV_TUNERR_COOKIE_JAR_FILE" \
-har /tmp/provider-session.harHAR import deduplicates cookies by name+domain+path and derives domain from the Host request header when the cookie domain field is empty.
Common flags:
-jar— required; path to cookie jar JSON file-cookie— inlinename=valuecookie string (use with-domain)-domain— domain for-cookieinput-netscape— path to Netscape-format cookie file-har— path to HAR file from browser DevTools-ttl— cookie lifetime in seconds when no expiry is set in the source (default: 24 hours)
See also: cloudflare-bypass.md
Offline view of per-host Cloudflare state. No running server required — reads directly from the cookie jar and cf-learned.json.
Shows per host: CF-tagged flag, cf_clearance presence and time-to-expiry, working UA learned by cycling.
Common flags:
-jar— cookie jar JSON path (default:IPTV_TUNERR_COOKIE_JAR_FILE)-learned— CF learned JSON path (default:IPTV_TUNERR_CF_LEARNED_FILEorcf-learned.jsonbeside the jar)-json— machine-readable output
Example:
iptv-tunerr cf-status
iptv-tunerr cf-status -json | jqSee also: cloudflare-bypass.md
Collect Tunerr-side diagnostic state into a bundle directory or .tar.gz for sharing with maintainers or feeding into scripts/analyze-bundle.py.
What is collected:
stream-attempts.json— last 500 stream attempts from/debug/stream-attempts.jsonprovider-profile.json— autopilot state from/provider/profile.jsoncf-learned.json— per-host CF state (working UA, CF-tagged flag)cookie-meta.json— cookie names/domains/expiry, no cookie values (safe to share)env.json— allIPTV_TUNERR_*vars, secrets redacted by defaultbundle-info.json— timestamp, version, collection summary
Common flags:
-url— base URL of running server (defaulthttp://localhost:5004)-out— output directory (defaultdebug-scratch/)--tar— also writetunerr-debug-TIMESTAMP.tar.gz--redact— redact secrets from env dump (defaulttrue)--no-server— skip live server fetch, collect only local state files
Example:
iptv-tunerr debug-bundle --out ./debug-scratch --tarSee also: debug-bundle.md
Fetch and inspect free public IPTV channels from configured sources without affecting the running catalog. Useful for exploring feeds before enabling them in production.
| Flag | Default | Meaning |
|---|---|---|
-by-group |
false | Print channel count summary grouped by group-title |
-catalog |
— | Path to a catalog JSON file; prints what would be added (channels not already present) |
-probe |
false | Run a live HTTP probe pass on fetched channels |
-probe-concurrency |
10 | Parallel probe workers |
-probe-timeout |
8s | Per-channel probe timeout |
-probe-max |
0 | Cap number of channels probed (0 = all) |
-require-tvgid |
false | Only include channels that have a tvg-id |
-limit |
0 | Print only first N results (0 = all) |
-json |
false | JSON output for scripting |
Examples:
# What groups are in the iptv-org US feed?
IPTV_TUNERR_FREE_SOURCE_IPTV_ORG_COUNTRIES=us \
iptv-tunerr free-sources -by-group
# What would be added to an existing catalog?
IPTV_TUNERR_FREE_SOURCES="$IPTV_TUNERR_FREE_SOURCE_PRIGOANA_URL" \
iptv-tunerr free-sources -catalog ./catalog.json
# Probe a sample of 50 channels to check live pass rate
IPTV_TUNERR_FREE_SOURCE_IPTV_ORG_ALL=true \
iptv-tunerr free-sources -probe -probe-max 50Discover physical SiliconDust HDHomeRun tuners on the local network, or query a device by HTTP only.
- UDP (default): broadcast discovery on port
65001, collectdiscoverreplies (device id, base URL, tuner count). - HTTP:
-addr http://<device-ip>skips UDP and loadsdiscover.json(and optionallylineup.json).
Flags:
| Flag | Meaning |
|---|---|
-timeout |
UDP listen window (default 3s; ignored with -addr) |
-addr |
Base URL of the device (e.g. http://192.168.1.100) — HTTP-only mode |
-lineup |
Also GET lineup.json and print channel count / metadata |
-json |
JSON output for scripting |
-guide-xml |
GET guide.xml (XMLTV) from each device base; prints byte size and counts <channel> / <programme> elements (does not merge into Tunerr) |
Merge semantics for HDHR + IPTV catalogs: adr/0002-hdhr-hardware-iptv-merge.md.
| Env | Meaning |
|---|---|
IPTV_TUNERR_WEBUI_DISABLED |
If 1, disable the dedicated dashboard on port 48879 (0xBEEF). |
IPTV_TUNERR_WEBUI_PORT |
Dedicated dashboard port (default 48879). |
IPTV_TUNERR_WEBUI_ALLOW_LAN |
If 1, allow non-loopback clients to open the dedicated dashboard (default: localhost only). |
IPTV_TUNERR_WEBUI_STATE_FILE |
Optional JSON state file for server-derived deck activity plus non-secret deck preferences across process restarts. |
IPTV_TUNERR_WEBUI_USER |
Dedicated deck HTTP Basic auth username (defaults to admin only when unset at startup). |
IPTV_TUNERR_WEBUI_PASS |
Dedicated deck HTTP Basic auth password. When unset, Tunerr generates a one-time startup password instead of using admin/admin. That generated password is logged once at startup and shown on the localhost login page until you pin a real password. |
IPTV_TUNERR_EVENT_WEBHOOKS_FILE |
Optional JSON file that configures outbound event webhooks. Each hook can declare name, url, optional events, optional extra headers, and optional per-hook timeout duration. /debug/event-hooks.json redacts credential-bearing webhook URLs and sensitive custom header values in its report; delivery still uses the configured values. |
IPTV_TUNERR_RECORDING_RULES_FILE |
Optional JSON file for durable server-side recording rules used by /recordings/rules.json, /recordings/rules/preview.json, and /recordings/history.json. |
IPTV_TUNERR_PLEX_LINEUP_HARVEST_FILE |
Optional JSON file storing the latest persisted Plex lineup-harvest report, surfaced via /programming/harvest.json, /programming/preview.json, and /programming/harvest-import.json. |
IPTV_TUNERR_VIRTUAL_CHANNELS_FILE |
Optional JSON file for file-backed virtual-channel rules used by /virtual-channels/rules.json, /virtual-channels/preview.json, /virtual-channels/schedule.json, /virtual-channels/live.m3u, and /virtual-channels/stream/<id>.mp4. |
IPTV_TUNERR_VIRTUAL_CHANNEL_BRANDING_DEFAULT |
When truthy, virtual channels that carry branding metadata are published through /virtual-channels/branded-stream/<id>.ts by default in /virtual-channels/live.m3u instead of the plain /virtual-channels/stream/<id>.mp4 path. |
IPTV_TUNERR_VIRTUAL_CHANNEL_RECOVERY_WARMUP_SEC |
Optional startup monitoring window for virtual-channel recovery. When higher than a channel’s recovery.black_screen_seconds, the response-byte sampler keeps watching until this longer warmup window before deciding whether to cut over to filler. |
IPTV_TUNERR_VIRTUAL_CHANNEL_RECOVERY_MIDSTREAM_PROBE_BYTES |
Optional rolling sampled-byte window for in-session virtual-channel media-content checks. When set, the live recovery relay repeatedly probes windows of this many bytes from the active stream body after startup and can trigger filler if a later sampled window probes as black/silent. Defaults to a bounded value derived from IPTV_TUNERR_VIRTUAL_CHANNEL_RECOVERY_PROBE_MAX_BYTES. |
IPTV_TUNERR_VIRTUAL_CHANNEL_RECOVERY_LIVE_STALL_SEC |
Optional per-read stall watchdog for virtual-channel filler recovery. When set, the plain/branded virtual stream path will attempt a one-time switch to the configured filler entry if the active upstream stops producing bytes for this many seconds after startup. |
IPTV_TUNERR_VIRTUAL_CHANNEL_RECOVERY_STATE_FILE |
Optional JSON file for persisting recent virtual-channel recovery events across restarts so /virtual-channels/recovery-report.json and /virtual-channels/report.json keep historical recovery posture instead of resetting to empty on process start. Source and fallback URLs are redacted before persistence/reporting, and new state files are written with private permissions. |
IPTV_TUNERR_VIRTUAL_CHANNEL_DENY_LITERAL_PRIVATE_UPSTREAM |
When truthy/default, virtual-channel plain/branded playback rejects catalog movie/episode upstream URLs whose host is a literal loopback, RFC1918-private, link-local, or unspecified IP before proxying or probing. Hostnames are not resolved. Set false only for explicit lab/private-origin testing. |
IPTV_TUNERR_XTREAM_USER |
Optional username for the read-only downstream Xtream-compatible live output. Requires IPTV_TUNERR_XTREAM_PASS. |
IPTV_TUNERR_XTREAM_PASS |
Optional password for the read-only downstream Xtream-compatible live output. Requires IPTV_TUNERR_XTREAM_USER. |
IPTV_TUNERR_XTREAM_VOD_DENY_LITERAL_PRIVATE_UPSTREAM |
When truthy/default, /movie/... and /series/... reject catalog VOD upstream URLs whose host is a literal loopback, RFC1918-private, link-local, or unspecified IP. Hostnames are not resolved. Set false only for explicit lab/private-origin testing. |
IPTV_TUNERR_UI_DISABLED |
If 1, /ui/ is not served. |
IPTV_TUNERR_UI_ALLOW_LAN |
If 1, allow non-loopback clients to open /ui/ (default: localhost only). |
Browser URLs:
- Dedicated deck:
http://127.0.0.1:48879/by default. It reverse-proxies tuner endpoints under/api/*, surfaces runtime settings from/api/debug/runtime.json, opens on a login page with a cookie-backed session, accepts direct HTTP Basic auth for scriptable/API access without minting browser sessions, generates a one-time startup password whenIPTV_TUNERR_WEBUI_PASSis unset, and exposes read-only deck telemetry under/deck/telemetry.jsonplus server-derived operator activity under/deck/activity.json. - Event hooks:
/debug/event-hooks.jsonreports configured hooks and recent delivery attempts. Lifecycle events currently includelineup.updated,stream.requested,stream.rejected, andstream.finished. - Xtream output (expanded starter): when
IPTV_TUNERR_XTREAM_USERandIPTV_TUNERR_XTREAM_PASSare set, Tunerr exposes a read-only downstream Xtream-compatible surface at/player_api.php(get_live_streams,get_live_categories,get_vod_categories,get_vod_streams,get_series_categories,get_series,get_series_info) plus/live/<user>/<pass>/<channel>.ts,/movie/<user>/<pass>/<id>.mp4, and/series/<user>/<pass>/<episode>.mp4. Generated path segments are URL-escaped, and the VOD proxy blocks literal private-IP upstream URLs by default viaIPTV_TUNERR_XTREAM_VOD_DENY_LITERAL_PRIVATE_UPSTREAM. - Xtream entitlements (starter): when
IPTV_TUNERR_XTREAM_USERS_FILEis set, Tunerr loads file-backed downstream users with per-user live/VOD/series access scopes, filtersplayer_api.phpresults for those users, gates/live|movie|series/...playback by the same rules, and exposes the current ruleset at/entitlements.json. - Recording rules (starter): when
IPTV_TUNERR_RECORDING_RULES_FILEis set, Tunerr exposes durable recorder-rule CRUD at/recordings/rules.json, live capsule matching at/recordings/rules/preview.json, and recorder-state classification at/recordings/history.json. - Programming harvest bridge: when
IPTV_TUNERR_PLEX_LINEUP_HARVEST_FILEis set, Tunerr reloads the saved harvest report and exposes it at/programming/harvest.json;/programming/preview.jsonalso includesharvest_readyplus dedupedharvest_lineupsso the Programming lane can surface harvested candidate lineups alongside recipe state;/programming/harvest-import.jsoncan preview or apply a chosen harvested lineup as a real saved Programming Manager recipe and reports which matching strategy succeeded (tvg_id_exact,guide_name_exact,guide_number_exact, orlocal_broadcast_stem). - Virtual channels (starter): when
IPTV_TUNERR_VIRTUAL_CHANNELS_FILEis set, Tunerr exposes/virtual-channels/rules.jsonfor durable file-backed rules,/virtual-channels/preview.jsonfor schedule previews over catalog movies/episodes,/virtual-channels/schedule.jsonfor a rolling schedule horizon,/virtual-channels/live.m3ufor a publishable synthetic-channel export,/virtual-channels/recovery-report.jsonfor recent filler/recovery events,/virtual-channels/branded-stream/<id>.tsfor branded playback, and/virtual-channels/stream/<id>.mp4for the current scheduled asset proxy. WhenIPTV_TUNERR_VIRTUAL_CHANNEL_BRANDING_DEFAULTis truthy, branded virtual channels are published through the branded stream path by default in/virtual-channels/live.m3u.IPTV_TUNERR_VIRTUAL_CHANNEL_RECOVERY_LIVE_STALL_SECextends that recovery lane past startup by enabling live cutover across the ordered fallback chain when the active upstream stalls,IPTV_TUNERR_VIRTUAL_CHANNEL_RECOVERY_MIDSTREAM_PROBE_BYTESlets that same relay perform repeated rolling in-session media-byte probes for later black/silent degradation, andIPTV_TUNERR_VIRTUAL_CHANNEL_RECOVERY_STATE_FILEmakes those recovery events survive process restarts. Virtual-channel playback blocks literal private-IP upstream URLs by default throughIPTV_TUNERR_VIRTUAL_CHANNEL_DENY_LITERAL_PRIVATE_UPSTREAM. - The deck Settings lane can now also apply
virtual_channel_recovery_live_stall_seclive through the localhost-only operator action path. Like shared replay bytes, it affects new sessions only. - When
IPTV_TUNERR_WEBUI_STATE_FILEis configured, the deck now persists bothshared_relay_replay_bytesandvirtual_channel_recovery_live_stall_secand replays them to the tuner on startup, so those live runtime settings survive process restarts. - Active stream intervention:
/debug/active-streams.jsonshows live request IDs and/ops/actions/stream-stopaccepts{"request_id":"..."}or{"channel_id":"..."}to cancel matching active stream contexts from the localhost operator plane. - Shared relay visibility:
/debug/shared-relays.jsonshows current same-channel shared-output sessions acrosshls_go, live FFmpeg HLS reuse, and packaged-HLS reuse, includingshared_upstream,content_type, producer request ID, start time, and subscriber counts when duplicate consumers are attached to one upstream producer. - Shared relay control:
/ops/actions/shared-relay-replayacceptsPOST {"shared_relay_replay_bytes":262144}from the localhost operator plane and applies that replay window to new shared live sessions; the deck Settings lane drives the same action and reflects the active value from/debug/runtime.json. - Programming Manager detail view:
/programming/channel-detail.json?channel_id=<id>&horizon=3h&limit=6returns focused channel metadata, exact-match backup alternatives, and upcoming programme capsules for category-first channel-builder tools. - Guide/operator endpoints include
/guide/lineup-match.json, which reports whether currentlineup.jsonrows have exact-name counterparts in emittedguide.xml, plus duplicate-name/number signals and a sample of unmatched rows includingchannel_id,guide_number,guide_name, and observedtvg_id. - Startup contract: until the first real merged guide is cached,
/guide.xmlreturns503 Service UnavailablewithRetry-After: 5,X-IptvTunerr-Guide-State: loading, and a visible placeholder XMLTV body. HDHR discovery/lineup endpoints stay200, but emitX-IptvTunerr-Startup-State: loadingwhile no lineup channels are loaded yet;/lineup_status.jsonreportsScanInProgress=1andLineupReady=falseduring that startup window, and an empty/lineup.jsonaddsRetry-After: 5. - Legacy pages on the tuner port:
http://127.0.0.1:<port>/ui/(home),/ui/guide/(merged guide preview from cache),/ui/guide-preview.json(JSON; optional?limit=).
Transcode profile names, HDHomeRun-style aliases, and ?profile= on /stream/<id>: transcode-profiles.md.
Probe provider URLs and print ranked results (best host first).
Common flags:
-urls
Use for:
- provider host failover validation
- diagnosing Cloudflare/proxy failures
Harvest Plex lineup candidates and emit a structured report.
Modes:
-mode oracle- probe Plex's HDHR guide/channelmap flow across several tuner lineup variants
- useful when you want to sweep lineup caps or tuner shapes and see which lineup Plex maps back
-mode provider- query Plex's real provider lineup catalog directly by country + postal code
- useful when you want real provider titles and lineup rows instead of synthetic
harvest-*DVR titles
Common flags:
-mode-plex-url-token-base-urls-base-url-template-caps-friendly-name-prefix-country-postal-code-lineup-types-title-query-lineup-limit-include-channels-provider-base-url-provider-version-wait-poll-reload-guide-activate-out
Notes:
oraclemode creates/registers real Plex DVR/device rows during the probe flow.oraclemode polls channel-map results for a bounded time instead of fetching only once.providermode talks to Plex's provider EPG service directly and does not create DVR/device rows.- Both modes emit per-result rows plus a deduped
lineups[]summary in JSON. - See plex-lineup-harvest.
Relevant env vars for deck / operator-driven harvest:
IPTV_TUNERR_PLEX_LINEUP_HARVEST_MODEIPTV_TUNERR_PLEX_LINEUP_HARVEST_BASE_URLSIPTV_TUNERR_PLEX_LINEUP_HARVEST_BASE_URL_TEMPLATEIPTV_TUNERR_PLEX_LINEUP_HARVEST_CAPSIPTV_TUNERR_PLEX_LINEUP_HARVEST_FRIENDLY_NAME_PREFIXIPTV_TUNERR_PLEX_LINEUP_HARVEST_WAITIPTV_TUNERR_PLEX_LINEUP_HARVEST_POLLIPTV_TUNERR_PLEX_LINEUP_HARVEST_RELOAD_GUIDEIPTV_TUNERR_PLEX_LINEUP_HARVEST_ACTIVATEIPTV_TUNERR_PLEX_LINEUP_HARVEST_COUNTRYIPTV_TUNERR_PLEX_LINEUP_HARVEST_POSTAL_CODEIPTV_TUNERR_PLEX_LINEUP_HARVEST_LINEUP_TYPESIPTV_TUNERR_PLEX_LINEUP_HARVEST_TITLE_QUERYIPTV_TUNERR_PLEX_LINEUP_HARVEST_LINEUP_LIMITIPTV_TUNERR_PLEX_LINEUP_HARVEST_INCLUDE_CHANNELSIPTV_TUNERR_PLEX_LINEUP_HARVEST_PROVIDER_BASE_URLIPTV_TUNERR_PLEX_LINEUP_HARVEST_PROVIDER_VERSION
Probe Plex's wizard-equivalent HDHR registration/guide/channelmap flow across one or more tuner base URLs and report what Plex maps.
This is an in-app tool for using Plex as a provider/EPG matching oracle during EPG-linking experiments (for example different lineup sizes/orderings for a region).
Common flags:
-plex-url-token-base-urls(comma-separated tuner URLs to test)-base-url-template+-caps(expand{cap}into multiple URLs)-reload-guide(defaulttrue)-activate(defaultfalse; report/probe only unless enabled)-out(JSON report)
Notes:
- Creates/registers Plex DVR/device rows as part of the probe flow.
- Best used in a lab/test Plex instance.
- Intended to harvest mapping outcomes, not as a runtime dependency.
Clean up DVR/device rows created during oracle experiments.
Default behavior is dry-run (prints matching DVR/device rows without deleting).
Common flags:
-plex-url-token-lineup-prefix(defaultoracle-)-device-uri-substr(optional extra filter)-do(actually delete)
Typical flow:
- Dry-run inspect:
iptv-tunerr plex-epg-oracle-cleanup -plex-url ... -token ...
- Apply cleanup:
iptv-tunerr plex-epg-oracle-cleanup -plex-url ... -token ... -do
Run multiple child iptv-tunerr instances from one JSON config.
Common flags:
-config
Use for:
- single-app / multi-DVR category deployments
- combined injected DVR + HDHR wizard lanes
Multi-host failover (one subscription, multiple CDN endpoints):
IPTV_TUNERR_PROVIDER_URLS— comma-separated list of provider base URLs; all are probed at startup, fastest/healthiest wins for indexing, rest become per-channel stream URL fallbacks
Single provider:
IPTV_TUNERR_PROVIDER_URLIPTV_TUNERR_PROVIDER_USERIPTV_TUNERR_PROVIDER_PASS
Multiple subscriptions (numbered suffix, merge into one catalog):
IPTV_TUNERR_PROVIDER_URL_2,IPTV_TUNERR_PROVIDER_USER_2,IPTV_TUNERR_PROVIDER_PASS_2IPTV_TUNERR_PROVIDER_URL_3,IPTV_TUNERR_PROVIDER_USER_3,IPTV_TUNERR_PROVIDER_PASS_3- (pattern continues for
_4,_5, ...) - Channels with duplicate
tvg-idvalues across providers are deduplicated — one lineup entry, all stream URLs merged as fallbacks
Other:
IPTV_TUNERR_SUBSCRIPTION_FILEIPTV_TUNERR_M3U_URL
Optional: probe each channel's primary stream URL at index time and drop channels that fail. Eliminates dead channels before they ever appear in the lineup.
IPTV_TUNERR_SMOKETEST_ENABLED(false) — enable the probe passIPTV_TUNERR_SMOKETEST_TIMEOUT(8s) — per-channel probe timeoutIPTV_TUNERR_SMOKETEST_CONCURRENCY(10) — parallel probe workersIPTV_TUNERR_SMOKETEST_MAX_CHANNELS(0= unlimited) — random sample cap; 0 probes all channelsIPTV_TUNERR_SMOKETEST_MAX_DURATION(5m) — wall-clock cap for the full probe passIPTV_TUNERR_SMOKETEST_CACHE_FILE— path to persistent per-URL result cache; skips re-probing fresh entries on subsequent runsIPTV_TUNERR_SMOKETEST_CACHE_TTL(4h) — how long a cached result is considered fresh
Probe method:
- MPEG-TS: HTTP Range request for first 4 KB (avoids pulling full streams); 200 or 206 = pass
- HLS (
.m3u8): GET playlist; validates#EXTM3U/#EXTINFor a non-comment segment URI
Supplement or enrich the paid catalog with public M3U feeds at index time. No redistribution — sources are fetched fresh per catalog build.
| Env | Default | Meaning |
|---|---|---|
IPTV_TUNERR_FREE_SOURCES |
— | Comma-separated public M3U URLs |
IPTV_TUNERR_FREE_SOURCE_IPTV_ORG_COUNTRIES |
— | Country codes (us,gb,ca) — uses iptv-org per-country feeds |
IPTV_TUNERR_FREE_SOURCE_IPTV_ORG_CATEGORIES |
— | Category slugs (news,sports) — uses iptv-org per-category feeds |
IPTV_TUNERR_FREE_SOURCE_IPTV_ORG_ALL |
false |
true — use iptv-org combined all-channels feed |
| Env | Default | Meaning |
|---|---|---|
IPTV_TUNERR_FREE_SOURCE_MODE |
supplement |
supplement — add channels absent from paid lineup; merge — append free URLs as paid-channel fallbacks; full — deduplicate combined catalog, paid takes precedence |
| Env | Default | Meaning |
|---|---|---|
IPTV_TUNERR_FREE_SOURCE_CACHE_TTL |
6h |
How long downloaded M3U and iptv-org API files are cached on disk |
IPTV_TUNERR_FREE_SOURCE_CACHE_DIR |
<CacheDir>/free-sources |
Override disk cache directory |
| Env | Default | Meaning |
|---|---|---|
IPTV_TUNERR_FREE_SOURCE_FILTER_NSFW |
true |
Drop NSFW channels (from iptv-org blocklist/channels.json); false = keep but tag GroupTitle with [NSFW] <category> |
IPTV_TUNERR_FREE_SOURCE_FILTER_CLOSED |
true |
Drop channels with a closure date in iptv-org channels.json |
IPTV_TUNERR_FREE_SOURCE_REQUIRE_TVG_ID |
false |
Drop channels without a tvg-id (EPG-linkable channels only) |
IPTV_TUNERR_FREE_SOURCE_SMOKETEST |
false |
Probe free channels at index time; reuses IPTV_TUNERR_SMOKETEST_CACHE_FILE |
Quick-start examples:
# Add US/GB news & sports to paid lineup (supplement mode)
IPTV_TUNERR_FREE_SOURCE_IPTV_ORG_COUNTRIES=us,gb
IPTV_TUNERR_FREE_SOURCE_IPTV_ORG_CATEGORIES=news,sports
# Use a custom public feed, keep NSFW but route via supervisor
IPTV_TUNERR_FREE_SOURCES="$IPTV_TUNERR_FREE_SOURCE_PRIGOANA_URL"
IPTV_TUNERR_FREE_SOURCE_FILTER_NSFW=false
# Append free URLs as fallbacks behind paid channels
IPTV_TUNERR_FREE_SOURCE_IPTV_ORG_ALL=true
IPTV_TUNERR_FREE_SOURCE_MODE=mergeIPTV_TUNERR_CATALOGIPTV_TUNERR_MOUNTIPTV_TUNERR_CACHE
IPTV_TUNERR_BASE_URLIPTV_TUNERR_DEVICE_IDIPTV_TUNERR_FRIENDLY_NAMEIPTV_TUNERR_TUNER_COUNTIPTV_TUNERR_LINEUP_MAX_CHANNELSIPTV_TUNERR_GUIDE_NUMBER_OFFSETIPTV_TUNERR_GUIDE_NUMBER_RESEQUENCEIPTV_TUNERR_GUIDE_NUMBER_RESEQUENCE_STARTIPTV_TUNERR_LINEUP_RECIPE— intelligence-driven lineup shaping:high_confidence= keep only channels with strong guide-confidence signalsbalanced= rank by combined guide + stream scoreguide_first= rank by guide confidence before stream resilienceresilient= rank by backup-stream resilience before guide scoresports_now= keep sports-heavy channels onlysports_na= keep North America sports-first channels onlykids_safe= keep kid/family channels while excluding obvious unsafe/adult/news matcheslocals_first= bubble likely local/regional channels to the top using the same North-American lineup-shape heuristics
IPTV_TUNERR_LINEUP_EXCLUDE_RECIPE— remove channels selected by a built-in recipe before applying the final lineup recipe. Example: set the primary DVR toIPTV_TUNERR_LINEUP_EXCLUDE_RECIPE=sports_naand the second DVR toIPTV_TUNERR_LINEUP_RECIPE=sports_naso the two lineups do not overlap.IPTV_TUNERR_LINEUP_DROP_SPORTS— optional sports/event-like row filter for general/cable DVRs. Set totrueon a primary/general DVR when a separate sports DVR owns sports; leave unset onsports_naDVRs.IPTV_TUNERR_LINEUP_DEDUPE— optional strong duplicate collapse before shard/cap logic. Set tostable,identity,strong, ortrueto keep the best representative when rows share atvg-idor normalized channel name; leave off for recipe-specific lineups such assports_nawhen you want no extra collapse.IPTV_TUNERR_LINEUP_EXCLUDE_CHANNEL_IDS— comma/space/newline separated exactchannel_id, guide number, ortvg-idvalues to remove before lineup recipe/shaping/cap logic. Use this for surgical excludes; preferIPTV_TUNERR_LINEUP_EXCLUDE_RECIPEfor durable primary+sports splits.IPTV_TUNERR_DNA_POLICY— optional duplicate-variant policy keyed bydna_id:off= keep all variantsprefer_best= keep the strongest duplicate by combined channel-intelligence scoreprefer_resilient= keep the most backup-stream-resilient duplicate first
IPTV_TUNERR_DNA_PREFERRED_HOSTS— optional comma-separated preferred provider/CDN authorities (for examplepreferred.example,backup.example:8080) used as a tie-breaker when duplicate variants share the samedna_idIPTV_TUNERR_GUIDE_POLICY— optional runtime guide-quality policy:off= current permissive behaviorhealthy= keep only channels with real programme rows once cached guide-health is availablestrict= same ashealthy, plus require a non-emptyTVGID
IPTV_TUNERR_REGISTER_RECIPE— optional media-server registration recipe:off= register channels in current catalog orderbalanced= rank by combined guide + stream scorehigh_confidence= bias registration order toward stronger guide-confidence channelsguide_first= prefer guide confidence, then stream resilienceresilient= prefer backup-stream resilience, then guide confidencehealthy= likehigh_confidence, but also drops poor-tier channels before Plex/Emby/Jellyfin registrationsports_now= register sports-heavy channels onlysports_na= register North America sports-first channels onlykids_safe= register kid/family-safe channels onlylocals_first= keep the full set, but bubble likely locals/regionals to the top
IPTV_TUNERR_GUIDE_NUMBER_OFFSET:
- adds a per-instance channel/guide ID offset
- useful for many DVRs in Plex to avoid guide cache collisions
IPTV_TUNERR_GUIDE_NUMBER_RESEQUENCE:
- when
true, rewrites guide numbers to a simple ascending sequence after lineup shaping so downstream clients see the curated order as the visible channel-number order too - pair with
IPTV_TUNERR_GUIDE_NUMBER_RESEQUENCE_STARTto choose the first number in that sequence (default1)
-
IPTV_TUNERR_STREAM_TRANSCODE—offremux only;onalways transcode (libx264/AAC);autorun ffprobe on tune and transcode only if codecs are not Plex-friendly (e.g. HEVC, VP9);auto_cached(aliascached_auto) remux-first using onlyIPTV_TUNERR_TRANSCODE_OVERRIDES_FILE(no ffprobe; channels not listed in the file stay on remux). -
IPTV_TUNERR_TRANSCODE_OVERRIDES_FILE— JSON object mapping channel_id, guide_number, or tvg_id →true/false(or stringtranscode/remux). Withoff/on/auto, the file overrides the global decision for matching channels (remux-first escapes foron, or per-channel transcode underoff). Withauto_cached, the file is the policy. Precedence after this: client adaptation (IPTV_TUNERR_CLIENT_ADAPT/ Autopilot) may still force websafe transcode for browser-class clients. See plex-livetv-http-tuning (HR-007). -
IPTV_TUNERR_PROFILE_OVERRIDES_FILE— JSON per-channel ffmpeg profile names (same key order as transcode overrides). Used when transcoding is active. Values may be built-in / HDHR alias names or names defined inIPTV_TUNERR_STREAM_PROFILES_FILE. -
IPTV_TUNERR_STREAM_PROFILES_FILE— optional JSON object defining custom profile names for?profile=and per-channel overrides. Each value is an object:base_profile(built-in preset:default,dashfast, …), optionaltranscode(true/false, defaulttruefor custom names), optionaloutput_mux(mpegts, packagedhls, orfmp4/mp4/dash), optionaldescription.output_mux: "hls"starts a background ffmpeg HLS packager and serves the generated playlist/segments back through Tunerr. See transcode-profiles. -
IPTV_TUNERR_STREAM_BUFFER_BYTES(0|auto|<bytes>) —autoenables adaptive buffering when transcoding;0disables; a fixed integer (e.g.2097152) sets a 2 MiB buffer. -
IPTV_TUNERR_STREAM_PUBLIC_BASE_URL— optional no-trailing-slash base URL (e.g.http://192.168.1.10:5004) prepended to?mux=hlsplaylist media lines so clients that mishandle relative URLs still resolve Tunerr. Empty = relative/stream/...lines only. -
IPTV_TUNERR_HLS_MUX_CORS— whentrue/1/on, add CORS headers on?mux=hlsplaylist and?mux=hls&seg=responses and handleOPTIONSpreflight for those URLs (for browser-based players or devtools). Default off. -
IPTV_TUNERR_HLS_MUX_MAX_CONCURRENT— optional absolute cap for concurrent?mux=hls|dash&seg=proxy requests. Default derives from effective tuner limit; when set, disables adaptive bonus below. -
IPTV_TUNERR_HLS_MUX_SEG_SLOTS_PER_TUNER— multiplier for the defaultseg=concurrency cap (effective_tuner_limit * slots_per_tuner, default8). -
IPTV_TUNERR_HLS_MUX_SEG_SLOTS_AUTO— when enabled, add temporary bonus slots from recent 503 seg-limit rejections (requiresMAX_CONCURRENTunset). Tunables:IPTV_TUNERR_HLS_MUX_SEG_AUTO_WINDOW_SEC(default 60, 5–600),IPTV_TUNERR_HLS_MUX_SEG_AUTO_BONUS_PER_HIT(default 4),IPTV_TUNERR_HLS_MUX_SEG_AUTO_BONUS_CAP(default 64). -
IPTV_TUNERR_HLS_MUX_ACCESS_LOG— append one JSON line per successfulseg=(redacted upstream URL, duration). -
IPTV_TUNERR_HLS_MUX_MAX_SEG_PARAM_BYTES— max length of the URL-decodedseg=value (default 262144; hard-capped). Over-limit400withX-IptvTunerr-Hls-Mux-Error: seg_param_too_large. -
IPTV_TUNERR_HLS_MUX_DENY_LITERAL_PRIVATE_UPSTREAM— whentrue/1/on(default), rejectseg=URLs whose host is a literal IP that is loopback, RFC1918-private, link-local, or unspecified (hostnames are not resolved). Response403withblocked_private_upstreamdiagnostic. Setfalseonly for explicit lab/private-origin testing. -
IPTV_TUNERR_HLS_MUX_UPSTREAM_ERR_BODY_MAX— max bytes of upstream error body to buffer when returningupstream_http_<status>forseg=(default 8192, hard-capped at 1 MiB). -
IPTV_TUNERR_HLS_MUX_DENY_RESOLVED_PRIVATE_UPSTREAM— when enabled, resolveseg=host (A/AAAA) and block if any IP is loopback/private/link-local/unspecified (DNS failure → allow, with a warning log). -
IPTV_TUNERR_HLS_MUX_SEG_RPS_PER_IP— optional per–source-IP rate limit (token bucket) forseg=onmux=hlsandmux=dash; exceeded → 429 with diagnosticseg_rate_limited. -
IPTV_TUNERR_HLS_MUX_WEB_DEMO— when true, serve/debug/hls-mux-demo.htmlbehind the operator localhost/LAN gate (static hls.js sample; still enableIPTV_TUNERR_HLS_MUX_CORSfor cross-origin playlists). -
IPTV_TUNERR_METRICS_ENABLE— when true/1/on, register Prometheus mux counters +iptv_tunerr_mux_seg_request_duration_secondshistogram, Autopilot consensus gauges (iptv_tunerr_autopilot_consensus_*), upstream host quarantine counter (iptv_tunerr_upstream_quarantine_skips_total), and exposeGET /metrics. -
IPTV_TUNERR_METRICS_MUX_CHANNEL_LABELS— when enabled, addchannel_idto mux metric labels (very high Prometheus cardinality; default off). -
IPTV_TUNERR_HTTP_ACCEPT_BROTLI— when enabled, sendbrinAccept-Encodingand transparently decompressContent-Encoding: bron the shared HTTP client (including?mux=*&seg=fetches). -
IPTV_TUNERR_HLS_MUX_DASH_EXPAND_SEGMENT_TEMPLATE— when enabled, expand uniform self-closing DASHSegmentTemplate(duration/timescale,$Number$inmedia, no$Time$) intoSegmentListbefore MPD URL rewrite (default off; large manifests possible). -
IPTV_TUNERR_HLS_MUX_DASH_EXPAND_MAX_SEGMENTS— maxSegmentURLentries per expanded template (default 10000, hard-capped at 500000). -
IPTV_TUNERR_HTTP_MAX_IDLE_CONNS_PER_HOST— optional override for the sharedhttp.TransportMaxIdleConnsPerHost(default 16), shared byinternal/httpclient(process start only). -
IPTV_TUNERR_HTTP_MAX_IDLE_CONNS— optional override forMaxIdleConnson that transport (default 100). -
IPTV_TUNERR_HTTP_IDLE_CONN_TIMEOUT_SEC— optional idle connection lifetime in seconds (default 90). See plex-livetv-http-tuning.These three knobs affect every call path that uses
httpclient.Default(),httpclient.WithTimeout, orForStreaming()’s transport clone — including indexer (M3U / Xtream), stream gateway upstream fetches, materializer, vodfs, Plex / Emby registration HTTP, HDHRdiscover.json/lineup.json/guide.xml, provider M3U probes,tunerEPG pipeline HTTP, health checks, andinternal/probe. They do not reconfigure ad hoc clients that bypassinternal/httpclient(rare); mux?mux=*&seg=uses a dedicated client with redirect validation (mux_http_client.go). -
IPTV_TUNERR_HLS_MUX_SEG_AUTOPILOT_BONUS— when enabled, add extra concurrentseg=capacity for channels whosedna_idhas “hot” Autopilot rows (bestHitsacross client classes); tunablesIPTV_TUNERR_HLS_MUX_SEG_AUTOPILOT_MIN_HITS,_BONUS_PER_STEP,_BONUS_CAP. Ignored whenIPTV_TUNERR_HLS_MUX_MAX_CONCURRENTis set. -
HLS / DASH mux —
GET /stream/<channel>?mux=hlson an HLS upstream returns a rewritten M3U8;?mux=dashon a DASH upstream returns a rewritten MPD (experimental). HLSURI='...'(single-quoted) is rewritten likeURI="...". DASHmedia=/initialization=/ similar accept single- or double-quoted values.?mux=dash&seg=preserves$Number%0Nd$/$Time%0Nd$through query encoding. OptionalIPTV_TUNERR_HLS_MUX_DASH_EXPAND_SEGMENT_TEMPLATEexpandsSegmentTemplate(includingSegmentTimeline, paired tags, padded$Number). Nested playlists/segments use?mux=<kind>&seg=<url>. Default stream behavior remains TS remux/transcode whenmuxis omitted. Directseg=targets must be http or https; other schemes return400(JSON-capable clients may sendAccept: application/json).X-IptvTunerr-Hls-Mux-Errordocuments failures; 4xx/5xx from upstream are passed through with a bounded body preview.HEADonseg=is forwarded as HEAD to the upstream.POST /ops/actions/mux-seg-decode(localhost / UI LAN policy) decodes base64seg_b64for support tickets (redacted URL in JSON only). -
Packaged HLS via named profiles — when a named stream profile prefers
output_mux: "hls", Tunerr starts a short-lived ffmpeg HLS packager, returns a packaged playlist, and serves follow-up playlist/segment files through Tunerr under internalmux=hlspkgsession URLs. This is separate from explicit?mux=hls, which still means native rewrite/proxy. -
Byte-range / conditional segments: client
Range/If-Range/If-None-Match/If-Modified-Sinceare forwarded to upstream?mux=hls&seg=fetches;206+Content-Range, or304, are passed back when the CDN responds that way. -
IPTV_TUNERR_FFMPEG_PATH— override the ffmpeg binary path (e.g./opt/ffmpeg-static/current/ffmpeg). -
IPTV_TUNERR_FFMPEG_DISABLED— disable ffmpeg entirely for HLS relay and stay on the Go playlist/segment fetch path. Useful when ffmpeg cannot satisfy provider header/cookie requirements. -
IPTV_TUNERR_FFMPEG_NO_DNS_RESOLVE— keep the original ffmpeg input hostname instead of rewriting it to a resolved IP. Useful for CDNs that validate the hostname againstHostor TLS state; some provider HLS edges reject direct-IP ffmpeg input even when headers are otherwise correct. -
IPTV_TUNERR_FFMPEG_HLS_RECONNECT— whentrue, adds HLS reconnect flags to ffmpeg (-reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1). Helps with providers whose HLS segment URLs expire mid-stream. -
IPTV_TUNERR_FFMPEG_HLS_PACKAGER_DIR— optional base directory for ffmpeg-packaged HLS session workdirs. Empty = system temp dir. -
IPTV_TUNERR_FFMPEG_HLS_PACKAGER_STARTUP_TIMEOUT_MS— wait this long for the first packaged playlist file before falling back to the normal relay path (default 8000). -
IPTV_TUNERR_FFMPEG_HLS_PACKAGER_FILE_WAIT_TIMEOUT_MS— wait this long for a packaged playlist/segment file on follow-up requests (default 4000). -
IPTV_TUNERR_FFMPEG_HLS_PACKAGER_IDLE_SEC— remove an inactive packaged-HLS session after this idle time (default 45). -
IPTV_TUNERR_FFMPEG_HLS_PACKAGER_MAX_AGE_SEC— hard cap on packaged-HLS session lifetime before janitor cleanup (default 300). -
IPTV_TUNERR_FFMPEG_HLS_PACKAGER_LIST_SIZE— ffmpeg HLS playlist window size for packaged-HLS mode (default 6). -
IPTV_TUNERR_FFMPEG_HLS_PACKAGER_SEGMENT_SECONDS— ffmpeg HLS segment target duration for packaged-HLS mode (default 2). -
IPTV_TUNERR_SHARED_RELAY_REPLAY_BYTES— bounded replay buffer in bytes for shared live-output sessions (hls_goand live FFmpeg shared attaches). Default 262144 so late subscribers can receive recent startup bytes; set0to disable replay buffering. -
IPTV_TUNERR_FFMPEG_HLS_FIRST_BYTES_TIMEOUT_MS— for non-transcode HLS ffmpeg-remux, wait this long for the first output bytes before aborting remux and falling back instead of letting the client sit on a dead remux attempt (default 4000;0disables). -
IPTV_TUNERR_HLS_PLAYLIST_RETRY_LIMIT/IPTV_TUNERR_HLS_PLAYLIST_RETRY_BACKOFF_MS— bounded retry/backoff for playlist refreshes that hit provider concurrency/limit responses; intended for short-lived509/similar contention rather than permanent failures. -
IPTV_TUNERR_HLS_RELAY_PREFER_GO_ON_PROVIDER_PRESSURE— skip non-transcode ffmpeg remux and go straight to the Go playlist/segment relay when Tunerr has learned concurrency pressure or the upstream host already has autotune penalty (same process; requiresIPTV_TUNERR_PROVIDER_AUTOTUNEso failures are recorded). Turning this off disables both signals unlessIPTV_TUNERR_HLS_RELAY_PREFER_GOis on. -
IPTV_TUNERR_HLS_RELAY_PREFER_GO— unconditional Go-relay preference (overrides thefalsebranch above). -
IPTV_TUNERR_HLS_RELAY_ALLOW_FFMPEG_CROSS_HOST— defaultfalse; when disabled, non-transcode HLS playlists that reference media/key/map/variant URLs on a different host than the playlist itself skip ffmpeg remux and use the Go relay. This avoids one static ffmpeg request-header context being reused across cross-host HLS subrequests.
Operational note for Plex Live TV: when Plex already transcodes remote playback,
prefer the smallest additional processing chain that stays stable. If the Go HLS
relay returns a clean completed tuner response after the live playlist stops
advancing, Plex may continue polling the now-ended transcode session and surface
stale start.mpd / segment errors. For these feeds, disable forced Go relay,
leave the stdin-normalizer off, use IPTV_TUNERR_PLEX_INTERNAL_FETCHER_POLICY=direct,
and set IPTV_TUNERR_FFMPEG_NO_DNS_RESOLVE=true when the CDN rejects direct-IP
ffmpeg input. Future work should improve Go-relay exit classification and live
playlist recovery so temporary no-new-segment windows do not look like a normal
successful stream completion.
IPTV_TUNERR_CLIENT_ADAPT— whentrue, resolve the Plex client from the active session and adapt stream mode by client class. By default, web/browser clients still get websafe (transcode + MP3 audio), while non-web/native clients stay on the direct/remux path unless another override wins.IPTV_TUNERR_CLIENT_ADAPT_STICKY_FALLBACK— when enabled (default), if adaptation chose the non-websafe path and the tune ends withall_upstreams_failedorupstream_concurrency_limited, register a session-scoped WebSafe fallback for that channel + Plex session/client id until TTL (see plex-livetv-http-tuning HR-004).IPTV_TUNERR_CLIENT_ADAPT_STICKY_TTL_SEC— sticky lifetime in seconds (default 14400; clamped 120–604800).IPTV_TUNERR_CLIENT_ADAPT_STICKY_LOG— set1to include internal sticky map keys in logs (withIPTV_TUNERR_STREAM_DEBUG).IPTV_TUNERR_PLEX_UNKNOWN_CLIENT_POLICY— adaptation policy when Plex session lookup does not resolve a client. Values:websafe(default),direct,inherit.directis the remux-first posture for host-local smart-TV testing when PMS often forwards no client hints to Tunerr.IPTV_TUNERR_PLEX_INTERNAL_FETCHER_POLICY— adaptation policy when the resolved client looks like PMS/Lavf/ffmpeg rather than the actual end client. Values:websafe(default),direct,inherit.IPTV_TUNERR_PLEX_RESOLVE_ERROR_POLICY— adaptation policy when PMS session lookup itself errors. Values:websafe(default),direct,inherit.IPTV_TUNERR_UPSTREAM_HEADERS— comma-separated extra headers applied to upstream playlist and segment requests, for exampleReferer,Origin, orHost.IPTV_TUNERR_UPSTREAM_ADD_SEC_FETCH— addSec-Fetch-Site: cross-siteandSec-Fetch-Mode: corson upstream requests and ffmpeg inputs.IPTV_TUNERR_UPSTREAM_USER_AGENT— override the upstreamUser-Agentwhile leaving downstream client detection untouched. Accepts preset names (lavf,vlc,mpv,kodi,firefox) or a literal UA string. When set to a preset, the resolved string matches the installed ffmpeg version (forlavf) or a canonical media-player/browser value.IPTV_TUNERR_COOKIE_JAR_FILE— persist upstream cookies learned during playback so provider/CDN clearance tokens (cf_clearance) survive restarts. Required for CF auto-boot persistence and forcf-status/import-cookiesto know where to read/write.IPTV_TUNERR_CF_REAL_BROWSER_FALLBACK— whentrue, CF auto-boot may open the user's real desktop browser (xdg-open/open) after headless Chromium fails. Defaultfalse, soIPTV_TUNERR_CF_AUTO_BOOT=truestays headless-only unless you explicitly opt in.IPTV_TUNERR_CF_LEARNED_FILE— optional explicit path for the per-host CF learned state file (cf-learned.json). When unset, Tunerr automatically derives the path next toIPTV_TUNERR_COOKIE_JAR_FILE. Stores: working UA found by cycling, CF-tagged flag, timestamp. Written atomically on every update. Read at startup to pre-populatelearnedUAByHostso UA cycling does not repeat after restarts.IPTV_TUNERR_HOST_UA— comma-separatedhost:presetpairs to pin a resolved upstream User-Agent per hostname at startup, without waiting for automatic cycling. Preset names:lavf/ffmpeg(auto-detected ffmpeg version),vlc,mpv,kodi,firefox, or any literal UA string. Example:IPTV_TUNERR_HOST_UA=provider.example.com:vlc,cdn2.example.com:lavf. Pre-populateslearnedUAByHost; does not prevent cycling from updating the value later if a CF block is observed.IPTV_TUNERR_STREAM_ATTEMPT_LOG— path to a JSONL file where each stream attempt is appended as a JSON record. Written asynchronously; does not block the stream path. The in-process ring buffer at/debug/stream-attempts.jsonresets on restart; this file persists across restarts for post-mortem analysis. Consumed byscripts/analyze-bundle.py. Example:IPTV_TUNERR_STREAM_ATTEMPT_LOG=/var/log/tunerr-attempts.jsonl.IPTV_TUNERR_AUTOPILOT_STATE_FILE— optional JSON file for remembered playback decisions keyed bydna_id + client_class; when enabled, successful stream choices can be reused on later requests before generic adaptation rules, including the last known-good upstream URL/host.IPTV_TUNERR_AUTOPILOT_CONSENSUS_HOST— whentrue/1/on, channels with no matching per-DNA Autopilot row (or stale URL memory) may prefer an upstream hostname that appears across multiple other channels’ rememberedpreferred_hostvalues (aggregate agreement). Requires at leastIPTV_TUNERR_AUTOPILOT_CONSENSUS_MIN_DNAdistinctdna_idrows (default 3) andIPTV_TUNERR_AUTOPILOT_CONSENSUS_MIN_HIT_SUMtotalhitsacross those rows for that host (default 15). Skips hosts with autotune penalty. Reported on/autopilot/report.jsonandintelligence.autopiloton/provider/profile.json.IPTV_TUNERR_AUTOPILOT_GLOBAL_PREFERRED_HOSTS— comma-separated upstream hostnames (nohttps://; match is case-insensitive on URL host). When set,reorderStreamURLsprefers the first catalogStreamURLsentry whose host matches any listed host after per-channel Autopilot memory (URL/host) and before consensus host + penalty sort. Does not requireIPTV_TUNERR_AUTOPILOT_STATE_FILE. Echoed on/autopilot/report.json(global_preferred_hosts),intelligence.autopilot, and/debug/runtime.json→tuner.autopilot_global_preferred_hosts.IPTV_TUNERR_AUTOPILOT_HOST_POLICY_FILE— optional JSON file for global host policy. Supported keys:global_preferred_hosts/preferred_hostsandglobal_blocked_hosts/blocked_hosts. Preferred hosts merge withIPTV_TUNERR_AUTOPILOT_GLOBAL_PREFERRED_HOSTS; blocked hosts are removed fromreorderStreamURLswhen at least one non-blocked backup remains. Surfaced on/autopilot/report.json(host_policy_file,global_blocked_hosts) and/debug/runtime.json→tuner.autopilot_host_policy_file.IPTV_TUNERR_AUTOPILOT_MAX_FAILURE_STREAK— maximum remembered failure streak before a stored Autopilot decision stops being reused automatically (default2)IPTV_TUNERR_HOT_START_ENABLED— enable hot-start tuning for favorite/high-hit channels (defaulttrue)IPTV_TUNERR_HOT_START_CHANNELS— comma-separated explicit favorites bychannel_id,dna_id,guide_number, or exactguide_nameIPTV_TUNERR_HOT_START_GROUP_TITLES— comma-separated substrings matched against each channel's M3Ugroup_title(case-insensitive substring). When any needle matches, hot-start applies with reasongroup_title. Emptygroup_titlenever matches. Precedence: explicitHOT_START_CHANNELSfirst, thenHOT_START_GROUP_TITLES, then AutopilotHOT_START_MIN_HITS./debug/runtime.json→tuner.hot_start_*echoes enabled / min-hits / this env (when set).IPTV_TUNERR_HOT_START_MIN_HITS— minimum remembered Autopilot hits before a channel becomes hot automatically (default3)IPTV_TUNERR_HOT_START_MIN_BYTES— lower startup-gate byte threshold for hot channels (default24576)IPTV_TUNERR_HOT_START_TIMEOUT_MS— lower startup-gate timeout for hot channels (default15000)IPTV_TUNERR_HOT_START_BOOTSTRAP_SECONDS— bootstrap burst duration for hot channels (default2.0)IPTV_TUNERR_HOT_START_PROGRAM_KEEPALIVE— enable PAT/PMT keepalive automatically for hot channels (defaulttrue)IPTV_TUNERR_FORCE_WEBSAFE— whentrue, always force the Plex-safe transcode path regardless of client. Default forced profile isplexsafeunlessIPTV_TUNERR_FORCE_WEBSAFE_PROFILEis set.IPTV_TUNERR_FORCE_WEBSAFE_PROFILE— built-in or named profile used whenIPTV_TUNERR_FORCE_WEBSAFE=true, and the default profile family for the Plex adaptationwebsafebranch when no client-class-specific override is set. Useful profiles includeplexsafehqfor full compatibility re-encode at higher quality,plexsafemaxfor the same TV-safe shape with more bitrate headroom,plexsafeaacfor experimental AAC-based A/B tests, orcopyvideomp3when you want to preserve source video while normalizing audio/subtitle behavior for Plex Live TV clients.IPTV_TUNERR_PLEX_WEB_CLIENT_PROFILE— optional built-in or named profile override for resolved Plex Web/browser clients when the adaptation logic chooses thewebsafebranch. Falls back toIPTV_TUNERR_FORCE_WEBSAFE_PROFILEwhen unset.IPTV_TUNERR_PLEX_NATIVE_CLIENT_PROFILE— optional built-in or named profile override for resolved native Plex clients if you deliberately put that lane onwebsafe. Falls back toIPTV_TUNERR_FORCE_WEBSAFE_PROFILEwhen unset.IPTV_TUNERR_PLEX_INTERNAL_FETCHER_PROFILE— optional built-in or named profile override for unresolved/resolved PMS internal fetchers (Lavf,PlexMediaServer) when the adaptation logic chooses thewebsafebranch. This is useful when browsers are happy withcopyvideomp3but TVs still need stricterplexsafehqorplexsafemax.plexsafeaacis available as an experimental AAC variant but should be A/B tested before replacing a known-good MP3-based lane.IPTV_TUNERR_STRIP_STREAM_HOSTS— comma-separated hostnames (e.g.cf.like-cdn.com,like-cdn.com) whose stream URLs are removed at catalog build time. Channels with only stripped hosts are dropped entirely so the tuner never attempts CF-blocked endpoints.IPTV_TUNERR_DEDUPE_BY_TVG_ID— whentrue/1/on(default), merge catalog rows that share the sametvg_idand matching normalized guide-name identity duringindex(including a post-merge pass after free sources + HDHR hardware lineup). This keeps intentionaltvg_idvariants like East/West orPlusvariants separate. Setfalse/0/offto disable (niche debugging).
These vars address the Plex dash_init_404 / "Failed to find consumer" race where Plex accepts a session but its DASH packager doesn't receive usable MPEG-TS bytes fast enough to initialize.
IPTV_TUNERR_WEBSAFE_BOOTSTRAP— whentrue, sends a short burst of TS bytes immediately on stream open to give Plex's packager a head start before the real stream arrives.IPTV_TUNERR_WEBSAFE_BOOTSTRAP_ALL— apply bootstrap to all stream types, not just HLS inputs.IPTV_TUNERR_WEBSAFE_BOOTSTRAP_SECONDS— duration of the bootstrap burst (default0.35).IPTV_TUNERR_WEBSAFE_STARTUP_MIN_BYTES— startup gate: minimum buffered bytes before releasing main ffmpeg TS to the client (default65536).IPTV_TUNERR_WEBSAFE_STARTUP_MAX_BYTES— max bytes of ffmpeg TS kept while scanning for a decodable start (default786432). WithIPTV_TUNERR_WEBSAFE_REQUIRE_GOOD_START, the scan uses a sliding window of this size (HR-001) so the gate does not flush at max size without H.264 IDR + AAC.IPTV_TUNERR_WEBSAFE_STARTUP_TIMEOUT_MS— max wait for prefetch to finish (default60000ms in gateway code).IPTV_TUNERR_WEBSAFE_REQUIRE_GOOD_START— whentrue(default) on transcode/WebSafe ffmpeg relay, require H.264 IDR (Annex B) or H.265/HEVC IRAP-family NAL (types 16–21, Annex B) plus AAC ADTS in the scanned TS (plusSTARTUP_MIN_BYTES) before releasing; still aborts on timeout per strict mode. Logs includerelease=min-bytes-idr-aac-readyor otherrelease=reasons on thestartup-gate buffered=line (fieldidr=means “video keyframe / IRAP detected”, not H.264-only).IPTV_TUNERR_WEBSAFE_STARTUP_MAX_FALLBACK_WITHOUT_IDR— whentrue, allow the legacy max-bytes release without IDR/AAC (loggedrelease=max-bytes-without-idr-fallback). Defaultfalse. Escape hatch for odd codecs; prefer largerSTARTUP_MAX_BYTESorREQUIRE_GOOD_START=falsefor experiments.IPTV_TUNERR_WEBSAFE_NULL_TS_KEEPALIVE— send null TS packets (PID0x1FFF) while the startup gate waits. Keeps the TCP connection alive but carries no program structure.IPTV_TUNERR_WEBSAFE_NULL_TS_KEEPALIVE_MS— interval between null TS bursts (default100ms).IPTV_TUNERR_WEBSAFE_NULL_TS_KEEPALIVE_PACKETS— null TS packets per burst (default1).IPTV_TUNERR_WEBSAFE_PROGRAM_KEEPALIVE— send real PAT+PMT packets while the startup gate waits. Stronger than null TS: delivers the program map (video+audio PIDs) so Plex's DASH packager can instantiate its consumer before the first IDR frame arrives. Use when null TS alone doesn't preventdash_init_404.IPTV_TUNERR_WEBSAFE_PROGRAM_KEEPALIVE_MS— interval between PAT+PMT bursts (default500ms).
See iptvtunerr-troubleshooting §6 for a recommended config profile.
The guide pipeline serves the most complete data available, merging three sources in priority order (highest wins per channel):
placeholder < external XMLTV < provider XMLTV (xmltv.php)
External gap-fills provider for any time windows the provider EPG doesn't cover. The cache is pre-warmed synchronously at startup so the first request is never cold. On fetch failure, stale data is served — no guide outage on transient errors.
For Plex DVR stability, recurring event-like programmes get deterministic identity metadata in emitted XMLTV without changing their visible title. Tunerr adds a date-specific sub-title, date, and episode-num system="iptvtunerr" to all Live: rows and generic sports event titles such as NBA Basketball. This prevents Plex from treating today's event as the same title-only recording target as an event recorded on a previous day.
Fetches EPG directly from your IPTV provider using existing credentials. No separate EPG source needed for Xtream providers.
IPTV_TUNERR_PROVIDER_EPG_ENABLED(true) — setfalseto disable provider EPG fetchIPTV_TUNERR_PROVIDER_EPG_TIMEOUT(90s) — fetch timeout; provider XMLTV can be large (10–50 MB)IPTV_TUNERR_PROVIDER_EPG_CACHE_TTL(10m) — how often to re-fetch; overridesXMLTV_CACHE_TTLwhen setIPTV_TUNERR_PROVIDER_EPG_DISK_CACHE— optional filesystem path to store the last downloaded providerxmltv.phpbody. When set, Tunerr persistsETag/Last-Modifiedin a sidecar*.meta.jsonand sends conditional request headers; HTTP 304 responses skip re-download and parse the cached file. Many Xtream panels do not emit validators — in that case behavior matches an uncached fetch (full download each refresh). Create the parent directory before use.IPTV_TUNERR_PROVIDER_EPG_INCREMENTAL(false) — whentrue, apply token rendering onIPTV_TUNERR_PROVIDER_EPG_URL_SUFFIXfrom SQLite horizon (GlobalMaxStopUnix)IPTV_TUNERR_PROVIDER_EPG_LOOKAHEAD_HOURS(72) — window end offset for incremental suffix token renderingIPTV_TUNERR_PROVIDER_EPG_BACKFILL_HOURS(6) — start offset before known max stop for incremental suffix token renderingIPTV_TUNERR_PROVIDER_SHORT_EPG_FALLBACK(false) — whentrue, useplayer_api.php?action=get_short_epgagainst the channel stream host (or provider base URL) to synthesize real programme blocks for channels whose merged provider/external/HDHR guide has fewer thanIPTV_TUNERR_PROVIDER_SHORT_EPG_MIN_PROGRAMMESreal rows, and also when providerxmltv.phpis unavailableIPTV_TUNERR_PROVIDER_SHORT_EPG_MIN_PROGRAMMES(2) — minimum real programme rows per channel before short-EPG gap-fill is skippedIPTV_TUNERR_PROVIDER_SHORT_EPG_LIMIT(6) — max short-EPG rows requested per channel when the fallback is enabledIPTV_TUNERR_PROVIDER_SHORT_EPG_CONCURRENCY(8) — concurrent short-EPG requests during fallback guide refreshIPTV_TUNERR_PROVIDER_SHORT_EPG_TIMEOUT(5s) — per-request timeout for short-EPG fallback calls- Suffix tokens:
{from_unix},{to_unix},{from_ymd},{to_ymd}(only meaningful when incremental is enabled and SQLite store has data)
IPTV_TUNERR_XMLTV_URL— external XMLTV source URL; fetched, filtered to your channels, remapped to guide numbersIPTV_TUNERR_REFIO_ALLOW_PRIVATE_HTTP— allow private/loopbackhttp(s)refs for external XMLTV / alias fetches. Default is off as a hardening measure; prefer filesystem paths for local files and enable this only when you intentionally fetch from localhost/LAN.IPTV_TUNERR_GUIDE_INPUT_ALLOWED_URLS— comma-separated extra exact remote XMLTV / alias URLs allowed beyond the configured provider, XMLTV, or HDHomeRun guide URLs. Use this only when those sources are intentional and stable.IPTV_TUNERR_GUIDE_INPUT_ROOTS— comma-separated safe root directories for local XMLTV / alias files. Relative paths resolve under the current working directory by default; absolute paths outside these roots are rejected.IPTV_TUNERR_XMLTV_ALIASES— optional file path orhttp(s)URL for alias overrides used in deterministic EPG repairIPTV_TUNERR_CATCHUP_GUIDE_POLICY— optionaloff|healthy|strict; applies guide-quality filtering to/guide/capsules.json,catchup-capsules, andcatchup-publishIPTV_TUNERR_GUIDE_POLICY— optionaloff|healthy|strict; applies guide-quality filtering to runtime lineups once guide health is cached.healthydrops channels with no real programmes, placeholder-only rows, or sparse real coverage below the provider short-EPG minimum.IPTV_TUNERR_CATCHUP_REPLAY_URL_TEMPLATE— optional source-backed replay URL template for capsules/publishing; when set, replay URLs are rendered with programme and channel tokens instead of falling back to live-channel launchersIPTV_TUNERR_RECORD_DEPRIORITIZE_HOSTS— comma-separated hostnames; forcatchup-daemon/catchup-recordwith-record-upstream-fallback, catalog capture fallbacks whose host matches (or is a subdomain of) these names are tried after other fallbacks (Tunerr/stream/<id>stays first)IPTV_TUNERR_XMLTV_MATCH_ENABLE— repair/assign channelTVGIDs from provider/external XMLTV channel metadata during catalog build (defaulttrue)IPTV_TUNERR_XMLTV_TIMEOUT— fetch timeout (default45s)IPTV_TUNERR_XMLTV_CACHE_TTL— refresh interval when provider EPG cache TTL is not set (default10m)IPTV_TUNERR_LIVE_EPG_ONLY— at catalog build, keep only channels withepg_linked/ atvg-id(drops unlinked rows before save). See lineup-epg-hygiene.IPTV_TUNERR_EPG_PRUNE_UNLINKED— exclude channels with no EPG match from emittedguide.xmlandlive.m3uby defaultIPTV_TUNERR_EPG_FORCE_LINEUP_MATCH— keep every lineup row represented inguide.xmleven whenIPTV_TUNERR_EPG_PRUNE_UNLINKED=1, using placeholder guide rows for unmatched channels so Plex can still map tuner channels to guide channelsIPTV_TUNERR_EPG_SQLITE_PATH— optional filesystem path to a SQLite file for durable EPG storage (merged guide sync after each refresh; schema v2 includesepg_meta). Empty = disabled. Rationale: ADR 0003.IPTV_TUNERR_EPG_SQLITE_RETAIN_PAST_HOURS— if> 0, after each sync delete SQLite programme rows whose end time is beforenow - N hours, then remove orphanepg_channelrows.0= keep the full merged snapshot in SQLite.IPTV_TUNERR_EPG_SQLITE_VACUUM— iftrue/1, run SQLiteVACUUMafter a retain-past prune that removed at least one row (optional; reclaims file space, may pause briefly on large DBs).IPTV_TUNERR_EPG_SQLITE_MAX_BYTES— optional post-sync cap on the SQLite file size (bytes); deletes programmes until the file fits (ended programmes first).IPTV_TUNERR_EPG_SQLITE_MAX_MBis a shorthand (mebibytes) ifMAX_BYTESis unset.IPTV_TUNERR_EPG_SQLITE_INCREMENTAL_UPSERT— use overlap-window upsert sync mode for merged XMLTV (does not truncate all programme/channel rows each refresh)IPTV_TUNERR_HDHR_LINEUP_URL— optionalhttp(s)://device/lineup.jsonmerged into the catalog oniptv-tunerr index(LP-002).IPTV_TUNERR_HDHR_LINEUP_ID_PREFIX(defaulthdhr) prefixes generatedchannel_ids.IPTV_TUNERR_PROVIDER_EPG_URL_SUFFIX— optional string appended to providerxmltv.phpas&…(e.g. panel-specific query params). Not part of stock Xtream; only use if your provider documents extra parameters.IPTV_TUNERR_HDHR_GUIDE_URL— optional http(s) URL to a physical HDHomeRun-styleguide.xml(e.g.http://192.168.1.50/guide.xml). Merged after provider + external gap-fill; see ADR 0004.IPTV_TUNERR_HDHR_GUIDE_TIMEOUT— fetch timeout for the HDHR guide URL (default90s).IPTV_TUNERR_HDHR_DISCOVER_BROADCASTS— optional comma-separated literal UDP targets for HDHR discovery (hdhr-scanwithout-addr, andDiscoverLAN). IPv4 entries are subnet broadcasts (e.g.192.168.1.255, default port 65001) orhost:port. IPv6 entries (e.g.[::1]:65001,fe80::1%eth0:65001, or::1:65001with a trailing port) use a separate UDP6 socket; link-local addresses should include a zone (%eth0) when required. Global IPv4255.255.255.255is always tried first. Hostnames are not resolved (same as IPv4-only behavior).- Tunerr HTTP:
GET /guide/epg-store.json— row counts,last_sync_utc,global_max_stop_unix,retain_past_hours,db_file_bytes,db_file_modified_utc,vacuum_after_prune; add?detail=1forchannel_max_stop_unix(incremental fetch horizon).
- Tunerr HTTP:
Applied to all sources (provider and external):
IPTV_TUNERR_XMLTV_PREFER_LANGS— preferred language codes for programme titles/descriptions (e.g.en,eng)IPTV_TUNERR_XMLTV_PREFER_LATIN— prefer Latin-script variants where availableIPTV_TUNERR_XMLTV_NON_LATIN_TITLE_FALLBACK— what to use when title text is non-Latin and no Latin variant exists (channel= use channel name)
IPTV_TUNERR_HDHR_NETWORK_MODEIPTV_TUNERR_HDHR_DEVICE_IDIPTV_TUNERR_HDHR_TUNER_COUNTIPTV_TUNERR_HDHR_FRIENDLY_NAMEIPTV_TUNERR_HDHR_SCAN_POSSIBLEIPTV_TUNERR_HDHR_MANUFACTURERIPTV_TUNERR_HDHR_MODEL_NUMBERIPTV_TUNERR_HDHR_FIRMWARE_NAMEIPTV_TUNERR_HDHR_FIRMWARE_VERSIONIPTV_TUNERR_HDHR_DEVICE_AUTH
Required:
IPTV_TUNERR_PMS_URLIPTV_TUNERR_PMS_TOKEN
Enable/tune:
IPTV_TUNERR_PLEX_SESSION_REAPERIPTV_TUNERR_PLEX_SESSION_REAPER_POLL_SIPTV_TUNERR_PLEX_SESSION_REAPER_IDLE_SIPTV_TUNERR_PLEX_SESSION_REAPER_RENEW_LEASE_SIPTV_TUNERR_PLEX_SESSION_REAPER_HARD_LEASE_SIPTV_TUNERR_PLEX_SESSION_REAPER_SSE
IPTV_TUNERR_LINEUP_DROP_MUSICIPTV_TUNERR_LINEUP_SHAPEIPTV_TUNERR_LINEUP_REGION_PROFILE
Typical use:
- broad feed HDHR lane capped to
479 - category DVR lanes use separate M3U inputs and no shaping
mount/ VODFS is Linux-onlyvod-webdavis cross-platform and provides the non-Linux VOD parity path- Core tuner paths (
run,serve,supervise) are cross-platform - HDHR network mode compiles on Linux/macOS/Windows; validate native Windows networking on a real Windows host (not
wine)
./scripts/verify./scripts/build-test-packages.sh./scripts/build-tester-release.sh./scripts/plex-hidden-grab-recover.sh --dry-run