Skip to content

Latest commit

 

History

4,189 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Enhanced Channel Manager

A professional-grade web interface for managing IPTV configurations with Dispatcharr. Built with React + TypeScript and Python FastAPI.

ECM gives you full control over your IPTV setup: manage M3U accounts and EPG sources, create and organize channels with drag-and-drop, automate channel creation with a powerful rules engine, probe stream health, and monitor live streaming stats — all from a single interface.

Installation

Docker Compose (Recommended)

services:
  ecm:
    image: ghcr.io/motwakorb/enhancedchannelmanager:latest
    ports:
      - "6100:6100"   # HTTP (configurable via ECM_PORT)
      - "6143:6143"   # HTTPS (configurable via ECM_HTTPS_PORT)
    volumes:
      - ./config:/config
    environment:
      - PUID=1000
      - PGID=1000
      - ECM_PORT=6100
      - ECM_HTTPS_PORT=6143
    # The image ships a HEALTHCHECK with `start_period=120s` baked in
    # (bd-ul0ah). On long-running installs the first-run migrations can
    # run against a bloated SQLite WAL file and take >30s — Docker's
    # default start_period would mark the container unhealthy before
    # migrations finish. If you need to override, set the healthcheck
    # block here; operators on consistently fast installs can lower it.

That's it. Open http://localhost:6100 and the setup wizard will guide you through creating an admin account and connecting to Dispatcharr.

With MCP Server (Claude AI Integration)

To add the optional MCP server for managing ECM through Claude, add the MCP service to your compose file:

services:
  ecm:
    image: ghcr.io/motwakorb/enhancedchannelmanager:latest
    ports:
      - "6100:6100"
      - "6143:6143"
    volumes:
      - ./config:/config
      # ECM writes MCP credential material here. This is the ONLY ECM-owned
      # directory the AI-facing sidecar can see.
      - ecm-mcp-secrets:/run/secrets/ecm-mcp
    environment:
      - PUID=1000
      - PGID=1000
      - MCP_SECRETS_DIR=/run/secrets/ecm-mcp

  ecm-mcp:
    image: ghcr.io/motwakorb/enhancedchannelmanager-mcp:latest
    ports:
      - "127.0.0.1:6101:6101"
    volumes:
      # Credential projection only. Do NOT mount /config here: the sidecar is
      # the process most exposed to prompt injection, and a /config mount puts
      # settings.json, auth_settings.json, the audit journal, TLS private keys
      # and every stored backup one file read away from it.
      - ecm-mcp-secrets:/run/secrets/ecm-mcp:ro
    # Must match ECM's PUID/PGID above: the projection is owner-only (0600) and
    # the sidecar can read it only because it runs as the same account.
    user: "1000:1000"
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    tmpfs:
      - /tmp:rw,noexec,nosuid,nodev,size=16m,mode=1777
    pids_limit: 128
    mem_limit: 256m
    cpus: 1.0
    environment:
      - MCP_SECRETS_DIR=/run/secrets/ecm-mcp
      - ECM_URL=http://ecm:6100
      - MCP_PORT=6101
      # Container-internal bind; host publishing above remains loopback-only.
      - MCP_BIND_ADDRESS=0.0.0.0
    depends_on:
      ecm:
        condition: service_healthy

volumes:
  ecm-mcp-secrets:

Upgrading from an MCP sidecar that mounted ./config:/config:ro. Add the ecm-mcp-secrets volume and the MCP_SECRETS_DIR variable to both services as shown, then recreate. Recreating the containers alone is not enough. The sidecar images from v0.18.1 onward default MCP_SECRETS_DIR to /run/secrets/ecm-mcp, so without that volume the sidecar finds an empty directory and reports api_key_status: file_not_found permanently. Once the volume is in place, ECM publishes an existing key during startup or provisions one when upgrading settings that predate the field; no save or regeneration is needed to make the sidecar ready. An explicitly revoked key stays revoked.

Or if you're building from source, use the MCP compose overlay:

docker compose -f docker-compose.yml -f docker-compose.mcp.yml up -d

This default publishes MCP on host loopback only. For another machine, put an HTTPS reverse proxy in front of MCP and use the fail-closed remote overlay:

MCP_ALLOWED_HOSTS=mcp.example.home \
MCP_TRUSTED_PROXY_IPS=172.20.0.10 \
docker compose -f docker-compose.yml -f docker-compose.mcp.yml \
  -f docker-compose.mcp.remote.yml up -d

The proxy must terminate TLS and forward to port 6101. Do not expose port 6101 through a router or firewall; remote mode rejects non-HTTPS /mcp requests. ECM's own TLS setting does not protect MCP. It terminates HTTPS for ECM's web interface on port 6143 only. The MCP sidecar is a separate listener with no TLS of its own, so the reverse proxy is what encrypts MCP traffic. MCP_TRUSTED_PROXY_IPS accepts only explicit IP addresses or bounded CIDRs. Trust-all values (*, 0.0.0.0/0, and ::/0) and malformed entries stop the sidecar at startup. Forwarded HTTPS is honored only from a configured peer.

Reaching the MCP container from ECM — ECM's Settings > MCP Integration status badge probes the MCP server's /health endpoint. By default it targets ecm-mcp:6101, which Docker DNS resolves to the MCP container on the canonical compose network — no extra configuration needed. If you run both containers with network_mode: host (host network namespace shared), set MCP_HOST=localhost on the ECM service so the probe targets the host loopback instead of the (non-existent on that topology) ecm-mcp DNS name.

Allowed MCP hostnames: The MCP sidecar accepts localhost, loopback IPs, and the Compose service name ecm-mcp by default. The remote HTTPS profile requires the proxy-facing hostname in MCP_ALLOWED_HOSTS (comma-separated for more than one), then recreates the container. List hostnames/IPs only: no scheme, port, path, or wildcard. Requests carrying any other or malformed Host value are rejected before MCP routing.

Reaching ECM from the MCP container — the symmetric case. The MCP server calls ECM's backend API at its ECM_URL (default http://ecm:6100, Docker DNS on the canonical compose network). If both containers run with network_mode: host, the ecm service name has no DNS entry on the shared host network and the backend answers only on the host loopback, so every MCP tool call fails with All connection attempts failed. Fix: set ECM_URL=http://localhost:6100 on the MCP service. See MCP integration troubleshooting.

Upgrade note (v0.17.1-0066+): If you previously ran ECM and ecm-mcp with network_mode: host and never set MCP_HOST, you need to act. Earlier versions hardcoded localhost as the probe target; v0.17.1-0064 changed the default to ecm-mcp (the canonical compose service name). On a host-networking deploy, ecm-mcp does not resolve — so after pulling dev, your Settings > MCP Integration badge will show "MCP server not reachable" even when MCP is healthy. Fix: add MCP_HOST=localhost to the ECM service's environment block in your compose file and restart the container.

See MCP Server (Claude Integration) for setup instructions.

User / Group Identifiers:

  • PUID (default: 1000) — User ID the application runs as
  • PGID (default: 1000) — Group ID the application runs as

Set these to match the owner of your bind-mounted volumes to avoid permission issues. Find your IDs with id your_user.

Port Configuration:

  • ECM_PORT (default: 6100): HTTP interface. It remains available when TLS is enabled, but ECM refuses to start or refresh a browser session there (see below).
  • ECM_HTTPS_PORT (default: 6143) — HTTPS interface (when TLS is configured in Settings)

When ECM TLS is enabled, use the HTTPS address for the web UI. The HTTP port continues to serve health checks and unauthenticated recovery surfaces, but sign-in, Dispatcharr sign-in and token refresh all answer 403 there with a message naming the HTTPS address, rather than appearing to succeed and then failing on the next request. Session cookies are Secure, HttpOnly and SameSite=Lax.

Turning TLS on signs everyone out once. The moment ECM starts terminating TLS, every existing browser session is revoked, so a browser still holding a pre-activation cookie cannot keep replaying it. Everyone, including the administrator who made the change, signs in again over HTTPS. That moment is wherever it actually happens: switching TLS on when a certificate is already present, issuing or completing a Let's Encrypt certificate, uploading one manually, or a manual renewal that issues the first certificate. Switching TLS on before any certificate exists is not activation and signs nobody out, because you still need the UI to finish issuing.

Reverse proxy deployments should set the canonical public_base_url to an https:// origin. ECM's own policy code does not consult X-Forwarded-Proto, X-Forwarded-Host or Forwarded, because it has no trusted-proxy allowlist of its own. That setting is how a proxy declares its external scheme. (Note that uvicorn, the server ECM runs under, enables its own ProxyHeadersMiddleware by default and may rewrite the request scheme for clients within FORWARDED_ALLOW_IPS, which defaults to 127.0.0.1. That is uvicorn's policy, not ECM's.)

Emergency recovery when HTTPS is unreachable

There are two escape hatches. Both send session credentials in plaintext, so use them only on a trusted network and close them the moment HTTPS is repaired. Both work regardless of whether public_base_url is set.

  1. If you can still sign in over HTTPS, enable Emergency recovery: allow authenticated sessions over HTTP in Settings › TLS. While it is actually costing you protection (ECM's own TLS is on, or public_base_url is an https:// origin), the TLS panel shows a plaintext-session warning banner and ECM logs a warning. On a plain-HTTP install with neither, the hatch changes nothing and both stay quiet.
  2. If you cannot sign in at all, stop ECM, set ECM_ALLOW_HTTP_SESSION_COOKIES=true on the ECM container, and restart it. ECM logs a warning at startup for as long as it is set. Sign in through HTTP only long enough to repair or disable TLS, then remove the variable (or set it back to false) and restart ECM.

Values other than 1, true, yes or on (including the false that docker-compose.yml ships by default) leave the protection ON.

Your browser may refuse to open the HTTP port at all. Read this before you need it. Every HTTPS response from ECM carries Strict-Transport-Security: max-age=31536000 (one year, deliberately without includeSubDomains and without preload). Per RFC 6797 §8.3 that pin is scoped to the hostname and is port-agnostic: once you have visited https://ecm.example.com:6143 even once, your browser will silently upgrade http://ecm.example.com:6100 to https://ecm.example.com:6100 for a year, with no click-through to bypass it. So "browse to http://ecm.example.com:6100" is not a recovery instruction that works on a pinned browser. Use one of these instead:

  • Reach ECM by IP literal, e.g. http://192.168.1.50:6100. HSTS pins are stored per hostname and are never applied to IP addresses, so this always works and is the fastest route.
  • Use a different hostname for the same instance (another DNS name, or a hosts-file alias) that you have never visited over HTTPS.
  • Clear the pin for the hostname: in Chrome/Edge open chrome://net-internals/#hsts, enter the domain under Delete domain security policies, and delete it. In Firefox, open the History sidebar (or Library › History), right-click the site and choose Forget About This Site. Note that this also clears that site's cookies, cache and history. Then browse to the HTTP port.

Volumes:

  • /config — Persistent storage for database, settings, logos, TLS certificates, and backups

Backup & Restore

ECM supports full backup and restore of all configuration. You can create backups from Settings, or restore from a backup during the first-run setup wizard.

Development Setup

# Frontend
cd frontend && npm install && npm run dev

# Backend
cd backend && pip install -r requirements.txt && uvicorn main:app --reload

Features

Channel Management

Full CRUD for channels and groups with a split-pane layout. Drag-and-drop streams onto channels, reorder streams by priority, bulk-create channels from stream groups with smart name normalization (quality variants, country prefixes, timezone handling), and organize everything into numbered channel groups. Staged edit mode lets you queue changes locally and commit or discard them as a batch.

M3U Manager

Manage Standard M3U, XtreamCodes, and HD Homerun accounts. Link related accounts so group enable/disable changes cascade automatically. Track changes detected across M3U refreshes with filtering, search, and optional email digest notifications.

EPG Manager

Configure multiple XMLTV and Schedules Direct EPG sources with drag-and-drop priority ordering. Create dummy EPG entries for channels without guide data. Bulk EPG assignment uses country-aware matching, call sign scoring, and HD preference to automatically map EPG data to channels.

TV Guide

EPG grid view with now-playing highlights, date/time navigation, channel profile filtering, and click-to-edit channel metadata.

Channel Pipeline

A rules-based automation engine for channel creation, stream merging, and lifecycle management. Build complex conditions (stream name, group, quality, codec, normalized matching, etc.) with AND/OR logic, then define actions (create channel/group, merge streams, assign metadata, set variables, name transforms). Per-rule normalization group selection lets each rule apply specific normalization groups. Supports dry-run preview, execution rollback, YAML import/export, orphan reconciliation, and a diagnostic debug bundle for troubleshooting.

Stream Health & Probing

Automated stream probing with configurable schedules, batch sizes, retry logic, and rate limit detection. Profile-aware probing distributes connections across M3U profiles. Results drive smart stream sorting by resolution, bitrate, framerate, video codec, and M3U priority with configurable ordering for deprioritized stream categories. Black screen detection identifies streams showing dark/blank content, and low FPS detection flags streams below a configurable threshold (5/10/15/20 FPS). Both are deprioritized in Smart Sort. A strikeout system tracks consecutive failures for bulk cleanup.

Logo Manager

Browse, search, upload, and assign logos to channels. Supports URL import and file upload to Dispatcharr with usage tracking and pagination.

Stats & Monitoring

Live dashboard showing active channels, M3U connection counts, per-channel FFmpeg metrics (speed, FPS, bitrate), and bandwidth charts. Enhanced analytics include unique viewer tracking with Dispatcharr user identification, per-channel bandwidth, popularity scoring with trend analysis, and watch history with user attribution.

Journal

Activity log tracking all changes to channels, EPG, and M3U accounts with filtering by category, action type, and time range. A daily Journal Noise Purge task auto-deletes automated-noise entries (watch start/stop telemetry and automated Channel Pipeline rule create/delete churn) older than a configurable retention window (default 3 days); operator-initiated entries and all other categories are kept.

Settings

Comprehensive configuration including Dispatcharr connection, channel defaults, stream name normalization (tag-based and rule-based engines), stream probing, scheduled tasks (EPG/M3U refresh, probing, cleanup), alert methods (Discord, Telegram, email), authentication (local + Dispatcharr SSO), user management, TLS certificates, VLC integration, appearance themes, and backup/restore.

Authentication

First-run setup wizard, local auth with bcrypt hashing, Dispatcharr SSO, account linking, email-based password reset, and CLI password reset for lockout recovery. JWT-based sessions with automatic token refresh.

Notification Center

In-app notification bell with history, active task pinning, and external alert methods (Discord webhooks, Telegram bots, SMTP email) with digest batching and source filtering.

MCP Server (Claude Integration)

ECM includes an MCP (Model Context Protocol) server: an optional sidecar container that exposes ECM's functionality to Claude — Claude Desktop, Claude Code, or any MCP-capable client — so you can manage your install in plain language instead of clicking through the UI. 130 tools across 14 domains (channels, channel groups, streams, M3U accounts, EPG sources, channel pipeline, scheduled tasks, stats, system/backup, notifications, profiles, normalization, deduplication, Emby integration), plus an overview resource that gives Claude a one-shot snapshot of your install.

Everything Claude does runs against your live ECM through the API — it's the same operations the UI performs, just driven by conversation. Mutating actions report the resulting state back (e.g. the new channel's group and number) so you can confirm the change took effect.

