mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-28 12:36:42 +00:00
Delete all auto-synced channels and create new ones.
This commit is contained in:
parent
b91a2286e4
commit
12c086dcd1
2 changed files with 126 additions and 111 deletions
|
|
@ -868,42 +868,38 @@ def sync_auto_channels(account_id):
|
|||
|
||||
logger.info(f"Processing auto sync for group: {channel_group.name} (start: {start_number})")
|
||||
|
||||
# Get all streams in this group for this M3U account
|
||||
current_streams = Stream.objects.filter(
|
||||
m3u_account=account,
|
||||
channel_group=channel_group
|
||||
)
|
||||
|
||||
# Get existing auto-created channels in this group that were created by this M3U account
|
||||
# Step 1: Delete all existing auto-created channels for this account in this group
|
||||
existing_auto_channels = Channel.objects.filter(
|
||||
channel_group=channel_group,
|
||||
auto_created=True,
|
||||
auto_created_by=account
|
||||
).distinct()
|
||||
)
|
||||
|
||||
# Create a mapping of stream hashes to existing channels
|
||||
existing_channel_streams = {}
|
||||
for channel in existing_auto_channels:
|
||||
for stream in channel.streams.filter(m3u_account=account):
|
||||
existing_channel_streams[stream.stream_hash] = channel
|
||||
deleted_count = existing_auto_channels.count()
|
||||
if deleted_count > 0:
|
||||
logger.debug(f"Deleting {deleted_count} existing auto-created channels in group {channel_group.name}")
|
||||
existing_auto_channels.delete()
|
||||
channels_deleted += deleted_count
|
||||
|
||||
# Track which channels should exist (based on current streams)
|
||||
channels_to_keep = set()
|
||||
# Step 2: Get all current streams in this group for this M3U account
|
||||
current_streams = Stream.objects.filter(
|
||||
m3u_account=account,
|
||||
channel_group=channel_group
|
||||
).order_by('name') # Sort by name for consistent ordering
|
||||
|
||||
if not current_streams.exists():
|
||||
logger.debug(f"No streams found in group {channel_group.name}, skipping channel creation")
|
||||
continue
|
||||
|
||||
# Step 3: Create new channels for all streams
|
||||
current_channel_number = start_number
|
||||
|
||||
# Create channels for streams that don't have them
|
||||
for stream in current_streams.order_by('name'):
|
||||
if stream.stream_hash in existing_channel_streams:
|
||||
# Channel already exists for this stream
|
||||
channels_to_keep.add(existing_channel_streams[stream.stream_hash].id)
|
||||
continue
|
||||
|
||||
# Find next available channel number
|
||||
while Channel.objects.filter(channel_number=current_channel_number).exists():
|
||||
current_channel_number += 0.1
|
||||
|
||||
# Create new channel
|
||||
for stream in current_streams:
|
||||
try:
|
||||
# Find next available channel number
|
||||
while Channel.objects.filter(channel_number=current_channel_number).exists():
|
||||
current_channel_number += 0.1
|
||||
|
||||
# Parse custom properties for additional info
|
||||
stream_custom_props = json.loads(stream.custom_properties) if stream.custom_properties else {}
|
||||
|
||||
|
|
@ -946,7 +942,6 @@ def sync_auto_channels(account_id):
|
|||
channel.logo = logo
|
||||
channel.save(update_fields=['logo'])
|
||||
|
||||
channels_to_keep.add(channel.id)
|
||||
channels_created += 1
|
||||
current_channel_number += 1.0
|
||||
|
||||
|
|
@ -956,19 +951,6 @@ def sync_auto_channels(account_id):
|
|||
logger.error(f"Error creating auto channel for stream {stream.name}: {str(e)}")
|
||||
continue
|
||||
|
||||
# Delete channels that no longer have corresponding streams
|
||||
# Only delete auto-created channels that were created by this M3U account
|
||||
channels_to_delete = existing_auto_channels.exclude(id__in=channels_to_keep)
|
||||
|
||||
for channel in channels_to_delete:
|
||||
# Double-check that this is an auto-created channel by this account
|
||||
if channel.auto_created and channel.auto_created_by_id == account.id:
|
||||
logger.debug(f"Deleting auto-created channel: {channel.channel_number} - {channel.name}")
|
||||
channel.delete()
|
||||
channels_deleted += 1
|
||||
else:
|
||||
logger.warning(f"Skipping deletion of non-auto-created channel: {channel.channel_number} - {channel.name}")
|
||||
|
||||
logger.info(f"Auto channel sync complete for account {account.name}: {channels_created} created, {channels_deleted} deleted")
|
||||
return f"Auto sync: {channels_created} channels created, {channels_deleted} deleted"
|
||||
|
||||
|
|
@ -1230,6 +1212,16 @@ def refresh_single_m3u_account(account_id):
|
|||
# Now run cleanup
|
||||
streams_deleted = cleanup_streams(account_id, refresh_start_timestamp)
|
||||
|
||||
# Run auto channel sync after successful refresh
|
||||
auto_sync_message = ""
|
||||
try:
|
||||
sync_result = sync_auto_channels(account_id)
|
||||
logger.info(f"Auto channel sync result for account {account_id}: {sync_result}")
|
||||
if sync_result and "created" in sync_result:
|
||||
auto_sync_message = f" {sync_result}."
|
||||
except Exception as e:
|
||||
logger.error(f"Error running auto channel sync for account {account_id}: {str(e)}")
|
||||
|
||||
# Calculate elapsed time
|
||||
elapsed_time = time.time() - start_time
|
||||
|
||||
|
|
@ -1238,7 +1230,7 @@ def refresh_single_m3u_account(account_id):
|
|||
account.last_message = (
|
||||
f"Processing completed in {elapsed_time:.1f} seconds. "
|
||||
f"Streams: {streams_created} created, {streams_updated} updated, {streams_deleted} removed. "
|
||||
f"Total processed: {streams_processed}."
|
||||
f"Total processed: {streams_processed}.{auto_sync_message}"
|
||||
)
|
||||
account.updated_at = timezone.now()
|
||||
account.save(update_fields=['status', 'last_message', 'updated_at'])
|
||||
|
|
@ -1258,12 +1250,6 @@ def refresh_single_m3u_account(account_id):
|
|||
message=account.last_message
|
||||
)
|
||||
|
||||
# Run auto channel sync after successful refresh
|
||||
try:
|
||||
sync_result = sync_auto_channels(account_id)
|
||||
logger.info(f"Auto channel sync result for account {account_id}: {sync_result}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error running auto channel sync for account {account_id}: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing M3U for account {account_id}: {str(e)}")
|
||||
account.status = M3UAccount.Status.ERROR
|
||||
|
|
|
|||
|
|
@ -24,10 +24,12 @@ import {
|
|||
NumberInput,
|
||||
Divider,
|
||||
Alert,
|
||||
Box,
|
||||
} from '@mantine/core';
|
||||
import { Info } from 'lucide-react';
|
||||
import useChannelsStore from '../../store/channels';
|
||||
import { CircleCheck, CircleX } from 'lucide-react';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
|
||||
const M3UGroupFilter = ({ playlist = null, isOpen, onClose }) => {
|
||||
const channelGroups = useChannelsStore((s) => s.channelGroups);
|
||||
|
|
@ -80,11 +82,27 @@ const M3UGroupFilter = ({ playlist = null, isOpen, onClose }) => {
|
|||
const submit = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// Update group settings via new API endpoint
|
||||
// Update group settings via API endpoint
|
||||
await API.updateM3UGroupSettings(playlist.id, groupStates);
|
||||
|
||||
// Refresh the playlist
|
||||
API.refreshPlaylist(playlist.id);
|
||||
// Show notification about the refresh process
|
||||
notifications.show({
|
||||
title: 'Group Settings Updated',
|
||||
message: 'Settings saved. Starting M3U refresh to apply changes...',
|
||||
color: 'green',
|
||||
autoClose: 3000,
|
||||
});
|
||||
|
||||
// Refresh the playlist - this will handle channel sync automatically at the end
|
||||
await API.refreshPlaylist(playlist.id);
|
||||
|
||||
notifications.show({
|
||||
title: 'M3U Refresh Started',
|
||||
message: 'The M3U account is being refreshed. Channel sync will occur automatically after parsing completes.',
|
||||
color: 'blue',
|
||||
autoClose: 5000,
|
||||
});
|
||||
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('Error updating group settings:', error);
|
||||
|
|
@ -124,7 +142,8 @@ const M3UGroupFilter = ({ playlist = null, isOpen, onClose }) => {
|
|||
opened={isOpen}
|
||||
onClose={onClose}
|
||||
title="M3U Group Filter & Auto Channel Sync"
|
||||
size={1200}
|
||||
size={1000}
|
||||
styles={{ content: { '--mantine-color-body': '#27272A' } }}
|
||||
>
|
||||
<LoadingOverlay visible={isLoading} overlayBlur={2} />
|
||||
<Stack>
|
||||
|
|
@ -141,78 +160,88 @@ const M3UGroupFilter = ({ playlist = null, isOpen, onClose }) => {
|
|||
value={groupFilter}
|
||||
onChange={(event) => setGroupFilter(event.currentTarget.value)}
|
||||
style={{ flex: 1 }}
|
||||
size="xs"
|
||||
/>
|
||||
<Button variant="default" size="sm" onClick={selectAll}>
|
||||
<Button variant="default" size="xs" onClick={selectAll}>
|
||||
Select Visible
|
||||
</Button>
|
||||
<Button variant="default" size="sm" onClick={deselectAll}>
|
||||
<Button variant="default" size="xs" onClick={deselectAll}>
|
||||
Deselect Visible
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Divider label="Groups & Auto Sync Settings" labelPosition="center" />
|
||||
|
||||
<Stack spacing="xs" style={{ maxHeight: '60vh', overflowY: 'auto' }}>
|
||||
{groupStates
|
||||
.filter((group) =>
|
||||
group.name.toLowerCase().includes(groupFilter.toLowerCase())
|
||||
)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((group) => (
|
||||
<Group key={group.channel_group} spacing="md" style={{
|
||||
padding: '12px',
|
||||
border: '1px solid #e0e0e0',
|
||||
borderRadius: '8px',
|
||||
backgroundColor: group.enabled ? '#f8f9fa' : '#f5f5f5'
|
||||
}}>
|
||||
{/* Group Enable/Disable Button */}
|
||||
<Button
|
||||
color={group.enabled ? 'green' : 'gray'}
|
||||
variant="filled"
|
||||
onClick={() => toggleGroupEnabled(group.channel_group)}
|
||||
radius="md"
|
||||
size="sm"
|
||||
leftSection={
|
||||
group.enabled ? (
|
||||
<CircleCheck size={16} />
|
||||
) : (
|
||||
<CircleX size={16} />
|
||||
)
|
||||
}
|
||||
style={{ minWidth: '140px' }}
|
||||
>
|
||||
<Text size="sm" truncate style={{ maxWidth: '120px' }}>
|
||||
{group.name}
|
||||
</Text>
|
||||
</Button>
|
||||
<Box style={{ maxHeight: '50vh', overflowY: 'auto' }}>
|
||||
<SimpleGrid
|
||||
cols={{ base: 1, sm: 2, md: 3 }}
|
||||
spacing="xs"
|
||||
verticalSpacing="xs"
|
||||
>
|
||||
{groupStates
|
||||
.filter((group) =>
|
||||
group.name.toLowerCase().includes(groupFilter.toLowerCase())
|
||||
)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((group) => (
|
||||
<Group key={group.channel_group} spacing="xs" style={{
|
||||
padding: '8px',
|
||||
border: '1px solid #444',
|
||||
borderRadius: '8px',
|
||||
backgroundColor: group.enabled ? '#2A2A2E' : '#1E1E22',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'stretch'
|
||||
}}>
|
||||
{/* Group Enable/Disable Button */}
|
||||
<Button
|
||||
color={group.enabled ? 'green' : 'gray'}
|
||||
variant="filled"
|
||||
onClick={() => toggleGroupEnabled(group.channel_group)}
|
||||
radius="md"
|
||||
size="xs"
|
||||
leftSection={
|
||||
group.enabled ? (
|
||||
<CircleCheck size={14} />
|
||||
) : (
|
||||
<CircleX size={14} />
|
||||
)
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<Text size="xs" truncate>
|
||||
{group.name}
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
{/* Auto Sync Checkbox */}
|
||||
<Checkbox
|
||||
label="Auto Channel Sync"
|
||||
checked={group.auto_channel_sync && group.enabled}
|
||||
disabled={!group.enabled}
|
||||
onChange={() => toggleAutoSync(group.channel_group)}
|
||||
size="sm"
|
||||
/>
|
||||
{/* Auto Sync Controls */}
|
||||
<Stack spacing={4}>
|
||||
<Checkbox
|
||||
label="Auto Channel Sync"
|
||||
checked={group.auto_channel_sync && group.enabled}
|
||||
disabled={!group.enabled}
|
||||
onChange={() => toggleAutoSync(group.channel_group)}
|
||||
size="xs"
|
||||
/>
|
||||
|
||||
{/* Channel Start Number Input */}
|
||||
<NumberInput
|
||||
label="Start Channel #"
|
||||
value={group.auto_sync_channel_start}
|
||||
onChange={(value) => updateChannelStart(group.channel_group, value)}
|
||||
disabled={!group.enabled || !group.auto_channel_sync}
|
||||
min={1}
|
||||
step={1}
|
||||
size="sm"
|
||||
style={{ width: '120px' }}
|
||||
precision={1}
|
||||
/>
|
||||
</Group>
|
||||
))}
|
||||
</Stack>
|
||||
{group.auto_channel_sync && group.enabled && (
|
||||
<NumberInput
|
||||
label="Start Channel #"
|
||||
value={group.auto_sync_channel_start}
|
||||
onChange={(value) => updateChannelStart(group.channel_group, value)}
|
||||
min={1}
|
||||
step={1}
|
||||
size="xs"
|
||||
precision={1}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Group>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
|
||||
<Flex mih={50} gap="xs" justify="flex-end" align="flex-end">
|
||||
<Button variant="default" onClick={onClose}>
|
||||
<Button variant="default" onClick={onClose} size="xs">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue