Add stale_stream_days field to M3UAccount model and update related logic

- Introduced stale_stream_days field to M3UAccount to specify the retention period for streams.
- Updated cleanup_streams task to remove streams not seen within the specified stale_stream_days.
- Enhanced M3U form to include stale_stream_days input for user configuration.
This commit is contained in:
SergeantPanda 2025-05-01 16:01:08 -05:00
parent 4cb2cb7b20
commit d26944a7a5
5 changed files with 52 additions and 7 deletions

View file

@ -0,0 +1,18 @@
# Generated by Django 5.1.6
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('m3u', '0007_remove_m3uaccount_uploaded_file_m3uaccount_file_path'),
]
operations = [
migrations.AddField(
model_name='m3uaccount',
name='stale_stream_days',
field=models.PositiveIntegerField(default=7, help_text='Number of days after which a stream will be removed if not seen in the M3U source.'),
),
]

View file

@ -74,6 +74,10 @@ class M3UAccount(models.Model):
refresh_task = models.ForeignKey(
PeriodicTask, on_delete=models.SET_NULL, null=True, blank=True
)
stale_stream_days = models.PositiveIntegerField(
default=7,
help_text="Number of days after which a stream will be removed if not seen in the M3U source."
)
def __str__(self):
return self.name

View file

@ -65,7 +65,7 @@ class M3UAccountSerializer(serializers.ModelSerializer):
model = M3UAccount
fields = [
'id', 'name', 'server_url', 'file_path', 'server_group',
'max_streams', 'is_active', 'created_at', 'updated_at', 'filters', 'user_agent', 'profiles', 'locked',
'max_streams', 'is_active', 'stale_stream_days', 'created_at', 'updated_at', 'filters', 'user_agent', 'profiles', 'locked',
'channel_groups', 'refresh_interval'
]

View file

@ -312,18 +312,31 @@ def cleanup_streams(account_id):
m3u_account__enabled=True,
).values_list('id', flat=True)
logger.info(f"Found {len(existing_groups)} active groups")
streams = Stream.objects.filter(m3u_account=account)
# Calculate cutoff date for stale streams
stale_cutoff = timezone.now() - timezone.timedelta(days=account.stale_stream_days)
logger.info(f"Removing streams not seen since {stale_cutoff}")
# Delete streams that are not in active groups
streams_to_delete = Stream.objects.filter(
m3u_account=account
).exclude(
channel_group__in=existing_groups # Exclude products having any of the excluded tags
channel_group__in=existing_groups
)
# Delete the filtered products
streams_to_delete.delete()
# Also delete streams that haven't been seen for longer than stale_stream_days
stale_streams = Stream.objects.filter(
m3u_account=account,
last_seen__lt=stale_cutoff
)
logger.info(f"Cleanup complete")
deleted_count = streams_to_delete.count()
stale_count = stale_streams.count()
streams_to_delete.delete()
stale_streams.delete()
logger.info(f"Cleanup complete: {deleted_count} streams removed due to group filter, {stale_count} removed as stale")
@shared_task
def refresh_m3u_groups(account_id, use_cache=False, full_refresh=False):

View file

@ -52,6 +52,7 @@ const M3U = ({ playlist = null, isOpen, onClose, playlistCreated = false }) => {
is_active: true,
max_streams: 0,
refresh_interval: 24,
stale_stream_days: 7,
},
validate: {
@ -65,10 +66,11 @@ const M3U = ({ playlist = null, isOpen, onClose, playlistCreated = false }) => {
if (playlist) {
form.setValues({
name: playlist.name,
server_url: playlist.server_url,
server_url: playlist.server_url || '',
max_streams: playlist.max_streams,
user_agent: playlist.user_agent ? `${playlist.user_agent}` : '0',
is_active: playlist.is_active,
stale_stream_days: playlist.stale_stream_days || 7,
refresh_interval: playlist.refresh_interval,
});
} else {
@ -202,6 +204,14 @@ const M3U = ({ playlist = null, isOpen, onClose, playlistCreated = false }) => {
key={form.key('refresh_interval')}
/>
<NumberInput
min={1}
max={365}
label="Stale Stream Retention (days)"
description="Streams not seen for this many days will be removed"
{...form.getInputProps('stale_stream_days')}
/>
<Checkbox
label="Is Active"
{...form.getInputProps('is_active', { type: 'checkbox' })}