What you can do with it

Things you can ask Claude to do:

Channels & streams

  • "List every channel in the Sports group that has fewer than 2 streams"
  • "Find duplicate channels and merge each set, keeping the one with the most streams"
  • "Add these 8 streams to channel 412 and put the 1080p ones first"
  • "Renumber the News group starting at 200"
  • "Build a channel lineup from this list of names and fuzzy-match streams to each"

Channel Pipeline

  • "Run the Channel Pipeline and tell me what it created"
  • "Analyze my Channel Pipeline rules and flag anything misconfigured" — the Rule Analyzer catches regex/structural mistakes (UK| that matches everything, ^4K typed under Contains that matches nothing, double-escape typos, OR-arms missing a group guard, merges into empty groups) without running the rule
  • "Create a rule that auto-creates channels for any stream whose name contains 'PPV', into the Events group"
  • "Why didn't the 4K Sports rule match anything?" — Claude can pull a debug bundle and analyze it
  • "Clear all the channels that the Channel Pipeline made in the Test group"

M3U & EPG

  • "Refresh all my M3U accounts and report any failures"
  • "Which channels are missing an EPG ID?" → "assign tvg_ids to those"
  • "Probe every stream in the Movies group and list the dead ones"

Housekeeping & insight

  • "Show me this week's watch stats" / "which channels has nobody watched in 30 days?"
  • "What scheduled tasks are enabled?" / "run the cleanup task"
  • "Back up my config" / "give me an overview of this ECM install"

Setup

