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}
)}