diff --git a/apps/m3u/signals.py b/apps/m3u/signals.py
index 22e723e0..6e46a0ff 100644
--- a/apps/m3u/signals.py
+++ b/apps/m3u/signals.py
@@ -2,9 +2,9 @@
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from .models import M3UAccount
-from .tasks import refresh_m3u_groups
+from .tasks import refresh_single_m3u_account, refresh_m3u_groups
from django_celery_beat.models import PeriodicTask, IntervalSchedule
-import json, gc
+import json
@receiver(post_save, sender=M3UAccount)
def refresh_account_on_save(sender, instance, created, **kwargs):
@@ -14,11 +14,7 @@ def refresh_account_on_save(sender, instance, created, **kwargs):
if it is active or newly created.
"""
if created:
- extinf_data, groups = refresh_m3u_groups(instance.id)
-
- # Aggresive GC since we pulled in the whole file
- del extinf_data, groups
- gc.collect()
+ refresh_m3u_groups.delay(instance.id)
@receiver(post_save, sender=M3UAccount)
def create_or_update_refresh_task(sender, instance, **kwargs):
diff --git a/apps/m3u/tasks.py b/apps/m3u/tasks.py
index 59cf34b2..460bb181 100644
--- a/apps/m3u/tasks.py
+++ b/apps/m3u/tasks.py
@@ -273,7 +273,8 @@ def cleanup_streams(account_id):
logger.info(f"Cleanup complete")
-def refresh_m3u_groups(account_id, use_cache=False):
+@shared_task
+def refresh_m3u_groups(account_id, use_cache=False, full_refresh=False):
if not acquire_task_lock('refresh_m3u_account_groups', account_id):
return f"Task already running for account_id={account_id}.", None
@@ -311,6 +312,16 @@ def refresh_m3u_groups(account_id, use_cache=False):
release_task_lock('refresh_m3u_account_groups', account_id)
+ if not full_refresh:
+ channel_layer = get_channel_layer()
+ async_to_sync(channel_layer.group_send)(
+ 'updates',
+ {
+ 'type': 'update',
+ "data": {"success": True, "type": "m3u_group_refresh", "account": account_id}
+ }
+ )
+
return extinf_data, groups
@shared_task
@@ -346,7 +357,7 @@ def refresh_single_m3u_account(account_id):
if not extinf_data:
try:
- extinf_data, groups = refresh_m3u_groups(account_id)
+ extinf_data, groups = refresh_m3u_groups(account_id, full_refresh=True)
if not extinf_data or not groups:
release_task_lock('refresh_single_m3u_account', account_id)
return "Failed to update m3u account, task may already be running"
diff --git a/frontend/src/WebSocket.jsx b/frontend/src/WebSocket.jsx
index 616236cc..4fa08216 100644
--- a/frontend/src/WebSocket.jsx
+++ b/frontend/src/WebSocket.jsx
@@ -57,6 +57,17 @@ export const WebsocketProvider = ({ children }) => {
socket.onmessage = async (event) => {
event = JSON.parse(event.data);
switch (event.data.type) {
+ case 'm3u_group_refresh':
+ fetchChannelGroups();
+ fetchPlaylists();
+
+ notifications.show({
+ title: 'Group processing finished!',
+ message: 'Refresh M3U or filter out groups to pull in streams.',
+ color: 'green.5',
+ });
+ break;
+
case 'm3u_refresh':
if (event.data.success) {
fetchStreams();
diff --git a/frontend/src/components/forms/M3U.jsx b/frontend/src/components/forms/M3U.jsx
index f8c70698..f0feb28a 100644
--- a/frontend/src/components/forms/M3U.jsx
+++ b/frontend/src/components/forms/M3U.jsx
@@ -23,6 +23,7 @@ import {
import M3UGroupFilter from './M3UGroupFilter';
import useChannelsStore from '../../store/channels';
import usePlaylistsStore from '../../store/playlists';
+import { notifications } from '@mantine/notifications';
const M3U = ({ playlist = null, isOpen, onClose, playlistCreated = false }) => {
const theme = useMantineTheme();
@@ -71,10 +72,15 @@ const M3U = ({ playlist = null, isOpen, onClose, playlistCreated = false }) => {
uploaded_file: file,
});
- await fetchChannelGroups();
+ notifications.show({
+ title: 'Fetching M3U Groups',
+ message: 'Filter out groups or refresh M3U once complete.',
+ // color: 'green.5',
+ });
// Don't prompt for group filters, but keeping this here
// in case we want to revive it
+ newPlaylist = null;
}
resetForm();
diff --git a/frontend/src/components/tables/ChannelsTable.jsx b/frontend/src/components/tables/ChannelsTable.jsx
index c69cc0a8..599e7c4f 100644
--- a/frontend/src/components/tables/ChannelsTable.jsx
+++ b/frontend/src/components/tables/ChannelsTable.jsx
@@ -26,6 +26,7 @@ import {
ScanEye,
EllipsisVertical,
CircleEllipsis,
+ CopyMinus,
} from 'lucide-react';
import ghostImage from '../../images/ghost.svg';
import {
@@ -540,6 +541,9 @@ const ChannelsTable = ({}) => {
};
const deleteChannel = async (id) => {
+ if (channelsPageSelection.length > 0) {
+ return deleteChannels();
+ }
await API.deleteChannel(id);
};
@@ -858,8 +862,18 @@ const ChannelsTable = ({}) => {
variant="transparent"
color={theme.tailwind.red[6]}
onClick={() => deleteChannel(row.original.id)}
+ disabled={
+ channelsPageSelection.length > 0 &&
+ !channelsPageSelection
+ .map((row) => row.id)
+ .includes(row.original.id)
+ }
>
-
+ {channelsPageSelection.length === 0 ? (
+
+ ) : (
+
+ )}