Dispatcharr fields in settings.json (GH #273)

Field in settings.json What it is for
url Dispatcharr base URL
dispatcharr_api_key Dispatcharr REST API token — ECM uses this to talk to Dispatcharr. (Canonical field name as of v0.17.1, GH #273. Operators upgrading from v0.17.0 or earlier will have the value in the legacy api_key field; ECM auto-migrates on next startup with a one-time [CONFIG] Reading deprecated 'api_key' field … WARN log.) Never replace it with an MCP key.
api_key DEPRECATED legacy alias for dispatcharr_api_key. ECM still reads this for one release of back-compat (v0.17.x). The first read after upgrade emits a deprecation WARN and mirrors the value into dispatcharr_api_key on the next save.

MCP key authority (separate from Dispatcharr settings)

Generate, regenerate, or revoke the MCP client key only through Settings > MCP Integration. Copy the generated or regenerated key from the UI into each client. The owner-only /run/secrets/ecm-mcp/api-key projection is the sole live authority for inbound MCP authentication. The settings.json:mcp_api_key field is only a compatibility mirror; generic settings saves and manual changes cannot promote it over the authority and instead repair it from the validated projection. An explicitly empty authority is durable revocation.

The separate mcp-service.json projection contains the sidecar's private backend and confirmation credentials. Operators never copy those credentials into a client. .api-key.recovery is an internal redo record for interrupted rotation or revocation; do not edit or delete it as cleanup.

Do not put the MCP key in dispatcharr_api_key (or its legacy api_key alias): doing so breaks every channel and stream operation because Dispatcharr rejects the wrong credential. If /health reports api_key_configured: false, its status distinguishes an absent (file_not_found), blank (field_empty), or malformed/unreadable (invalid_key) projection. Read it with GET http://localhost:6101/health on the Docker host.

Migration example. A v0.17.0 settings.json from an operator hit by GH #273:

{
  "url": "http://dispatcharr:9191",
  "api_key": "REDACTED_DISPATCHARR_REST_TOKEN"
}

After the first v0.17.1 startup and the next settings save, the file becomes:

{
  "url": "http://dispatcharr:9191",
  "dispatcharr_api_key": "REDACTED_DISPATCHARR_REST_TOKEN",
  "api_key": "REDACTED_DISPATCHARR_REST_TOKEN"
}

Both fields hold the same Dispatcharr token. The duplicate is intentional so external scripts that still read api_key keep working. ECM handles the migration and compatibility writes; neither field is an MCP credential.

  1. Generate an API key in ECM Settings > MCP Integration and copy the displayed key for your MCP client. ECM publishes the authoritative projection and compatibility mirror automatically.
  2. Start the MCP container — add the ecm-mcp service to your compose file (see With MCP Server) and start it on port 6101
  3. Connect Claude — choose your method:

Choose your connection method

ECM's MCP server is authenticated with the public MCP client key in an Authorization: Bearer header. The default deployment is available only on the Docker host's loopback interface. Use the HTTPS remote profile above for access from another machine.

Method Node.js? Best for
Claude Desktop — mcp-remote bridge Yes (LTS 18+ on the Claude Desktop machine) Claude Desktop users; private/homelab deploys; existing setups
Claude Code — .mcp.json No Claude Code in any project; direct HTTP, no Node.js

Claude Desktop — mcp-remote bridge (Node required)

Works on a private network — no public exposure. mcp-remote runs on your machine and connects to ECM over your LAN/VPN, so ECM never has to be reachable from the internet (the cost is needing Node.js on the Claude Desktop machine).

Claude Desktop talks to remote MCP servers through the mcp-remote bridge. Add this to your claude_desktop_config.json:

Prerequisite: Claude Desktop does not bundle Node.js. The mcp-remote bridge is an npm package that Claude Desktop runs via npx, so you need Node.js installed on the same machine as Claude Desktop (any current LTS — Node 18+ — is fine). Install it from nodejs.org (or via a package manager: winget install OpenJS.NodeJS.LTS on Windows, brew install node on macOS, apt install nodejs npm on Debian/Ubuntu). Without Node on PATH, Claude Desktop fails to launch the MCP server with a spawn npx ENOENT error in its logs.

{
  "mcpServers": {
    "ecm": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://localhost:6101/mcp",
        "--header",
        "Authorization:${ECM_MCP_AUTH}",
        "--allow-http"
      ]
    }
  }
}

