mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-01-23 18:54:58 +00:00
275 lines
10 KiB
Python
275 lines
10 KiB
Python
from core.utils import validate_flexible_url
|
|
from rest_framework import serializers, status
|
|
from rest_framework.response import Response
|
|
from .models import M3UAccount, M3UFilter, ServerGroup, M3UAccountProfile
|
|
from core.models import UserAgent
|
|
from apps.channels.models import ChannelGroup, ChannelGroupM3UAccount
|
|
from apps.channels.serializers import (
|
|
ChannelGroupM3UAccountSerializer,
|
|
)
|
|
import logging
|
|
import json
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class M3UFilterSerializer(serializers.ModelSerializer):
|
|
"""Serializer for M3U Filters"""
|
|
|
|
class Meta:
|
|
model = M3UFilter
|
|
fields = [
|
|
"id",
|
|
"filter_type",
|
|
"regex_pattern",
|
|
"exclude",
|
|
"order",
|
|
"custom_properties",
|
|
]
|
|
|
|
|
|
class M3UAccountProfileSerializer(serializers.ModelSerializer):
|
|
account = serializers.SerializerMethodField()
|
|
|
|
def get_account(self, obj):
|
|
"""Include basic account information for frontend use"""
|
|
return {
|
|
'id': obj.m3u_account.id,
|
|
'name': obj.m3u_account.name,
|
|
'account_type': obj.m3u_account.account_type,
|
|
'is_xtream_codes': obj.m3u_account.account_type == 'XC'
|
|
}
|
|
|
|
class Meta:
|
|
model = M3UAccountProfile
|
|
fields = [
|
|
"id",
|
|
"name",
|
|
"max_streams",
|
|
"is_active",
|
|
"is_default",
|
|
"current_viewers",
|
|
"search_pattern",
|
|
"replace_pattern",
|
|
"custom_properties",
|
|
"account",
|
|
]
|
|
read_only_fields = ["id", "account"]
|
|
extra_kwargs = {
|
|
'search_pattern': {'required': False, 'allow_blank': True},
|
|
'replace_pattern': {'required': False, 'allow_blank': True},
|
|
}
|
|
|
|
def create(self, validated_data):
|
|
m3u_account = self.context.get("m3u_account")
|
|
|
|
# Use the m3u_account when creating the profile
|
|
validated_data["m3u_account_id"] = m3u_account.id
|
|
|
|
return super().create(validated_data)
|
|
|
|
def validate(self, data):
|
|
"""Custom validation to handle default profiles"""
|
|
# For updates to existing instances
|
|
if self.instance and self.instance.is_default:
|
|
# For default profiles, search_pattern and replace_pattern are not required
|
|
# and we don't want to validate them since they shouldn't be changed
|
|
return data
|
|
|
|
# For non-default profiles or new profiles, ensure required fields are present
|
|
if not data.get('search_pattern'):
|
|
raise serializers.ValidationError({
|
|
'search_pattern': ['This field is required for non-default profiles.']
|
|
})
|
|
if not data.get('replace_pattern'):
|
|
raise serializers.ValidationError({
|
|
'replace_pattern': ['This field is required for non-default profiles.']
|
|
})
|
|
|
|
return data
|
|
|
|
def update(self, instance, validated_data):
|
|
if instance.is_default:
|
|
# For default profiles, only allow updating name and custom_properties (for notes)
|
|
allowed_fields = {'name', 'custom_properties'}
|
|
|
|
# Remove any fields that aren't allowed for default profiles
|
|
disallowed_fields = set(validated_data.keys()) - allowed_fields
|
|
if disallowed_fields:
|
|
raise serializers.ValidationError(
|
|
f"Default profiles can only modify name and notes. "
|
|
f"Cannot modify: {', '.join(disallowed_fields)}"
|
|
)
|
|
|
|
return super().update(instance, validated_data)
|
|
|
|
def destroy(self, request, *args, **kwargs):
|
|
instance = self.get_object()
|
|
if instance.is_default:
|
|
return Response(
|
|
{"error": "Default profiles cannot be deleted."},
|
|
status=status.HTTP_400_BAD_REQUEST,
|
|
)
|
|
return super().destroy(request, *args, **kwargs)
|
|
|
|
|
|
class M3UAccountSerializer(serializers.ModelSerializer):
|
|
"""Serializer for M3U Account"""
|
|
|
|
filters = serializers.SerializerMethodField()
|
|
# Include user_agent as a mandatory field using its primary key.
|
|
user_agent = serializers.PrimaryKeyRelatedField(
|
|
queryset=UserAgent.objects.all(),
|
|
required=False,
|
|
allow_null=True,
|
|
)
|
|
profiles = M3UAccountProfileSerializer(many=True, read_only=True)
|
|
read_only_fields = ["locked", "created_at", "updated_at"]
|
|
# channel_groups = serializers.SerializerMethodField()
|
|
channel_groups = ChannelGroupM3UAccountSerializer(
|
|
source="channel_group", many=True, required=False
|
|
)
|
|
server_url = serializers.CharField(
|
|
required=False,
|
|
allow_blank=True,
|
|
allow_null=True,
|
|
validators=[validate_flexible_url],
|
|
)
|
|
enable_vod = serializers.BooleanField(required=False, write_only=True)
|
|
auto_enable_new_groups_live = serializers.BooleanField(required=False, write_only=True)
|
|
auto_enable_new_groups_vod = serializers.BooleanField(required=False, write_only=True)
|
|
auto_enable_new_groups_series = serializers.BooleanField(required=False, write_only=True)
|
|
|
|
class Meta:
|
|
model = M3UAccount
|
|
fields = [
|
|
"id",
|
|
"name",
|
|
"server_url",
|
|
"file_path",
|
|
"server_group",
|
|
"max_streams",
|
|
"is_active",
|
|
"created_at",
|
|
"updated_at",
|
|
"filters",
|
|
"user_agent",
|
|
"profiles",
|
|
"locked",
|
|
"channel_groups",
|
|
"refresh_interval",
|
|
"custom_properties",
|
|
"account_type",
|
|
"username",
|
|
"password",
|
|
"stale_stream_days",
|
|
"priority",
|
|
"status",
|
|
"last_message",
|
|
"enable_vod",
|
|
"auto_enable_new_groups_live",
|
|
"auto_enable_new_groups_vod",
|
|
"auto_enable_new_groups_series",
|
|
]
|
|
extra_kwargs = {
|
|
"password": {
|
|
"required": False,
|
|
"allow_blank": True,
|
|
},
|
|
}
|
|
|
|
def to_representation(self, instance):
|
|
data = super().to_representation(instance)
|
|
|
|
# Parse custom_properties to get VOD preference and auto_enable_new_groups settings
|
|
custom_props = instance.custom_properties or {}
|
|
|
|
data["enable_vod"] = custom_props.get("enable_vod", False)
|
|
data["auto_enable_new_groups_live"] = custom_props.get("auto_enable_new_groups_live", True)
|
|
data["auto_enable_new_groups_vod"] = custom_props.get("auto_enable_new_groups_vod", True)
|
|
data["auto_enable_new_groups_series"] = custom_props.get("auto_enable_new_groups_series", True)
|
|
return data
|
|
|
|
def update(self, instance, validated_data):
|
|
# Handle enable_vod preference and auto_enable_new_groups settings
|
|
enable_vod = validated_data.pop("enable_vod", None)
|
|
auto_enable_new_groups_live = validated_data.pop("auto_enable_new_groups_live", None)
|
|
auto_enable_new_groups_vod = validated_data.pop("auto_enable_new_groups_vod", None)
|
|
auto_enable_new_groups_series = validated_data.pop("auto_enable_new_groups_series", None)
|
|
|
|
# Get existing custom_properties
|
|
custom_props = instance.custom_properties or {}
|
|
|
|
# Update preferences
|
|
if enable_vod is not None:
|
|
custom_props["enable_vod"] = enable_vod
|
|
if auto_enable_new_groups_live is not None:
|
|
custom_props["auto_enable_new_groups_live"] = auto_enable_new_groups_live
|
|
if auto_enable_new_groups_vod is not None:
|
|
custom_props["auto_enable_new_groups_vod"] = auto_enable_new_groups_vod
|
|
if auto_enable_new_groups_series is not None:
|
|
custom_props["auto_enable_new_groups_series"] = auto_enable_new_groups_series
|
|
|
|
validated_data["custom_properties"] = custom_props
|
|
|
|
# Pop out channel group memberships so we can handle them manually
|
|
channel_group_data = validated_data.pop("channel_group", [])
|
|
|
|
# First, update the M3UAccount itself
|
|
for attr, value in validated_data.items():
|
|
setattr(instance, attr, value)
|
|
instance.save()
|
|
|
|
# Prepare a list of memberships to update
|
|
memberships_to_update = []
|
|
for group_data in channel_group_data:
|
|
group = group_data.get("channel_group")
|
|
enabled = group_data.get("enabled")
|
|
|
|
try:
|
|
membership = ChannelGroupM3UAccount.objects.get(
|
|
m3u_account=instance, channel_group=group
|
|
)
|
|
membership.enabled = enabled
|
|
memberships_to_update.append(membership)
|
|
except ChannelGroupM3UAccount.DoesNotExist:
|
|
continue
|
|
|
|
# Perform the bulk update
|
|
if memberships_to_update:
|
|
ChannelGroupM3UAccount.objects.bulk_update(
|
|
memberships_to_update, ["enabled"]
|
|
)
|
|
|
|
return instance
|
|
|
|
def create(self, validated_data):
|
|
# Handle enable_vod preference and auto_enable_new_groups settings during creation
|
|
enable_vod = validated_data.pop("enable_vod", False)
|
|
auto_enable_new_groups_live = validated_data.pop("auto_enable_new_groups_live", True)
|
|
auto_enable_new_groups_vod = validated_data.pop("auto_enable_new_groups_vod", True)
|
|
auto_enable_new_groups_series = validated_data.pop("auto_enable_new_groups_series", True)
|
|
|
|
# Parse existing custom_properties or create new
|
|
custom_props = validated_data.get("custom_properties", {})
|
|
|
|
# Set preferences (default to True for auto_enable_new_groups)
|
|
custom_props["enable_vod"] = enable_vod
|
|
custom_props["auto_enable_new_groups_live"] = auto_enable_new_groups_live
|
|
custom_props["auto_enable_new_groups_vod"] = auto_enable_new_groups_vod
|
|
custom_props["auto_enable_new_groups_series"] = auto_enable_new_groups_series
|
|
validated_data["custom_properties"] = custom_props
|
|
|
|
return super().create(validated_data)
|
|
|
|
def get_filters(self, obj):
|
|
filters = obj.filters.order_by("order")
|
|
return M3UFilterSerializer(filters, many=True).data
|
|
|
|
|
|
class ServerGroupSerializer(serializers.ModelSerializer):
|
|
"""Serializer for Server Group"""
|
|
|
|
class Meta:
|
|
model = ServerGroup
|
|
fields = ["id", "name"]
|