You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
11 KiB
285 lines
11 KiB
from rest_framework import serializers
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from utils.image_compression import maybe_compress_uploaded_file
|
|
|
|
from .models import PlaylistItem, Video, VideoCategory, VideoCollection, VideoPlaylist
|
|
|
|
|
|
class AbsoluteImageField(serializers.ImageField):
|
|
def to_internal_value(self, data):
|
|
uploaded = super().to_internal_value(data)
|
|
compressed_bytes = maybe_compress_uploaded_file(uploaded)
|
|
if compressed_bytes is None:
|
|
return uploaded
|
|
|
|
return SimpleUploadedFile(
|
|
name=getattr(uploaded, "name", "image"),
|
|
content=compressed_bytes,
|
|
content_type=getattr(uploaded, "content_type", None),
|
|
)
|
|
|
|
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"]
|
|
|
|
|
|
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)
|
|
|
|
bookmark_count = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Video
|
|
fields = [
|
|
"id",
|
|
"title",
|
|
"slug",
|
|
"thumbnail",
|
|
"description",
|
|
"video_type",
|
|
"video_file",
|
|
"video_url",
|
|
"video_time",
|
|
"status",
|
|
"view_count",
|
|
"bookmark_count",
|
|
"created_at",
|
|
"updated_at",
|
|
"remove_thumbnail",
|
|
"remove_video_file",
|
|
]
|
|
read_only_fields = ["id", "view_count", "bookmark_count", "created_at", "updated_at"]
|
|
|
|
def get_bookmark_count(self, obj):
|
|
from apps.bookmark.models.bookmark import Bookmark
|
|
return Bookmark.objects.filter(service=Bookmark.ServiceChoices.VIDEO, content_id=obj.id, status=True).count()
|
|
|
|
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)
|
|
|
|
bookmark_count = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = VideoPlaylist
|
|
fields = [
|
|
"id",
|
|
"title",
|
|
"slug",
|
|
"slogan",
|
|
"description",
|
|
"thumbnail",
|
|
"order",
|
|
"status",
|
|
"view_count",
|
|
"bookmark_count",
|
|
"total_time",
|
|
"created_at",
|
|
"updated_at",
|
|
"categories",
|
|
"collections",
|
|
"categories_detail",
|
|
"collections_detail",
|
|
"playlist_items_detail",
|
|
"remove_thumbnail",
|
|
]
|
|
read_only_fields = ["id", "view_count", "bookmark_count", "total_time", "created_at", "updated_at", "playlist_items_detail"]
|
|
|
|
def get_bookmark_count(self, obj):
|
|
from apps.bookmark.models.bookmark import Bookmark
|
|
return Bookmark.objects.filter(service=Bookmark.ServiceChoices.VIDEO_PLAYLIST, content_id=obj.id, status=True).count()
|
|
|
|
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
|