Set ECM_MCP_AUTH in the operating-system environment to Bearer <your key> before starting Claude Desktop. --allow-http is appropriate only for this loopback URL; use https:// and omit it for remote access.

Note: the Bearer value is the MCP key copied from Settings > MCP Integration, not the Dispatcharr REST token in dispatcharr_api_key or its legacy api_key alias.


Claude Code (.mcp.json)

Works on a private network — no Node.js, no public exposure. Claude Code speaks the HTTP transport natively and connects directly from your machine, so a LAN/VPN-reachable ECM is all you need. This is the simplest private path if you use Claude Code.

Create a .mcp.json file in any project directory where you want ECM tools available:

{
  "mcpServers": {
    "ecm": {
      "type": "http",
      "url": "http://localhost:6101/mcp",
      "headers": {
        "Authorization": "Bearer ${ECM_MCP_API_KEY}"
      }
    }
  }
}

To connect:

  1. Set ECM_MCP_API_KEY in your local environment and create the .mcp.json above
  2. Start Claude Code in that directory — it auto-detects .mcp.json on launch
  3. Run /mcp to reconnect if the MCP server restarts
  4. Ask Claude to manage your channels — e.g. "list my channels", "create a Channel Pipeline rule for sports", "probe all streams"

If running ECM locally, use localhost as your host. If the MCP container is on the same Docker network as Claude Code, use the container name (ecm-mcp).


Upgrading from an earlier version: remove ?api_key=... from every MCP URL and configure the Bearer header shown above. The deprecated SSE endpoints remain removed. Rotate the old key through Settings > MCP Integration after removing URL-based configs because URLs may have been retained in logs or histories.

Redeploying or rotating the MCP key: use Settings > MCP Integration > Regenerate Key, copy the returned key, then update the local environment variable used for the Bearer header. Rotation is effective on the next request without restarting the sidecar. Revoke access with Settings > MCP Integration > Revoke Key. Do not edit server-side credential projection files or dispatcharr_api_key (or its legacy api_key alias).

For the full reference — step-by-step connection setup, key rotation details, and troubleshooting — see docs/user_guide/integrations/mcp.md.

Available Tools (130)

