# core/serializers.py import json import ipaddress from rest_framework import serializers from .models import CoreSettings, UserAgent, StreamProfile, OutputProfile, DVR_SETTINGS_KEY, NETWORK_ACCESS_KEY class UserAgentSerializer(serializers.ModelSerializer): class Meta: model = UserAgent fields = [ "id", "name", "user_agent", "description", "is_active", "created_at", "updated_at", ] class StreamProfileSerializer(serializers.ModelSerializer): class Meta: model = StreamProfile fields = [ "id", "name", "command", "parameters", "is_active", "user_agent", "locked", ] class OutputProfileSerializer(serializers.ModelSerializer): class Meta: model = OutputProfile fields = ["id", "name", "command", "parameters", "is_active", "locked"] class CoreSettingsSerializer(serializers.ModelSerializer): class Meta: model = CoreSettings fields = "__all__" def update(self, instance, validated_data): if instance.key == NETWORK_ACCESS_KEY: errors = False invalid = {} value = validated_data.get("value") for key, val in value.items(): cidrs = val.split(",") for cidr in cidrs: try: ipaddress.ip_network(cidr) except: errors = True if key not in invalid: invalid[key] = [] invalid[key].append(cidr) if errors: # Perform CIDR validation raise serializers.ValidationError( { "message": "Invalid CIDRs", "value": invalid, } ) # Sanitize series_rules when DVR settings are saved through the # generic settings API (e.g. Settings page round-trip) to prevent # corrupted non-dict entries from persisting. if instance.key == DVR_SETTINGS_KEY: value = validated_data.get("value") if isinstance(value, dict) and "series_rules" in value: rules = value["series_rules"] value["series_rules"] = ( [r for r in rules if isinstance(r, dict)] if isinstance(rules, list) else [] ) result = super().update(instance, validated_data) # Note: Cache invalidation and notification sync is handled by post_save signal # in core/signals.py to ensure it happens even if settings are updated elsewhere return result class ProxySettingsSerializer(serializers.Serializer): """Serializer for proxy settings stored as JSON in CoreSettings""" buffering_timeout = serializers.IntegerField(min_value=0, max_value=300) buffering_speed = serializers.FloatField(min_value=0.1, max_value=10.0) redis_chunk_ttl = serializers.IntegerField(min_value=10, max_value=3600) channel_shutdown_delay = serializers.IntegerField(min_value=0, max_value=300) channel_init_grace_period = serializers.IntegerField(min_value=0, max_value=300) channel_client_wait_period = serializers.IntegerField(min_value=0, max_value=300, required=False, default=5) new_client_behind_seconds = serializers.IntegerField(min_value=0, max_value=120, required=False, default=5) def validate_buffering_timeout(self, value): if value < 0 or value > 300: raise serializers.ValidationError("Buffering timeout must be between 0 and 300 seconds") return value def validate_buffering_speed(self, value): if value < 0.1 or value > 10.0: raise serializers.ValidationError("Buffering speed must be between 0.1 and 10.0") return value def validate_redis_chunk_ttl(self, value): if value < 10 or value > 3600: raise serializers.ValidationError("Redis chunk TTL must be between 10 and 3600 seconds") return value def validate_channel_shutdown_delay(self, value): if value < 0 or value > 300: raise serializers.ValidationError("Channel shutdown delay must be between 0 and 300 seconds") return value def validate_channel_init_grace_period(self, value): if value < 0 or value > 300: raise serializers.ValidationError( "Channel initialization timeout must be between 0 and 300 seconds" ) return value def validate_channel_client_wait_period(self, value): if value < 0 or value > 300: raise serializers.ValidationError( "Client connect grace period must be between 0 and 300 seconds" ) return value def validate_new_client_behind_seconds(self, value): if value < 0 or value > 120: raise serializers.ValidationError("New client buffer must be between 0 and 120 seconds") return value class SystemNotificationSerializer(serializers.ModelSerializer): """Serializer for system notifications.""" is_dismissed = serializers.SerializerMethodField() class Meta: from .models import SystemNotification model = SystemNotification fields = [ 'id', 'notification_key', 'notification_type', 'priority', 'title', 'message', 'action_data', 'is_active', 'admin_only', 'expires_at', 'created_at', 'is_dismissed', 'source', ] read_only_fields = ['created_at'] def get_is_dismissed(self, obj): """Check if the current user has dismissed this notification.""" request = self.context.get('request') if request and request.user.is_authenticated: return obj.dismissals.filter(user=request.user).exists() return False class NotificationDismissalSerializer(serializers.ModelSerializer): """Serializer for notification dismissals.""" class Meta: from .models import NotificationDismissal model = NotificationDismissal fields = ['id', 'notification', 'dismissed_at', 'action_taken'] read_only_fields = ['dismissed_at']