github PiratesIRC/Dispatcharr-Channel-Maparr-Plugin 1.26.2141433
v1.26.2141433

4 hours ago

The previous published release was v1.26.1930617 on 12 July 2026. Five versions were built since then and never published, so this release carries all of them. Each is listed below in the order it was made, newest first.

Install by uploading Channel-Maparr.zip on Dispatcharr's Plugins page. Nothing in this release changes existing behaviour unless you switch on the new emailed reports, which are off by default.


This release adds emailed reports, delivered by the Newsflasharr plugin, as an HTML page and a
CSV built specifically for sending. It also documents the plugin properly for the first time: a
task-oriented user guide, a disclaimer, and a screenshot of what an emailed report looks like.
The three entries below cover the work in the order it was done.

Documentation

  • New user guide, task-oriented rather than reference: a first run, what Dry
    Run actually changes, how to scope which channels are touched, how broadcast station names are
    built, what to do when a channel will not match, and a full walkthrough for setting up emailed
    reports including the Newsflasharr routing rule that is otherwise invisible when missing.
  • The README gained a Documentation table, a Disclaimer, and a screenshot of an emailed report
    built from invented data.
  • Corrected a stale README entry: it documented a version cache file at
    /data/channel_mapparr_version_check.json. The self-update check was removed in v1.26.2071908
    and nothing writes that file now. Any copy on your installation is left over and can be deleted.
  • The Settings Reference, action list and File Locations sections now include the emailed-report
    settings, the Email Report Now action, and /data/channel_mapparr_reports/.

Sorting

The HTML report's table can now be sorted by clicking a column heading. Requested by the
operator. Click a heading to sort by it, click again to reverse, or move focus to it and press
Enter. An arrow on the heading shows which column is sorted and in which direction.

  • Numbers sort as numbers. Channel Number 10 comes after 2, not before it, which is what a
    plain text comparison would give. Text sorts ignoring letter case.
  • The comparison reads a data-v attribute carrying the same value the cell displays, so what is
    sorted is always what is shown.
  • The script is embedded in the page and requests nothing from anywhere. The report is opened
    from a file path or from a mail attachment, where an external request would not resolve and
    would disclose that the report had been opened.
  • Sorting is an addition, not a requirement. Every row is in the page markup, so a reader
    whose mail client strips scripts still sees the whole table and simply cannot reorder it. The
    page says so in its own footer. In practice sorting works when the attachment is saved and
    opened in a browser, which is the ordinary way to read an HTML attachment.

The sorting is tested by running it, not by looking at it. A test that only checks a script
tag is present would pass for months while proving nothing. tests/report_sort_harness.js builds
a minimal document model from the real rendered page and runs the shipped script in Node, then
checks the row order that comes out. It is skipped when Node is absent, so it is a local safety
net rather than a build gate, and tests/test_reports.py still covers the markup everywhere. The
harness carries its own control: a second test feeds it a script that does nothing and requires
it to fail.

Note on the request. This was asked for on the basis that Stream-Mapparr already does it.
Measured: Stream-Mapparr's report is not sortable, and Stream-Mapparr/tests/test_reports_render.py
actively asserts its report page contains no script element at all. This plugin now deliberately
differs from that sibling.

v1.26.2141418 (August 2, 2026)

The emailed report no longer sends a hyperlink that cannot be opened. Reported by the
operator on the first real delivery: the email arrived with
/data/channel_mapparr_reports/channel_mapparr_report_20260802_191252.html rendered as a
clickable link. That path exists only inside the Dispatcharr container, so clicking it from a
mail client does nothing.

The cause was a design decision made in the previous release. The report's path was passed as
the notification's url field, on the reasoning that it was a locator rather than a link.
Newsflasharr's email template renders url as a hyperlink, so that distinction did not survive
contact with a real mail client.

  • No url is sent now. Nothing goes in that field unless it is genuinely reachable from an
    inbox.
  • The same information is stated as plain text in the notification body instead: the attachment
    filename, then Kept in /data/channel_mapparr_reports inside the container. No mail client
    turns that into a link.
  • Two tests pin the behaviour, one asserting no url is sent and one asserting the body still
    names both the file and its directory. Both were checked by reintroducing the defect and
    confirming they fail.

Nothing else changed. The attachment itself, the report content and the redaction rules are
unaffected.

v1.26.2141319 (August 2, 2026)

Reports can now be emailed, using the Newsflasharr plugin as the delivery path.
Channel Maparr writes its CSV exports to /data/exports, and until now there was no way
to get one off the box.

  • Three new settings. Send notifications to Newsflasharr is the master toggle and is
    off by default, because a released plugin must not start writing into another
    plugin's queue the moment it is upgraded. Email A Report After chooses between
    never and every run that produces an export. Email Report Format chooses HTML,
    CSV, or both; the default is HTML alone, so one run sends one email.
  • One new action, Email Report Now, builds a report from the last processed channels
    and queues it. It refuses, in the persistent red area of the plugin card, when
    Newsflasharr is absent, disabled, missing email settings, missing a routing rule for
    this plugin, or when its collector is not running. It says queued, never sent:
    notify() returning true means durably written to Newsflasharr's queue, not delivered.
  • The emailed report is a new, purpose-built pair of files in
    /data/channel_mapparr_reports, an HTML page and a CSV. The exports in /data/exports
    are unchanged and are never emailed.

The emailed report cannot carry your provider's hostname, by construction.
Every CSV export opens with a settings header naming the configured M3U sources, which on
a real installation is the provider hostname, and Newsflasharr sends an attachment
verbatim and unredacted.

  • reports.py builds its model by copying a named allow list of columns out of the rows
    the actions already hold in memory. It never opens an export file, which is pinned by a
    test that walks the module's syntax tree. A column added to a CSV writer later therefore
    cannot start being emailed on its own.
  • The settings header is replaced by a fixed safe subset: plugin version, generation time
    in UTC, dry run state, match sensitivity, the country databases actually resolved on
    disk, and row counts.
  • Every free text cell is scrubbed of the M3U account names, case insensitively and
    longest match first, and of IPv4 and IPv6 addresses.
  • That scrub fails closed. If the M3U account lookup raises, no report is built at
    all, rather than one whose redaction was a silent no operation. These names are the
    primary redaction input here, not a backstop.

Which runs report, and why the list is shorter than it looks.

  • The channel rename preview reports. The category organization preview reports. A
    completed M3U import reports.
  • Organize by Category reports only in Dry Run, because a real run of it writes no
    export at all. This is stated in the setting's help text rather than left to surprise.
  • The M3U import dry run does not report, so one import produces one report rather
    than two.
  • An audit of the syntax tree pins this list, so a fourth export writer added later cannot
    quietly skip its report.

Size handling is a fixed row cap, not a trimming loop. An M3U import can carry
seventeen thousand rows, and three of the paths that build a report run inside the web
request, where a pure Python loop performs no input or output and so never yields. Under
the server's async model that would freeze the whole worker. The cap is applied once, and
both renderings carry a visible line saying how many rows of how many are shown and naming
the complete file.

The category organization export is now written atomically, through a temporary file
and a rename, like the other two export writers already were. A plain write left a
truncated file at the final path when it failed part way, with no temporary file to clean
up, and that truncated file was the one an operator would later believe was complete.

Validate Settings reports emailed-report problems, using the warning glyph so the
lines actually reach the operator: an unrecognised stored value in either new select, a
Newsflasharr configuration that could not deliver, and a failing M3U account lookup.

Newsflasharr must be configured on its own settings page for any of this to arrive.
A routing rule keyed on source channel-mapparr and event usage_report, sending to
email. Without it the queue write succeeds and the mail is delivered somewhere else and
without its attachment. Do not set Newsflasharr's report-absence expectation for this
plugin: that detector measures cadence, and this plugin has no schedule.

Repository hygiene, done in the same change. Plugin export files at the repository
root were untracked but not ignored, and each carried the provider hostname; the ignore
rules were widened and the files removed. .publish-audit.json was added and every one of
its deny rules was proved to fire against a planted test file. Developer machine paths
were removed from the tracked notes.

v1.26.2071908 (July 26, 2026)

Validate Settings reports only what needs acting on. It previously returned its
entire readout, so every failure parked a wall of mostly-OK lines permanently under the
settings form (Dispatcharr renders error persistently on the plugin card and message
as a transient toast).

  • A failure now returns the failing lines and nothing else, in error. No DB OK, no
    Dry Run: ON, no OK lines at all.
  • A clean run returns a short toast and leaves nothing behind:
    All settings validated successfully. If an exclusion is configured it adds what that
    exclusion actually resolved to, since confirming that is the reason it is reported here.
  • Warnings are not failures, so they ride in the success toast rather than creating a
    persistent card.
  • Severity is read from the glyph each report line is built with, now named by
    _VALIDATION_ERROR_GLYPH / _VALIDATION_WARNING_GLYPH, and a mismatch between a
    counter and its lines is logged rather than silently mis-reported.

Removed the plugin's self-update check. Plugin.fields is read on Dispatcharr's
per-request hot path, and it made a live call to GitHub's releases API (plus a /data
cache write) every time the settings page was rendered. Plugin settings therefore could
not display without outbound network access, and a slow or unreachable GitHub stalled the
request.

  • Removed the check itself, the three helpers (_get_latest_version,
    _should_check_for_updates, _save_version_check), the VERSION_CHECK_FILE constant
    and its /data cache, and both urllib imports from plugin.py.
  • The Plugin Version field stays, and now simply reports Installed: vX.Y.Z. Operators
    still see what is installed; the plugin no longer has an opinion about what is newest.
  • tests/test_plugin_contract.py::test_no_update_check_remains fails the build if any of
    that machinery returns to plugin.py.
  • Unrelated but worth knowing: logo_matcher.py still uses the network for the tv-logos
    file list. That is expected, and it runs inside an action rather than on the request path.

v1.26.2071409 (July 26, 2026)

New setting: "Channel Groups to Ignore" - process every group except the ones you name.
Requested by a user running Teamarr, which owns its own static channel group.

  • Comma-separated, supports * and ? wildcards, case-insensitive. Composes with
    "Channel Groups to Process" (include first, then subtract), so leaving that blank and
    ignoring one group gives "everything except that group".
  • Enforced everywhere it has to be, not just where it was easy: the five channel-fetch
    sites, the two actions that replay a persisted results file without fetching channels
    (Rename Channels, Tag Unknown Channels, and the dry-run preview - a stale results file
    produced before the exclusion was set is still filtered, not just a live query), and the
    two write directions that could otherwise create channels into an ignored group
    (Organize by Category's target groups, and Import M3U Streams' destination, which now
    refuses the whole run rather than skip the one target). That asymmetry (Organize skips and
    continues, Import refuses the run) is deliberate: Organize is one write per category target,
    Import is one run per destination group.
  • Does not apply to Import M3U Streams' stream matching or duplicate detection. Those
    read channel names in ignored groups to decide what already exists, but never write to
    them, so the exclusion has nothing to guard there.
  • Fail-closed on a typo. An entry matching no group refuses the run rather than
    degrading to "process everything" - silent damage to the channels you were protecting is
    the failure this setting exists to prevent. A stray comma reads as blank, not as an
    unmatched entry. Validate Settings reports the resolved exclusion.
  • Organize by Category now also organizes channels that are in no group. Previously they
    were silently skipped by the same if group_ids: gap that bug-044 fixed for the logo
    actions in the prior release. This is a real behaviour change for other installs; on this
    box it is inert (0 of 1440 channels are currently ungrouped).

Divergence from EPG-Janitor, deliberately: EPG-Janitor raises when both its group filters
are set, though its help text says the ignore list is applied after the include filter.
Channel-Maparr implements the help text as written. See the spec's follow-ups.

v1.26.2071035 (July 26, 2026)

Hardening release. No new settings. Fixes three defects found while designing the
ignore_groups feature (docs/superpowers/specs/2026-07-26-ignore-groups-design.md).

  • bug-044 — a typo in "Channel Groups to Process" applied logos to EVERY channel.
    _get_all_channels guarded with if group_ids:, so an empty set was indistinguishable
    from None and both meant "no filter". The two logo actions built their include set with
    no error path, so an unresolvable value silently widened the scope from the named groups to
    the whole database. Fixed in two layers: an empty set now filters to nothing (loudly), and
    both logo actions refuse an unresolvable include filter with a visible error.
  • Ungrouped channels are no longer at risk of being evicted when a scope is explicit.
    Channels with no channel group were included only because a blank filter passed None.
  • Every failure is now visible. Dispatcharr's plugin card renders error (persistent
    red) and message (a transient green toast) but never status, and this plugin set
    error on none of its ~30 failure returns — so every failure looked like success. An AST
    guard now enforces it.

Full history, including versions before this release: docs/CHANGELOG.md

Don't miss a new Dispatcharr-Channel-Maparr-Plugin release

NewReleases is sending notifications on new releases.