Skip to content

Commit e78c4cc

Browse files
authored
[stream-mapparr] Bump version to 1.26.1720023 (#138)
1 parent 0b6a475 commit e78c4cc

5 files changed

Lines changed: 17435 additions & 41 deletions

File tree

plugins/stream-mapparr/CHANGELOG.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,46 @@ _Nothing yet._
66

77
---
88

9+
## v1.26.1720023 (June 21, 2026)
10+
11+
**Type**: Bugfix release — OTA (broadcast) callsign matching restored, and a
12+
data-loss guard for overwrite runs. Fixes bug-063 (user-reported: running
13+
Match & Assign against US: ABC/CBS/FOX/NBC left almost every channel with no
14+
streams).
15+
16+
### Fixed
17+
18+
- **Overwrite no longer wipes streams on a zero-match run.** In
19+
`add_streams_to_channels`, a channel that matched 0 streams while
20+
`overwrite_streams=True` previously had all its existing `ChannelStream`
21+
rows deleted and nothing assigned — so a run that matched nothing (wrong
22+
threshold, a database/callsign gap) silently emptied every channel.
23+
"Overwrite" now only replaces when there are actual replacement streams;
24+
an empty match set leaves existing streams untouched.
25+
26+
- **OTA affiliates match by callsign again.** OTA detection in
27+
`_match_streams_to_channel` depended solely on a `US_channels.json` database
28+
lookup, but that database carries no `broadcast`/`callsign` entries — so
29+
affiliates like `ABC - AL Montgomery (WNCF)` fell through to strict fuzzy
30+
name matching and matched nothing. New `_resolve_ota_callsign()` resolves the
31+
callsign from the FCC station table first, then falls back to a
32+
parenthesized callsign in the Dispatcharr channel name.
33+
34+
### Added
35+
36+
- **FCC station table (`networks.json`) is now loaded and used.** Stream-Mapparr
37+
already shipped `networks.json` (1915 US OTA stations: callsign → network /
38+
city / state) but never loaded it. `FuzzyMatcher._load_broadcast_stations()`
39+
(ported from Channel-Maparr) now indexes it into `channel_lookup` on every
40+
load, giving authoritative callsign validation. `_build_us_callsign_database`
41+
(used by `Match US OTA Only`) is repointed from the empty `US_channels.json`
42+
to `networks.json`, reviving that action. **`networks.json` is now part of the
43+
deploy file set** (it was missing from the container entirely).
44+
45+
- 19 regression tests in `tests/test_ota_callsign_fallback.py`.
46+
47+
---
48+
949
## v1.26.1650116 (June 13, 2026)
1050

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

plugins/stream-mapparr/fuzzy_matcher.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,54 @@ def _load_channel_databases(self):
341341
self.logger.error(f"Error loading {channel_file}: {e}")
342342

343343
self.logger.info(f"Total channels loaded: {total_broadcast} broadcast, {total_premium} premium")
344+
self._load_broadcast_stations()
344345
return True
345346

347+
def _load_broadcast_stations(self):
348+
"""Load the FCC station table (networks.json) into the OTA lookup.
349+
350+
The per-country *_channels.json databases ship only premium
351+
(National/Regional) entries — no ``broadcast`` type, no ``callsign``
352+
field — so OTA/callsign matching relies on this US station table:
353+
callsign -> {network_affiliation, community_served_city,
354+
community_served_state, ...}. Each station is appended to
355+
``broadcast_channels`` and indexed in ``channel_lookup`` by both its
356+
full callsign (``WEWS-TV``) and base callsign (``WEWS``) so a stream
357+
citing either form resolves. A missing file is non-fatal. Ported from
358+
Channel-Maparr to revive OTA matching after the US database lost its
359+
broadcast entries. bug-063.
360+
361+
Returns the number of stations loaded.
362+
"""
363+
stations_path = os.path.join(self.plugin_dir, "networks.json")
364+
if not os.path.exists(stations_path):
365+
self.logger.info("No networks.json present — OTA station table not loaded")
366+
return 0
367+
368+
try:
369+
with open(stations_path, 'r', encoding='utf-8') as f:
370+
stations = json.load(f)
371+
except Exception as e:
372+
self.logger.error(f"Error loading networks.json: {e}")
373+
return 0
374+
375+
loaded = 0
376+
for station in stations:
377+
callsign = (station.get('callsign') or '').strip().upper()
378+
if not callsign:
379+
continue
380+
self.broadcast_channels.append(station)
381+
# setdefault: keep the first (primary) station for a given key so a
382+
# later subchannel entry can't clobber the main affiliate.
383+
self.channel_lookup.setdefault(callsign, station)
384+
base_callsign = re.sub(r'-(?:TV|CD|LP|DT|LD)$', '', callsign)
385+
if base_callsign != callsign:
386+
self.channel_lookup.setdefault(base_callsign, station)
387+
loaded += 1
388+
389+
self.logger.info(f"Loaded {loaded} OTA broadcast stations from networks.json")
390+
return loaded
391+
346392
def reload_databases(self, country_codes=None):
347393
"""
348394
Reload channel databases with specific country codes.
@@ -429,6 +475,7 @@ def reload_databases(self, country_codes=None):
429475
self.logger.error(f"Error loading {channel_file}: {e}")
430476

431477
self.logger.info(f"Total channels loaded: {total_broadcast} broadcast, {total_premium} premium")
478+
self._load_broadcast_stations()
432479
return True
433480

434481
def extract_callsign(self, channel_name):
@@ -446,7 +493,15 @@ def extract_callsign(self, channel_name):
446493
callsign = paren_match.group(1).upper()
447494
if callsign not in ['WEST', 'EAST', 'KIDS', 'WOMEN', 'WILD', 'WORLD']:
448495
return callsign
449-
496+
497+
# Priority 1b: grandfathered 3-letter callsigns in parentheses without a suffix
498+
# (WWL/WJZ/KYW/WRC). Suffixed forms fall through to Priority 2. bug-062.
499+
paren3_match = re.search(r'\(([KW][A-Z]{2})\)', channel_name, re.IGNORECASE)
500+
if paren3_match:
501+
callsign = paren3_match.group(1).upper()
502+
if callsign not in ['WEST', 'EAST', 'KIDS', 'WOMEN', 'WILD', 'WORLD']:
503+
return callsign
504+
450505
# Priority 2: Callsigns with suffix in parentheses
451506
paren_suffix_match = re.search(r'\(([KW][A-Z]{2,4}-(?:TV|CD|LP|DT|LD))\)', channel_name, re.IGNORECASE)
452507
if paren_suffix_match:

0 commit comments

Comments
 (0)