Tool Description
Channels (20)
list_channels List channels with optional group/search/stream count filtering
get_channel Get detailed channel info (streams, EPG, logo)
create_channel Create a new channel
update_channel Update channel name, number, or group
delete_channel Delete a channel
bulk_delete_channels Delete multiple channels at once
add_stream_to_channel Add a stream to a channel
add_stream Create a channel from a stream name and assign it to a group, with deduplication control
remove_stream_from_channel Remove a stream from a channel
reorder_streams Reorder streams within a channel by priority
assign_channel_numbers Bulk-assign sequential channel numbers
merge_channels Merge multiple channels into one
find_duplicate_channels Scan for channels with matching normalized names
bulk_merge_duplicate_channels Merge multiple groups of duplicates at once
bulk_commit_channels Commit a batch of channel operations atomically
build_channel_lineup Bulk-create channels and fuzzy-match streams
clear_auto_created Remove auto-created channels by group
bulk_add_streams_to_channel Add multiple streams to a channel in one backend call (single Dispatcharr roundtrip)
bulk_assign_epg Assign EPG IDs (tvg_id) to multiple channels
set_logo_from_epg Set channel logos from their linked EPG entry's icon_url
Groups (8)
list_channel_groups List all groups with channel counts
create_channel_group Create a new group
get_orphaned_groups Find groups with no channels
delete_channel_group Delete a group, optionally deleting its channels
delete_orphaned_groups Delete groups with no channels assigned
get_hidden_groups List hidden channel groups
get_auto_created_groups List auto-created groups
get_groups_with_streams List groups with stream count info
Streams (18)
list_streams List streams with group/provider/search filtering
search_streams Search streams by name across all providers
get_streams_by_ids Fetch detailed info for specific stream IDs
get_streams_for_channel Get streams assigned to a channel
get_stream_health Stream health summary from last probe
probe_streams Start probing all streams (background)
probe_single_stream Probe one specific stream
probe_bulk_streams Probe multiple streams at once
get_probe_progress Check ongoing probe status
get_probe_results Results from the most recent probe
get_struck_out_streams List streams with consecutive failures
cleanup_struck_out_streams Remove struck-out streams from channels
bulk_remove_streams Remove multiple streams from a channel
cancel_probe Cancel a running probe
bulk_search_streams Search multiple stream names in one call
fuzzy_match_stream Find best fuzzy match for a stream name
preview_fuzzy_matches Preview scored stream→channel fuzzy matches without writing anything
match_streams_to_channels Match streams to channels by name similarity
M3U (9)
list_m3u_accounts List all M3U provider accounts
get_m3u_account Get detailed account info
create_m3u_account Create a new M3U account
update_m3u_account Update account name or URL
delete_m3u_account Delete an M3U account
refresh_m3u Refresh a specific M3U account
refresh_all_m3u Refresh all M3U accounts
update_m3u_group_settings Enable/disable stream groups on an account
bulk_update_m3u_group_settings Enable/disable multiple stream groups at once
EPG (15)
list_epg_sources List EPG data sources
create_epg_source Create a new EPG source
update_epg_source Update an EPG source
delete_epg_source Delete an EPG source
refresh_epg Refresh a specific EPG source
match_channels_epg Auto-match channels to EPG data
link_channel_epg Link a channel to a chosen EPG candidate so its guide data attaches
refresh_all_epg Refresh multiple or all EPG sources at once
get_epg_grid What's on TV now — EPG schedule grid
list_sd_lineups List the active Schedules Direct lineups for an SD EPG source
search_sd_lineups Search Schedules Direct headends/lineups by country + postal code
add_sd_lineup Add a Schedules Direct lineup to the account (rate-limited)
remove_sd_lineup Remove a Schedules Direct lineup from the account
list_dummy_epg_profiles List dummy EPG profiles
generate_dummy_epg Regenerate dummy EPG XMLTV data
Channel Pipeline (14)
Deprecated alias: every tool below is also callable under its old *_auto_creation_* name (e.g. run_auto_creation, list_auto_creation_rules). The alias forwards to the same handler and continues to work, but is hidden from new-tool listings — use the canonical channel_pipeline-named tools shown here.
list_channel_pipeline_rules List all rules
get_channel_pipeline_rule Get rule details (conditions, actions, normalization groups, sort config)
create_channel_pipeline_rule Create a rule with conditions, actions, and per-rule normalization groups
update_channel_pipeline_rule Update an existing rule (supports normalization_group_ids)
delete_channel_pipeline_rule Delete a rule
toggle_channel_pipeline_rule Enable/disable a rule
duplicate_channel_pipeline_rule Duplicate a rule
run_channel_pipeline Run pipeline (dry_run=true by default)
list_channel_pipeline_executions View execution history
rollback_channel_pipeline Undo an execution
restore_channel_pipeline_snapshot Full whole-run revert of a Channel Pipeline run from its pre-run snapshot
analyze_channel_pipeline_rules Lint and structurally analyze Channel Pipeline rules
get_channel_pipeline_debug_bundle Info about the diagnostic debug bundle for troubleshooting
bulk_toggle_channel_pipeline_rules Toggle multiple rules at once
Tasks (7)
list_tasks List scheduled tasks and status
run_task Run a task immediately
cancel_task Cancel a running task
get_task_history View task execution history
list_task_schedules List schedules for a task
create_task_schedule Create a schedule for a task (interval / daily / weekly / biweekly / monthly)
delete_task_schedule Delete a schedule
Stats (14)
get_channel_stats Channel viewing stats and active viewers
get_top_watched Most-watched channels by viewing time
get_bandwidth Bandwidth usage (today, week, month, all-time)
get_channel_bandwidth Per-channel bandwidth statistics
get_popularity_rankings Channel popularity scores and trending
get_channel_popularity Popularity score and metrics for a specific channel
get_trending Channels trending up or down in popularity
get_watch_history Watch history with user attribution and filters (channel, IP, days)
get_unique_viewers Unique viewer counts by channel
get_user_watch_time Per-user watch-time totals
get_user_channel_breakdown Per-channel watch-time breakdown for a specific user
get_provider_stats Per-provider statistics
get_activity Recent system activity (channel start/stop, buffering, client connections)
compute_stream_sort Compute optimal stream sort order (resolution, bitrate, video codec, etc.)
System (10)
get_settings ECM settings overview
create_backup Create config backup
restore_backup Restore ECM configuration from a saved backup ZIP on the server
create_dbas_backup Create a DBAS backup artifact (ecm-backup-.zip) on the server
restore_dbas_backup_saved Restore from a saved DBAS artifact on the server, by filename
get_export_sections List available YAML export sections
list_saved_backups List saved backup files
delete_saved_backup Delete a saved backup file
get_journal Activity audit log (with limit/category filters)
list_cloud_targets List configured cloud storage targets (DBAS backup upload destinations)
Notifications (5)
list_notifications List notifications with unread count
mark_notifications_read Mark all as read
delete_all_notifications Clear all notifications
list_alert_methods List configured alert methods (Discord, Telegram, email)
test_alert_method Send a test notification through an alert method. Not usable over MCP (build 0089): the endpoint refuses the MCP service key with 403, by design. An admin runs the test from Settings > Alert Methods
Profiles (3)
list_channel_profiles List channel profiles
list_stream_profiles List stream profiles
apply_profile_to_channels Bulk-assign a profile to channels
Normalization (3)
test_normalization Test how stream names normalize
list_normalization_rules List normalization rule groups
set_normalization_group_enabled Enable or disable a normalization rule group globally
Dedup (3)
list_pending_channel_merges List pending channel-merge candidates from the dedup queue
accept_channel_merge Accept a pending channel merge — triggers the Dispatcharr merge
dismiss_channel_merge Dismiss a pending channel merge — rejects the dedup candidate
Emby (1)
clear_emby_logos Clear cached Emby channel logos so Emby re-fetches fresh ones

