From b91a2286e4f88cb6063b0096fafda9ab953495b4 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Sun, 13 Jul 2025 16:32:52 -0500 Subject: [PATCH] Add auto-created tracking to Channel model and related serializers - Introduced `auto_created` and `auto_created_by` fields in the Channel model to track channels created via M3U auto channel sync. - Updated ChannelSerializer to include these new fields and added a method to retrieve the name of the M3U account that created the channel. - Modified sync_auto_channels task to set these fields when creating channels and to filter existing channels accordingly. --- ...el_auto_created_channel_auto_created_by.py | 25 ++++++++++++++++++ apps/channels/models.py | 13 ++++++++++ apps/channels/serializers.py | 11 ++++++++ apps/m3u/tasks.py | 26 +++++++++---------- 4 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 apps/channels/migrations/0023_channel_auto_created_channel_auto_created_by.py diff --git a/apps/channels/migrations/0023_channel_auto_created_channel_auto_created_by.py b/apps/channels/migrations/0023_channel_auto_created_channel_auto_created_by.py new file mode 100644 index 00000000..603ec6dd --- /dev/null +++ b/apps/channels/migrations/0023_channel_auto_created_channel_auto_created_by.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.6 on 2025-01-XX XX:XX + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dispatcharr_channels', '0022_channelgroupm3uaccount_auto_channel_sync_and_more'), + ('m3u', '0003_create_custom_account'), + ] + + operations = [ + migrations.AddField( + model_name='channel', + name='auto_created', + field=models.BooleanField(default=False, help_text='Whether this channel was automatically created via M3U auto channel sync'), + ), + migrations.AddField( + model_name='channel', + name='auto_created_by', + field=models.ForeignKey(blank=True, help_text='The M3U account that auto-created this channel', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='auto_created_channels', to='m3u.m3uaccount'), + ), + ] diff --git a/apps/channels/models.py b/apps/channels/models.py index b6333aab..f53a9875 100644 --- a/apps/channels/models.py +++ b/apps/channels/models.py @@ -277,6 +277,19 @@ class Channel(models.Model): user_level = models.IntegerField(default=0) + auto_created = models.BooleanField( + default=False, + help_text="Whether this channel was automatically created via M3U auto channel sync" + ) + auto_created_by = models.ForeignKey( + "m3u.M3UAccount", + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="auto_created_channels", + help_text="The M3U account that auto-created this channel" + ) + def clean(self): # Enforce unique channel_number within a given group existing = Channel.objects.filter( diff --git a/apps/channels/serializers.py b/apps/channels/serializers.py index 0eb5acc3..278399dd 100644 --- a/apps/channels/serializers.py +++ b/apps/channels/serializers.py @@ -173,6 +173,8 @@ class ChannelSerializer(serializers.ModelSerializer): required=False, ) + auto_created_by_name = serializers.SerializerMethodField() + class Meta: model = Channel fields = [ @@ -188,6 +190,9 @@ class ChannelSerializer(serializers.ModelSerializer): "uuid", "logo_id", "user_level", + "auto_created", + "auto_created_by", + "auto_created_by_name", ] def to_representation(self, instance): @@ -286,6 +291,12 @@ class ChannelSerializer(serializers.ModelSerializer): return None return value # PrimaryKeyRelatedField will handle the conversion to object + def get_auto_created_by_name(self, obj): + """Get the name of the M3U account that auto-created this channel.""" + if obj.auto_created_by: + return obj.auto_created_by.name + return None + class ChannelGroupM3UAccountSerializer(serializers.ModelSerializer): enabled = serializers.BooleanField() diff --git a/apps/m3u/tasks.py b/apps/m3u/tasks.py index b5614376..97a12808 100644 --- a/apps/m3u/tasks.py +++ b/apps/m3u/tasks.py @@ -874,10 +874,11 @@ def sync_auto_channels(account_id): channel_group=channel_group ) - # Get existing channels in this group that were auto-created (we'll track this via a custom property) + # Get existing auto-created channels in this group that were created by this M3U account existing_auto_channels = Channel.objects.filter( channel_group=channel_group, - streams__m3u_account=account + auto_created=True, + auto_created_by=account ).distinct() # Create a mapping of stream hashes to existing channels @@ -909,14 +910,16 @@ def sync_auto_channels(account_id): # Get tvc_guide_stationid from custom properties if it exists tvc_guide_stationid = stream_custom_props.get("tvc-guide-stationid") - # Create the channel + # Create the channel with auto-created tracking channel = Channel.objects.create( channel_number=current_channel_number, name=stream.name, tvg_id=stream.tvg_id, tvc_guide_stationid=tvc_guide_stationid, channel_group=channel_group, - user_level=0 # Default user level + user_level=0, # Default user level + auto_created=True, # Mark as auto-created + auto_created_by=account # Track which M3U account created it ) # Associate the stream with the channel @@ -954,20 +957,17 @@ def sync_auto_channels(account_id): 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: - # Only delete if all streams for this channel are from this M3U account - # and this channel group - all_streams_from_account = all( - s.m3u_account_id == account.id and s.channel_group_id == channel_group.id - for s in channel.streams.all() - ) - - if all_streams_from_account: - logger.debug(f"Deleting auto channel: {channel.channel_number} - {channel.name}") + # 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"