Releases: PiratesIRC/Stream-Mapparr
Release list
1.26.1931038
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
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
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;.gitattributespins it to LF.matching_core.pyships with the code — the plugin won't load without it. strip_bare_regionis a class-attr opt-in (off here — preserves bug-066 behavior).calculate_similarityuses a Python>= min_ratiogate (replacing rapidfuzzscore_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
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
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 fromPlugin.__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.flocknow 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, andAtlanticwere 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
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 whileoverwrite_streams=Truepreviously had all its existingChannelStreamrows 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_channeldepended solely on aUS_channels.jsondatabase lookup, but that database carries nobroadcast/callsignentries — so affiliates likeABC - 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 shippednetworks.json(1915 US OTA stations: callsign → network / city / state) but never loaded it.FuzzyMatcher._load_broadcast_stations()now indexes it intochannel_lookupon every load, giving authoritative callsign validation._build_us_callsign_database(used byMatch US OTA Only) is repointed from the emptyUS_channels.jsontonetworks.json, reviving that action.networks.jsonis now part of the deploy file set. -
19 regression tests in
tests/test_ota_callsign_fallback.py.
v1.26.1650116
Type: Maintenance — plugin manifest cleanup (no runtime behavior change).
Internal
plugin.jsonsettings manifest de-drifted. The staticfieldsarray had gone stale: it still listed the removedtimezonesetting and was missingcustom_aliasesplus the audio / throughput / webhook fields.plugin.pydefines settings dynamically via thePlugin.fieldsproperty (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
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.1641824–1.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, soRK: WEATHERNATION ᴿᴬᵂnever matched channel WeatherNation (0 matches). - Fix:
normalize_namenow 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-capH=U+029C, modifierV=U+2C7D). Collision-safe (ASCIIGold/VIPuntouched) and non-Latin-safe (Arabic/Cyrillic/CJK preserved). Runs unconditionally; punctuation-glued ornaments (◉:, superscriptHD/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 foro= SPORTS, the beIN family, ~682 names). Previously the ball became a space (sp rts) and never matchedsports. - Fix:
normalize_namemaps an emoji to the letter it replaces only when flanked by ASCII letters (SP⚽RTS→SPORTS), and strips emoji used purely as decoration (♬,☾, standalone⚽, and zero-widthU+FE0F/ZWJ). Recovers the basebeIN Sportsfeed 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 viaRESOLUTION_PATTERNS(\b\d{3,4}[pi]\b, gated by quality stripping, applied before the keyword patterns to avoid space-gluing). Requires thep/iglued 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_similarityhad two implementations — a rapidfuzz fast path returning1 - distance / max(len), and a pure-Python fallback returning(len1 + len2 - distance) / (len1 + len2). They disagreed: at threshold 95,Fox Sports 1vsFox Sports 2scored 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 viapytz, with aUTCfallback. 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_matchermatching/normalization (incl. the three new normalizers, with collision/non-Latin guards),plugin.pypure 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-privilegepermissionsblock andpytzinstalled 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 underdocs/.- Deploy process corrected:
plugin.jsonmtime hot-reload proved unreliable in practice — alwaysdocker restart dispatcharrafter copying, and copy every changed file (incl.fuzzy_matcher.pyand new modules likealiases.py).
Notes
fuzzy_matcher.pybumped 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.mdreferences.
v1.26.1362122 — Audio priority sorting (#27) + profile-dropdown fix (#26)
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
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 versioning —
1.MAJOR.DDDHHMMformat;bump_version.pykeepsplugin.jsonandplugin.pyin sync.
Performance
bulk_createfor 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 theiptv_checkerpattern. - Completion notifications tightened to single-line plain text (fits the Mantine toast).
Fixes
- Manual runs were incorrectly logged as
Scheduleddue to a positional-arg bug in the action dispatcher. Fixed. - Zone expansion dedupes case-insensitively and warns on malformed
zonesvalues.