Three read-only MCP resources provide quick context without a tool call: ecm://stats/overview, ecm://channels/summary, and ecm://tasks/status.

CLI Utilities

Password Reset

# Interactive mode (lists users, prompts for password)
docker exec -it enhancedchannelmanager python /app/reset_password.py

# Non-interactive
docker exec enhancedchannelmanager python /app/reset_password.py -u admin -p 'NewPass123'

# Skip password strength validation
docker exec enhancedchannelmanager python /app/reset_password.py -u admin -p 'simple' --force

Search Streams

./scripts/search-stream.sh http://dispatcharr:9191 admin password "ESPN"

Technical Stack

Layer Technology
Frontend React 18, TypeScript, Vite, @dnd-kit
Backend Python, FastAPI, 20+ modular API routers
MCP Server Python, FastMCP, Streamable HTTP transport, 130 tools
Deployment Docker Compose, two containers (ECM + MCP)

API Reference

Interactive API docs are available at /api/docs (Swagger UI) and /api/redoc. See docs/api.md for the full endpoint reference.

Roadmap

Completed

  • v0.17.2 — Full Stats v2 MCP coverage (8 new tools: provider stats, per-user watch time, trending, activity, channel bandwidth; media-server attribution now queryable via Claude); 30+ MCP correctness fixes from a live sweep of all tools; MCP API-key timing-attack hardening
  • v0.17.1 — Plex + Jellyfin user attribution and multi-viewer display; real client IP threading through the attribution pipeline; SSRF hardening on media-server test-connection endpoints
  • v0.17.0 — Stats v2 foundation: session_telemetry table, per-user watch-time API, Alembic schema migration system, Prometheus /metrics, structured JSON logging with trace IDs; ghost-source-channel fix after merge in edit mode
  • v0.16.0 — MCP server for natural language channel management via Claude (124 tools, 14 domains, Streamable HTTP transport); auto-creation rule analyzer; Alembic migrations; structured logging; per-rule normalization and sort options (an earlier 0.16.0 build was rolled back on 2026-04-20 before any external consumer pulled it; this is the shipping release, cut 2026-05-12)
  • v0.15.1 — OWASP hardening (security headers, CORS, rate limiting, NIST password policy, log redaction, path validation)
  • v0.15.0 — Server-side EPG matching, stream normalization, PUID/PGID support, low FPS detection, export/publish pipeline
  • v0.14.0 — Dummy EPG profiles, auto-creation pipeline, normalization engine
  • v0.13.0 — Backend modularization (20+ routers), auth system, task engine

v0.17.3 — Next release

See CHANGELOG.md [Unreleased] for the canonical list of fixes and features queued for the next cut.

About

An enhanced channel manager for Dispatcharr

Resources

Contributing

Security policy

Stars

80 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages