From 983a6675c546991eda9a33c99de9cd2df7086da3 Mon Sep 17 00:00:00 2001 From: None <190783071+CodeBormen@users.noreply.github.com> Date: Sun, 3 May 2026 16:53:57 -0500 Subject: [PATCH] ui: scope regex preview to the M3U account - Scope regex previews to the M3U account being edited - Empty-state message extended from "No streams in this group yet." to "No streams in this group yet. Run an M3U refresh first to populate streams." --- apps/channels/api_views.py | 15 +++++++++++++++ frontend/src/api.js | 5 ++++- frontend/src/components/forms/LiveGroupFilter.jsx | 10 +++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/apps/channels/api_views.py b/apps/channels/api_views.py index 9c10e555..5eaeab35 100644 --- a/apps/channels/api_views.py +++ b/apps/channels/api_views.py @@ -311,6 +311,19 @@ class StreamViewSet(viewsets.ModelViewSet): status=status.HTTP_400_BAD_REQUEST, ) + # Group names are not unique across M3U accounts (two providers + # can both publish a "Sports" group). Scope to the calling + # account so the sample reflects only the user's edits. + m3u_account_id = request.query_params.get("m3u_account_id") + if m3u_account_id is not None: + try: + m3u_account_id = int(m3u_account_id) + except (TypeError, ValueError): + return Response( + {"detail": "m3u_account_id must be an integer"}, + status=status.HTTP_400_BAD_REQUEST, + ) + find_pat = request.query_params.get("find") or None replace_pat = request.query_params.get("replace") or "" match_pat = request.query_params.get("match") or None @@ -358,6 +371,8 @@ class StreamViewSet(viewsets.ModelViewSet): # separate COUNT lets the client surface scan_limit_hit when # the preview covers only a sample. base_qs = Stream.objects.filter(channel_group__name=group_name) + if m3u_account_id is not None: + base_qs = base_qs.filter(m3u_account_id=m3u_account_id) names_iter = base_qs.values_list("name", flat=True)[:SCAN_CAP] total_in_group = base_qs.count() diff --git a/frontend/src/api.js b/frontend/src/api.js index d6a15092..6690b07c 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -316,12 +316,15 @@ export default class API { // any combination can be supplied per call. static async getStreamsRegexPreview( channelGroupName, - { find, replace, match, exclude, limit = 10, signal } = {} + { find, replace, match, exclude, limit = 10, signal, m3uAccountId } = {} ) { try { const params = new URLSearchParams({ channel_group: channelGroupName, }); + if (m3uAccountId !== undefined && m3uAccountId !== null) { + params.set('m3u_account_id', String(m3uAccountId)); + } if (find) params.set('find', find); if (replace !== undefined && replace !== null) { params.set('replace', replace); diff --git a/frontend/src/components/forms/LiveGroupFilter.jsx b/frontend/src/components/forms/LiveGroupFilter.jsx index 4b4aa66e..b1e87da7 100644 --- a/frontend/src/components/forms/LiveGroupFilter.jsx +++ b/frontend/src/components/forms/LiveGroupFilter.jsx @@ -325,7 +325,10 @@ const LiveGroupFilter = ({ setRegexPreviewState((prev) => ({ ...prev, [groupId]: emptyState })); return; } - const cacheKey = `${groupId}|${find}|${replace}|${match}|${exclude}`; + // Account ID in the cache key so previews stay correct when the + // user switches between accounts that share a group name. + const accountId = playlist?.id ?? ''; + const cacheKey = `${accountId}|${groupId}|${find}|${replace}|${match}|${exclude}`; const cached = regexPreviewCacheRef.current[cacheKey]; if (cached) { cancelPending(); @@ -364,6 +367,7 @@ const LiveGroupFilter = ({ exclude: exclude || undefined, limit: 10, signal: controller.signal, + m3uAccountId: playlist?.id, }); } catch (e) { if (e?.name === 'AbortError') return; @@ -838,7 +842,7 @@ const LiveGroupFilter = ({ {result && !result.error && result.matches.length === 0 && ( {result.total_in_group === 0 - ? 'No streams in this group yet.' + ? 'No streams in this group yet. Run an M3U refresh first to populate streams.' : 'No streams matched this pattern.'} )} @@ -913,7 +917,7 @@ const LiveGroupFilter = ({ {result && !result.error && result.matches.length === 0 && ( {result.total_in_group === 0 - ? 'No streams in this group yet.' + ? 'No streams in this group yet. Run an M3U refresh first to populate streams.' : emptyText} )}