Skip to content

Releases: PiratesIRC/Stream-Mapparr

1.26.1931038

Choose a tag to compare

@PiratesIRC PiratesIRC released this 12 Jul 11:00

Two releases worth of work land together here: the auto-match on M3U refresh feature (built earlier but never published) and a fix for a web UI hang that could follow a Match & Assign run.

Fixed

Dispatcharr web UI hangs with a 504 after running Match & Assign.

Symptoms: shortly after a Match and Assign run (including a dry run), the Dispatcharr web UI stops responding. The login page still loads, but logging in never completes, and nginx returns "504 Gateway Time-out". Only a container restart recovers it. Live streams keep playing normally the whole time, which makes it look like Dispatcharr is fine when the API is not.

Cause: Stream-Mapparr could decide a matching job was small enough to run inline inside the HTTP request. Dispatcharr runs uWSGI with gevent, so the matcher's CPU-bound loop never yields to the event loop, and running it in a request freezes the entire worker process, not just that one request. Every other request handled by that worker, including your login, is stuck behind it.

The size estimate that made the decision was wrong in two ways. It assumed 0.8 seconds per channel group, which measured about 3.4x too optimistic in the field (a 29 group job estimated at 23 seconds actually took 1 minute 18 seconds). And it sized the job from the previous run's cached channel set, so changing the selected group or category was invisible to it. That is why the hang tended to show up right after switching category.

Matching jobs now run in the background unless they are genuinely trivial, so they can no longer block the web UI. If the job size cannot be determined at all, it runs in the background too, rather than being run inline on a guess.

Note: a background run still keeps one worker busy for its duration. Moving matching off the uWSGI event loop entirely is planned as follow-up work.

Added (previously unreleased, from 1.26.1861801)

Auto-match after M3U refresh (opt-in). A new "Auto-match after M3U refresh" setting. When enabled, Stream-Mapparr runs Match and Assign automatically as soon as a Dispatcharr M3U refresh finishes, so newly added streams get assigned without waiting for the daily schedule. Requires a Profile to be selected, and requires Dispatcharr v0.27 or newer (the setting is simply inert on older versions). Refresh events for multiple M3U accounts are coalesced, so a "refresh all" does not kick off one match per account.

Housekeeping

All text files in the repository are now normalized to LF. Previous release zips shipped 13 files with Windows CRLF line endings. This was harmless on Dispatcharr's Linux host, but it is now corrected at the source.

Install

Update from the Dispatcharr Plugin Hub, or download Stream-Mapparr.zip below and install it manually.

1.26.1820605

Choose a tag to compare

@PiratesIRC PiratesIRC released this 03 Jul 10:22

Stream-Mapparr 1.26.1820605

Matching-robustness release. Bundles two fixes not yet in a public release.

Strip invisible zero-width characters before matching (bug-105 / #36)

Some IPTV providers pad stream names with invisible Unicode format characters (category Cf: ZERO WIDTH SPACE U+200B, ZWNJ/ZWJ, WORD JOINER U+2060, BOM U+FEFF, SOFT HYPHEN, bidi marks), often wrapped around a decorative block glyph (e.g. UK ␣▎␣BBC 1 FHD). The visible glyph was already stripped, but the surrounding zero-width chars were matched by neither a normal space nor the visible-symbol strip, so they survived normalization and silently tanked the whole provider's match rate — and pasting the character into Ignore Tags couldn't fix it.

normalize_name now removes all category-Cf characters up front (removed, not spaced, since they are zero-width), so UK ▎BBC 1 FHD matches BBC 1. Non-Latin names (Cyrillic/CJK/Arabic) are unaffected.

OTA common-word callsign false positives (bug-098)

OTA channels whose callsign is also a common English word (KING/WHO/WOLF/WAVE/WOOD/WEEK/WILL) no longer grab unrelated streams (24/7 KING OF THE HILL, DOCTOR WHO, TEEN WOLF). A candidate stream must now corroborate the station — a parenthesized (KING)/suffixed KING-TV form, or the FCC network affiliation / community-served city — and branded number forms (KING 5, WAVE 3, WOOD TV8) still resolve correctly.

Full test suite green (547 tests). See Stream-Mapparr/CHANGELOG.md for details.

Stream-Mapparr 1.26.1791529

Choose a tag to compare

@PiratesIRC PiratesIRC released this 29 Jun 00:57
6931f28

Shared matcher core migration

The drifting per-plugin fuzzy_matcher.py is retired. The pure matching primitives now live once in a shared FuzzyMatcherCore, vendored byte-identically into the inner folder as matching_core.py; FuzzyMatcher subclasses it and keeps only Stream-Mapparr's plugin-specific layer (zone routing, OTA callsign resolution, channel-DB loading). Stream-Mapparr was the migration canary.

  • Vendored core is sha256-pinned (scripts/core_manifest.json) and enforced by a parity gate (tests/test_core_parity.py) + golden gate (tests/test_matcher_golden.py) in CI; .gitattributes pins it to LF. matching_core.py ships with the code — the plugin won't load without it.
  • strip_bare_region is a class-attr opt-in (off here — preserves bug-066 behavior).
  • calculate_similarity uses a Python >= min_ratio gate (replacing rapidfuzz score_cutoff); no change to live match decisions.

Normalization (ported from Lineuparr PR #13)

  • Non-ASCII names no longer collapse to empty in process_string_for_matching (NFKD-fold, keep alphanumerics).
  • Unicode box-bar delimiters ( / ) recognized for prefix stripping.

Packaging built with git archive (bug-087-safe forward-slash paths), validated with scripts/validate_zip.py (matching_core.py included).

1.26.1761110

Choose a tag to compare

@PiratesIRC PiratesIRC released this 25 Jun 11:28

Re-release of 1.26.1751210 with corrected zip packaging (bug-087). The 1.26.1751210 release asset had backslash path separators (built via PowerShell Compress-Archive / .NET Framework), which broke install on Dispatcharr's Linux host with 'missing plugin.py or package init.py' — both for direct installs and via the marketplace (external-mode, which re-hosts this zip). No functional code change; version-only bump so the registry re-fetches a correctly-packaged zip.

1.26.1751210

Choose a tag to compare

@PiratesIRC PiratesIRC released this 24 Jun 13:39

Stream-Mapparr 1.26.1751210

Everything since the last release (1.26.1720023). Two independent QA passes and a /simplify quality pass. The scheduler fix was test-fired on a live container and confirmed.

Fixed

  • Background scheduler runs again (bug-065). It armed only from on_load(), which this Dispatcharr build never calls, so scheduled runs had silently stopped for days. It now bootstraps from Plugin.__init__(), serialized with a re-entrant lock.
  • Scheduled run no longer fires once per worker (bug-069). Each Dispatcharr worker armed a scheduler, so the job ran several times (five CSVs in one minute). A shared last-run file plus an fcntl.flock now make the claim atomic, so only one worker runs it. Live-verified: one trigger, one skip, one CSV.
  • Distinct channels no longer collapse (bug-066). Bare Pacific, Central, Mountain, and Atlantic were stripped as timezones, so "Comedy Central" became "Comedy" and "The Atlantic" became "The". They are kept now (bare East and West and parenthesized tags still strip).
  • Discord and Slack webhooks deliver again (bug-067, issue #32). Completion webhooks posted a body those platforms reject (HTTP 400). They now receive their native shape ({"content"} for Discord, {"text"} for Slack), and other endpoints are unchanged.

Added

  • Zone-aware East and West stream routing (bug-068). "Starz Encore" and "STARZ Encore (W)" now route West feeds to the West channel and East or generic feeds to the default channel, applied in Match and Assign, Sort, and Preview. Routing activates only when a zone sibling exists, so the common single-feed case is unchanged.
  • View Check Progress and View Last Results actions, plus live progress notifications (started, percent and ETA, completion), backed by on-disk state.

Notes

  • 243 regression tests.
  • Install through the Dispatcharr Plugin Hub, or import the attached zip, then restart Dispatcharr.

1.26.1720023

Choose a tag to compare

@PiratesIRC PiratesIRC released this 21 Jun 10:55

Type: Bugfix release — OTA (broadcast) callsign matching restored, and a data-loss guard for overwrite runs. Fixes bug-063 (user-reported: running Match & Assign against US: ABC/CBS/FOX/NBC left almost every channel with no streams).

Fixed

  • Overwrite no longer wipes streams on a zero-match run. In add_streams_to_channels, a channel that matched 0 streams while overwrite_streams=True previously had all its existing ChannelStream rows deleted and nothing assigned — so a run that matched nothing (wrong threshold, a database/callsign gap) silently emptied every channel. "Overwrite" now only replaces when there are actual replacement streams; an empty match set leaves existing streams untouched.

  • OTA affiliates match by callsign again. OTA detection in _match_streams_to_channel depended solely on a US_channels.json database lookup, but that database carries no broadcast/callsign entries — so affiliates like ABC - AL Montgomery (WNCF) fell through to strict fuzzy name matching and matched nothing. New _resolve_ota_callsign() resolves the callsign from the FCC station table first, then falls back to a parenthesized callsign in the Dispatcharr channel name.

Added

  • FCC station table (networks.json) is now loaded and used. Stream-Mapparr already shipped networks.json (1915 US OTA stations: callsign → network / city / state) but never loaded it. FuzzyMatcher._load_broadcast_stations() now indexes it into channel_lookup on every load, giving authoritative callsign validation. _build_us_callsign_database (used by Match US OTA Only) is repointed from the empty US_channels.json to networks.json, reviving that action. networks.json is now part of the deploy file set.

  • 19 regression tests in tests/test_ota_callsign_fallback.py.

v1.26.1650116

Choose a tag to compare

@PiratesIRC PiratesIRC released this 14 Jun 01:22

Type: Maintenance — plugin manifest cleanup (no runtime behavior change).

Internal

  • plugin.json settings manifest de-drifted. The static fields array had gone stale: it still listed the removed timezone setting and was missing custom_aliases plus the audio / throughput / webhook fields. plugin.py defines settings dynamically via the Plugin.fields property (the single source of truth, which Dispatcharr always uses when present), so the static array is now intentionally "fields": [] with an explanatory _fields_note — mirroring the Lineuparr convention so it can't drift again. No runtime or settings-form change; the published Plugin Hub entry now shows accurate metadata.

v1.26.1650009

Choose a tag to compare

@PiratesIRC PiratesIRC released this 14 Jun 00:49

Type: Feature + bugfix release — matcher robustness (stylized-Unicode / emoji / resolution-marker normalization), Phase-1 channel-name alias matching, Dispatcharr-sourced timezone, multi-source stream dedup, CSV source labeling, plus the project's first automated test suite & CI. Consolidates the work shipped across 1.26.16418241.26.1650009.

Matching & normalization

Three normalization passes were added to the top of FuzzyMatcher.normalize_name so stylized stream names match their plain channel names. Each was validated by an old-vs-new corpus diff over the real ~54k-name stream pool and all channel databases, asserting 0 harmful changes (no real ASCII or non-Latin name altered).

Stylized-Unicode decoration stripping (bug-048):

  • Streams decorate names with stylized-Unicode tier/format markers — superscripts (RK: WEATHERNATION ᴿᴬᵂ, … ⁶⁰ᶠᵖˢ, … ⱽᴵᴾ, … ³⁸⁴⁰ᴾ), Latin small-caps (… ꜰʜᴅ), and bullets (). The ASCII tag regexes couldn't see them, so RK: WEATHERNATION ᴿᴬᵂ never matched channel WeatherNation (0 matches).
  • Fix: normalize_name now drops whole tokens that are pure stylized decoration, then NFKD-canonicalizes the rest. Decoration is detected by Unicode character name (SUPERSCRIPT / SUBSCRIPT / SMALL CAPITAL / MODIFIER LETTER), not hard-coded code-point ranges — real markers fall outside the obvious blocks (small-cap H=U+029C, modifier V=U+2C7D). Collision-safe (ASCII Gold/VIP untouched) and non-Latin-safe (Arabic/Cyrillic/CJK preserved). Runs unconditionally; punctuation-glued ornaments (◉:, superscript HD/RAW) are handled too.

Emoji-as-letter substitution (bug-051):

  • Some streams use an emoji as a letter: beIN SP⚽RTS / Sp⚽rts (the soccer ball stands in for o = SPORTS, the beIN family, ~682 names). Previously the ball became a space (sp rts) and never matched sports.
  • Fix: normalize_name maps an emoji to the letter it replaces only when flanked by ASCII letters (SP⚽RTSSPORTS), and strips emoji used purely as decoration (, , standalone , and zero-width U+FE0F/ZWJ). Recovers the base beIN Sports feed when its streams are present in the selected sources.

Numeric resolution-marker stripping (bug-055):

  • Resolution tags the keyword quality patterns missed — 3840P, 2160P, 1080P/1080i, 720P — are now stripped via RESOLUTION_PATTERNS (\b\d{3,4}[pi]\b, gated by quality stripping, applied before the keyword patterns to avoid space-gluing). Requires the p/i glued to the digits, so bare numbers and single-digit channel numbers (Channel 4, Studio 1080) are untouched.

Matcher score depended on whether rapidfuzz was installed (bug-026):

  • FuzzyMatcher.calculate_similarity had two implementations — a rapidfuzz fast path returning 1 - distance / max(len), and a pure-Python fallback returning (len1 + len2 - distance) / (len1 + len2). They disagreed: at threshold 95, Fox Sports 1 vs Fox Sports 2 scored 0.917 (rapidfuzz) vs 0.958 (pure-Python), flipping the match decision.
  • Fix: the pure-Python branch (and its early-termination bounds) now use 1 - distance / max(len), matching rapidfuzz exactly. Production runs the rapidfuzz path, so live behavior is unchanged — only the no-rapidfuzz fallback was corrected. Enforced by an automated parity test.

Features

  • Phase-1 channel-name alias matching: an exact-normalized alias layer (FuzzyMatcher.alias_lookup + a built-in US alias table) force-includes known aliases into a channel's matches, independent of the fuzzy threshold. A new Custom Aliases setting accepts a JSON object of additional "channel": ["alias", …] mappings (merged with the built-ins; invalid entries are logged and skipped).
  • Timezone now follows Dispatcharr's global setting: the plugin's own Timezone dropdown was removed. Scheduled runs read the timezone from Dispatcharr (CoreSettings.get_system_time_zone()), validated via pytz, with a UTC fallback. One less setting to keep in sync.
  • CSV stream-source labeling: every stream name in a CSV export is now tagged with its M3U source (e.g. GO: CNN [streamq.tv-bk15]), so identical names from different providers are distinguishable in reports.
  • Multi-source stream dedup (GitHub #28 / #29): deduplication is keyed on (name, m3u_account), so the same channel name from different providers both survive (multi-source failover); only true same-source duplicates collapse. Runs after the quality sort, so the kept copy is the best one.
  • Norwegian (NO) channel database (GitHub #30).

Internal & tooling

  • First automated test suite (tests/, 176 passing): fuzzy_matcher matching/normalization (incl. the three new normalizers, with collision/non-Latin guards), plugin.py pure helpers (via a Django-stubbing conftest), channel-database schema validation, and version-sync. Cases are regression locks derived from the bug history — every fix above ships with one.
  • CI (.github/workflows/ci.yml): py_compile + version-sync + database validation + pytest on every push/PR, with a least-privilege permissions block and pytz installed for the timezone tests.
  • Pre-commit gate (.githooks/pre-commit, opt-in) and helper scripts (scripts/check_version_sync.py, scripts/validate_databases.py).
  • docs/DEVELOPMENT.md + design specs/plans under docs/.
  • Deploy process corrected: plugin.json mtime hot-reload proved unreliable in practice — always docker restart dispatcharr after copying, and copy every changed file (incl. fuzzy_matcher.py and new modules like aliases.py).

Notes

  • fuzzy_matcher.py bumped to v26.165.0009.
  • The three normalization fixes are matcher-shared; port guides for the sibling plugins (Channel-Maparr, EPG-Janitor, Lineuparr, Metadata-Trackarr) are kept as local MATCHER-NORMALIZATION-PORT.md references.

v1.26.1362122 — Audio priority sorting (#27) + profile-dropdown fix (#26)

Choose a tag to compare

@PiratesIRC PiratesIRC released this 16 May 21:31

Highlights

Feature — Audio priority sorting (fixes #27)

Two new opt-in settings rank streams by audio quality within the existing quality sort:

  • Audio Channels Priority — e.g. 7.1, 5.1, stereo, mono
  • Audio Codec Priority — e.g. eac3, ac3, aac, mp2

Comma-separated, most-preferred first, case-insensitive substring match. Applied after the video resolution/FPS tier, before the pixel/FPS tiebreaker; channel layout ranked before codec; unlisted/missing audio sorts last. Data comes from stream_stats (IPTV Checker) — no probing. Blank = disabled, so there is no behavior change on upgrade.

Bugfix — "Cannot load channels - validation failed" (fixes #26)

On Dispatcharr instances with zero Channel Profiles, the profile dropdown emitted a blank-value option that Dispatcharr's field serializer rejected, silently dropping the entire profile_name field — users could never select a profile. The placeholder now uses a non-blank sentinel, and the error message names the specific failed check instead of a generic string.

See CHANGELOG.md for full detail.

Upgrade

Update via the Dispatcharr Plugin Hub (or pull the latest plugin.py + plugin.json). No settings migration required.

1.26.1082140

Choose a tag to compare

@PiratesIRC PiratesIRC released this 18 Apr 21:49

What's new

Features

  • Zone-based channel variants — channels declaring "zones": ["East", "West"] in the DB expand to per-zone entries at load time. 33 major US cable networks (FX, FXX, USA, Syfy, Disney Channel, etc.) now have East/West feeds. Closes #25.
  • Country-restricted matching (opt-in) — only match streams from the same detected country. Covers all 11 shipped country DBs (US/UK/CA/AU/IN/DE/FR/NL/ES/MX/BR).
  • Webhook completion notifications — POST a JSON summary (action, counts, CSV filename, dry-run flag) to any HTTP(S) endpoint. Runs in a daemon thread (fire-and-forget).
  • Calver versioning1.MAJOR.DDDHHMM format; bump_version.py keeps plugin.json and plugin.py in sync.

Performance

  • bulk_create for all ORM write paths — ~100× faster stream assignment.
  • CSV export reuses cached match results from the main loop, eliminating redundant fuzzy-match + threshold-variant work.
  • ETA calibrated to actual rapidfuzz timing.
  • Hybrid sync/background dispatch: jobs estimated under 25s run synchronously so the Dispatcharr UI shows the real completion notification; larger jobs stay background with webhook/WebSocket signalling.

UX

  • Action buttons get proper button_variant, button_color, button_label, and confirm dialogs matching the iptv_checker pattern.
  • Completion notifications tightened to single-line plain text (fits the Mantine toast).

Fixes

  • Manual runs were incorrectly logged as Scheduled due to a positional-arg bug in the action dispatcher. Fixed.
  • Zone expansion dedupes case-insensitively and warns on malformed zones values.