From 9876a25e628453cd88195a0feb280ac4e99b84bc Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Sat, 7 Mar 2026 18:33:17 -0600 Subject: [PATCH] Bug Fix: XC stream refresh crashing with a `null value in column "name"` database error when a provider returns streams with a null or empty name. Affected streams are now assigned a generated fallback name in the format ` - ` so the refresh completes successfully and the stream remains accessible. A warning is logged for each affected stream. --- CHANGELOG.md | 1 + apps/m3u/tasks.py | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d78f782..ed606809 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- XC stream refresh crashing with a `null value in column "name"` database error when a provider returns streams with a null or empty name. Affected streams are now assigned a generated fallback name in the format ` - ` so the refresh completes successfully and the stream remains accessible. A warning is logged for each affected stream. - 504 Gateway Timeout when saving M3U group settings on slower hardware (e.g. Synology NAS). Replaced per-row `update_or_create()` loops with `bulk_create(update_conflicts=True)` wrapped in `transaction.atomic()` for both `ChannelGroupM3UAccount` and `M3UVODCategoryRelation`, reducing hundreds of individual DB round-trips to a single query per model. (Fixes #745) — Thanks [@nickgerrer](https://github.com/nickgerrer) - Improved frontend table stability during M3U imports: Fixed incorrect default `state` initialization (`[]` → `{}`) in `CustomTable` to match TanStack Table v8's expected state object shape. Added `autoResetPageIndex: false` and `autoResetExpanded: false` to prevent TanStack Table from issuing internal state resets on data updates. Memoized `processedData` in `M3UsTable` to avoid redundant sort/filter recomputation on re-renders. - Thanks [@marcinolek](https://github.com/marcinolek) - `debian_install.sh` hardened for non-UTF8 environments (common in minimal LXC containers) - Thanks [@marcinolek](https://github.com/marcinolek) diff --git a/apps/m3u/tasks.py b/apps/m3u/tasks.py index 162359e9..fc8dc756 100644 --- a/apps/m3u/tasks.py +++ b/apps/m3u/tasks.py @@ -751,6 +751,14 @@ def collect_xc_streams(account_id, enabled_groups): # Filter streams based on enabled categories filtered_count = 0 for stream in all_xc_streams: + # Fall back to a generated name if the provider returns null/empty + stream_name = stream.get("name") or f"{account.name} - {stream.get('stream_id', 'Unknown')}" + if not stream.get("name"): + logger.warning( + f"XC stream has null/empty name; using generated name '{stream_name}' " + f"(stream_id={stream.get('stream_id', 'unknown')})" + ) + # Get the category_id for this stream category_id = str(stream.get("category_id", "")) @@ -760,7 +768,7 @@ def collect_xc_streams(account_id, enabled_groups): # Convert XC stream to our standard format with all properties preserved stream_data = { - "name": stream["name"], + "name": stream_name, "url": xc_client.get_stream_url(stream["stream_id"]), "attributes": { "tvg-id": stream.get("epg_channel_id", ""), @@ -844,7 +852,12 @@ def process_xc_category_direct(account_id, batch, groups, hash_keys): ) for stream in streams: - name = stream["name"] + name = stream.get("name") or f"{account.name} - {stream.get('stream_id', 'Unknown')}" + if not stream.get("name"): + logger.warning( + f"XC stream has null/empty name in category {group_name}; " + f"using generated name '{name}' (stream_id={stream.get('stream_id', 'unknown')})" + ) raw_stream_id = stream.get("stream_id", "") provider_stream_id = None if raw_stream_id: