from pathlib import Path from django.contrib.auth.password_validation import validate_password from django.core import signing from rest_framework import serializers from apps.library.services import MEDIA_FILE_SALT, VIDEO_EXTENSIONS from .models import User def signed_media_url(request, item): """A signed media URL an tag can load for an authenticated user.""" if item is None: return None user = getattr(request, "user", None) if user is None or not getattr(user, "is_authenticated", False): return None location = item.locations.first() is_video = ( location is not None and Path(location.rel_path).suffix.lower() in VIDEO_EXTENSIONS ) action = "thumbnail" if is_video else "raw" signature = signing.dumps( {"item": item.id, "user": user.id, "action": action}, salt=MEDIA_FILE_SALT, ) return f"/api/files/J-{item.id}/{action}/?sig={signature}" class UserSerializer(serializers.ModelSerializer): e621_configured = serializers.BooleanField(read_only=True) can_upload = serializers.BooleanField(read_only=True) avatar_j_id = serializers.SerializerMethodField() avatar_url = serializers.SerializerMethodField() class Meta: model = User fields = [ "id", "username", "email", "is_staff", "is_superuser", "date_joined", "role", "can_upload", "avatar_j_id", "avatar_url", "e621_username", "e621_configured", "preferences", ] read_only_fields = fields def get_avatar_j_id(self, obj): return f"J-{obj.avatar_id}" if obj.avatar_id else None def get_avatar_url(self, obj): return signed_media_url(self.context.get("request"), obj.avatar) class UserListSerializer(serializers.ModelSerializer): avatar_j_id = serializers.SerializerMethodField() avatar_url = serializers.SerializerMethodField() uploads_count = serializers.IntegerField(read_only=True) class Meta: model = User fields = [ "id", "username", "email", "role", "is_staff", "is_superuser", "date_joined", "avatar_j_id", "avatar_url", "uploads_count", ] read_only_fields = fields def get_avatar_j_id(self, obj): return f"J-{obj.avatar_id}" if obj.avatar_id else None def get_avatar_url(self, obj): return signed_media_url(self.context.get("request"), obj.avatar) class UserUpdateSerializer(serializers.Serializer): avatar_j_id = serializers.CharField( required=False, allow_blank=True, allow_null=True ) role = serializers.ChoiceField(choices=User.ROLE_CHOICES, required=False) class RegisterSerializer(serializers.ModelSerializer): password = serializers.CharField(write_only=True, validators=[validate_password]) class Meta: model = User fields = ["username", "email", "password"] def create(self, validated_data): return User.objects.create_user(**validated_data) class E621CredentialsSerializer(serializers.Serializer): username = serializers.CharField(max_length=100, allow_blank=True) api_key = serializers.CharField( max_length=100, allow_blank=True, trim_whitespace=True ) base_url = serializers.URLField( max_length=200, required=False, allow_blank=True ) class PreferencesSerializer(serializers.Serializer): """Per-user browse preferences; unknown keys are ignored.""" LANDING_PAGES = ["library", "online", "pools", "followed"] landing_page = serializers.ChoiceField(choices=LANDING_PAGES, required=False) ratings = serializers.ListField( child=serializers.ChoiceField(choices=["s", "q", "e"]), required=False, max_length=3, ) ordering = serializers.CharField(max_length=30, required=False) per_page = serializers.IntegerField(min_value=12, max_value=200, required=False) zoom = serializers.IntegerField(min_value=120, max_value=400, required=False)