from rest_framework import serializers from .models import PlaylistItem, Video, VideoCategory, VideoCollection, VideoPlaylist class AbsoluteImageField(serializers.ImageField): def to_representation(self, value): if not value: return None request = self.context.get("request") url = value.url if hasattr(value, "url") else str(value) if request: return request.build_absolute_uri(url) return url class AbsoluteFileField(serializers.FileField): def to_representation(self, value): if not value: return None request = self.context.get("request") url = value.url if hasattr(value, "url") else str(value) if request: return request.build_absolute_uri(url) return url class AdminVideoCategorySerializer(serializers.ModelSerializer): class Meta: model = VideoCategory fields = ["id", "title", "slug", "status", "order"] class AdminVideoCollectionSerializer(serializers.ModelSerializer): class Meta: model = VideoCollection fields = ["id", "title", "slug", "summary", "display_position", "status", "order", "pin_top"] class AdminVideoListSerializer(serializers.ModelSerializer): thumbnail = AbsoluteImageField(required=False, allow_null=True) video_file = AbsoluteFileField(required=False, allow_null=True) class Meta: model = Video fields = [ "id", "title", "slug", "thumbnail", "description", "video_type", "video_file", "video_url", "video_time", "status", "view_count", "created_at", "updated_at", ] read_only_fields = ["id", "view_count", "created_at", "updated_at"] class AdminVideoDetailSerializer(serializers.ModelSerializer): thumbnail = AbsoluteImageField(required=False, allow_null=True) video_file = AbsoluteFileField(required=False, allow_null=True) remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False) remove_video_file = serializers.BooleanField(write_only=True, required=False, default=False) class Meta: model = Video fields = [ "id", "title", "slug", "thumbnail", "description", "video_type", "video_file", "video_url", "video_time", "status", "view_count", "created_at", "updated_at", "remove_thumbnail", "remove_video_file", ] read_only_fields = ["id", "view_count", "created_at", "updated_at"] def validate(self, attrs): video_type = attrs.get("video_type", getattr(self.instance, "video_type", None)) video_url = attrs.get("video_url", getattr(self.instance, "video_url", None)) video_file = attrs.get("video_file", getattr(self.instance, "video_file", None)) remove_video_file = attrs.get("remove_video_file", False) if video_type == Video.VedioTypeChoices.YOUTUBE_LINK and not video_url: raise serializers.ValidationError({ "video_url": "This field is required when video type is link.", }) if video_type == Video.VedioTypeChoices.VIDEO_FILE and not video_file and not getattr(self.instance, "video_file", None): raise serializers.ValidationError({ "video_file": "This field is required when video type is file.", }) if video_type == Video.VedioTypeChoices.VIDEO_FILE and remove_video_file and "video_file" not in attrs: raise serializers.ValidationError({ "video_file": "Please upload a new file before removing the current one.", }) return attrs def create(self, validated_data): validated_data.pop("remove_thumbnail", False) validated_data.pop("remove_video_file", False) return super().create(validated_data) def update(self, instance, validated_data): remove_thumbnail = validated_data.pop("remove_thumbnail", False) remove_video_file = validated_data.pop("remove_video_file", False) if remove_thumbnail and instance.thumbnail: instance.thumbnail.delete(save=False) instance.thumbnail = None if remove_video_file and instance.video_file: instance.video_file.delete(save=False) instance.video_file = None return super().update(instance, validated_data) class AdminVideoPlaylistItemVideoSerializer(serializers.ModelSerializer): thumbnail = AbsoluteImageField(required=False, allow_null=True) class Meta: model = Video fields = ["id", "title", "slug", "thumbnail", "video_type", "video_time", "status"] class AdminVideoPlaylistItemSerializer(serializers.ModelSerializer): video_detail = AdminVideoPlaylistItemVideoSerializer(source="video", read_only=True) class Meta: model = PlaylistItem fields = ["id", "playlist", "video", "priority", "created_at", "updated_at", "video_detail"] read_only_fields = ["id", "created_at", "updated_at", "video_detail"] def validate(self, attrs): playlist = attrs.get("playlist", getattr(self.instance, "playlist", None)) video = attrs.get("video", getattr(self.instance, "video", None)) if playlist and video: query = PlaylistItem.objects.filter(video=video).exclude(playlist=playlist) if self.instance: query = query.exclude(pk=self.instance.pk) existing = query.select_related("playlist").first() if existing: raise serializers.ValidationError({ "video": f'This video is already used in playlist "{existing.playlist.title}".', }) return attrs class AdminVideoPlaylistListSerializer(serializers.ModelSerializer): thumbnail = AbsoluteImageField(required=False, allow_null=True) categories_detail = AdminVideoCategorySerializer(source="categories", many=True, read_only=True) collections_detail = AdminVideoCollectionSerializer(source="collections", many=True, read_only=True) items_count = serializers.SerializerMethodField() class Meta: model = VideoPlaylist fields = [ "id", "title", "slug", "thumbnail", "slogan", "status", "order", "view_count", "total_time", "created_at", "updated_at", "categories_detail", "collections_detail", "items_count", ] read_only_fields = ["id", "view_count", "total_time", "created_at", "updated_at", "items_count"] def get_items_count(self, obj): return obj.playlist_items.count() class AdminVideoPlaylistDetailSerializer(serializers.ModelSerializer): thumbnail = AbsoluteImageField(required=False, allow_null=True) categories = serializers.PrimaryKeyRelatedField(queryset=VideoCategory.objects.all(), many=True, required=False) collections = serializers.PrimaryKeyRelatedField(queryset=VideoCollection.objects.all(), many=True, required=False) categories_detail = AdminVideoCategorySerializer(source="categories", many=True, read_only=True) collections_detail = AdminVideoCollectionSerializer(source="collections", many=True, read_only=True) playlist_items_detail = AdminVideoPlaylistItemSerializer(source="playlist_items", many=True, read_only=True) remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False) class Meta: model = VideoPlaylist fields = [ "id", "title", "slug", "slogan", "description", "thumbnail", "order", "status", "view_count", "total_time", "created_at", "updated_at", "categories", "collections", "categories_detail", "collections_detail", "playlist_items_detail", "remove_thumbnail", ] read_only_fields = ["id", "view_count", "total_time", "created_at", "updated_at", "playlist_items_detail"] def create(self, validated_data): validated_data.pop("remove_thumbnail", False) categories = validated_data.pop("categories", []) collections = validated_data.pop("collections", []) playlist = super().create(validated_data) if categories: playlist.categories.set(categories) if collections: playlist.collections.set(collections) playlist.total_time = playlist.calculate_total_time() playlist.save(update_fields=["total_time"]) return playlist def update(self, instance, validated_data): remove_thumbnail = validated_data.pop("remove_thumbnail", False) categories = validated_data.pop("categories", None) collections = validated_data.pop("collections", None) if remove_thumbnail and instance.thumbnail: instance.thumbnail.delete(save=False) instance.thumbnail = None playlist = super().update(instance, validated_data) if categories is not None: playlist.categories.set(categories) if collections is not None: playlist.collections.set(collections) playlist.total_time = playlist.calculate_total_time() playlist.save(update_fields=["total_time"]) return playlist