diff --git a/apps/channels/api_views.py b/apps/channels/api_views.py index 0973dce0..73c6412e 100644 --- a/apps/channels/api_views.py +++ b/apps/channels/api_views.py @@ -1122,7 +1122,7 @@ class CleanupUnusedLogosAPIView(APIView): def post(self, request): """Delete all logos with no channel associations""" delete_files = request.data.get("delete_files", False) - + unused_logos = Logo.objects.filter(channels__isnull=True) deleted_count = unused_logos.count() logo_names = list(unused_logos.values_list('name', flat=True)) @@ -1204,7 +1204,13 @@ class LogoViewSet(viewsets.ModelViewSet): def update(self, request, *args, **kwargs): """Update an existing logo""" - return super().update(request, *args, **kwargs) + partial = kwargs.pop('partial', False) + instance = self.get_object() + serializer = self.get_serializer(instance, data=request.data, partial=partial) + if serializer.is_valid(): + logo = serializer.save() + return Response(self.get_serializer(logo).data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def destroy(self, request, *args, **kwargs): """Delete a logo and remove it from any channels using it""" diff --git a/apps/channels/serializers.py b/apps/channels/serializers.py index 82b5f808..9273b265 100644 --- a/apps/channels/serializers.py +++ b/apps/channels/serializers.py @@ -38,6 +38,17 @@ class LogoSerializer(serializers.ModelSerializer): return value + def create(self, validated_data): + """Handle logo creation with proper URL validation""" + return Logo.objects.create(**validated_data) + + def update(self, instance, validated_data): + """Handle logo updates""" + for attr, value in validated_data.items(): + setattr(instance, attr, value) + instance.save() + return instance + def get_cache_url(self, obj): # return f"/api/channels/logos/{obj.id}/cache/" request = self.context.get("request") diff --git a/dispatcharr/utils.py b/dispatcharr/utils.py index 5e1ad087..260515fc 100644 --- a/dispatcharr/utils.py +++ b/dispatcharr/utils.py @@ -21,10 +21,10 @@ def json_success_response(data=None, status=200): def validate_logo_file(file): """Validate uploaded logo file size and MIME type.""" - valid_mime_types = ["image/jpeg", "image/png", "image/gif", "image/webp"] + valid_mime_types = ["image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml"] if file.content_type not in valid_mime_types: - raise ValidationError("Unsupported file type. Allowed types: JPEG, PNG, GIF, WebP.") - if file.size > 5 * 1024 * 1024: # Increased to 5MB + raise ValidationError("Unsupported file type. Allowed types: JPEG, PNG, GIF, WebP, SVG.") + if file.size > 5 * 1024 * 1024: # 5MB raise ValidationError("File too large. Max 5MB.") diff --git a/frontend/src/api.js b/frontend/src/api.js index 7cbb6214..154a1341 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -1299,9 +1299,17 @@ export default class API { static async createLogo(values) { try { + // Use FormData for logo creation to match backend expectations + const formData = new FormData(); + for (const [key, value] of Object.entries(values)) { + if (value !== null && value !== undefined) { + formData.append(key, value); + } + } + const response = await request(`${host}/api/channels/logos/`, { method: 'POST', - body: values, + body: formData, }); useChannelsStore.getState().addLogo(response); @@ -1314,19 +1322,9 @@ export default class API { static async updateLogo(id, values) { try { - // Convert values to FormData for the multipart/form-data content type - const formData = new FormData(); - - // Add each field to the form data - Object.keys(values).forEach(key => { - if (values[key] !== null && values[key] !== undefined) { - formData.append(key, values[key]); - } - }); - const response = await request(`${host}/api/channels/logos/${id}/`, { method: 'PUT', - body: formData, // Send as FormData instead of JSON + body: values, // This will be converted to JSON in the request function }); useChannelsStore.getState().updateLogo(response); diff --git a/frontend/src/components/forms/Logo.jsx b/frontend/src/components/forms/Logo.jsx index e209659c..1aef1e66 100644 --- a/frontend/src/components/forms/Logo.jsx +++ b/frontend/src/components/forms/Logo.jsx @@ -206,9 +206,10 @@ const LogoForm = ({ logo = null, isOpen, onClose }) => { @@ -226,7 +227,7 @@ const LogoForm = ({ logo = null, isOpen, onClose }) => { Drag image here or click to select - Supports PNG, JPEG, GIF, WebP files + Supports PNG, JPEG, GIF, WebP, SVG files