diff --git a/backend/apps/library/guest_filter.py b/backend/apps/library/guest_filter.py index 633d500..15c3450 100644 --- a/backend/apps/library/guest_filter.py +++ b/backend/apps/library/guest_filter.py @@ -20,6 +20,7 @@ from django.core.cache import cache logger = logging.getLogger(__name__) CACHE_KEY = "j621.guest_blacklist" +MIRROR_KEY = "j621.guest_blacklist.mirror" def parse_blacklist_patterns(lines): @@ -37,10 +38,18 @@ def parse_blacklist_patterns(lines): def cached_guest_blacklist(): - """Raw mirrored blacklist lines. Never hits the network.""" + """Raw mirrored blacklist lines. Never hits the network. + + Prefers the TTL'd entry, falls back to the persistent mirror (last + successful e621 fetch) and only then to the local fallback list, so an + expired TTL never degrades filtering. + """ stored = cache.get(CACHE_KEY) if stored is not None: return list(stored) + mirror = cache.get(MIRROR_KEY) + if mirror is not None: + return list(mirror) return list(settings.GUEST_BLACKLIST_FALLBACK) @@ -101,7 +110,11 @@ def refresh_guest_blacklist(): lines = fetch_e621_default_blacklist() used_fallback = lines is None if used_fallback: - lines = list(settings.GUEST_BLACKLIST_FALLBACK) + # Keep the last successful mirror (or the local list) rather than + # degrading guest filtering when e621 is unreachable. + lines = cache.get(MIRROR_KEY) or list(settings.GUEST_BLACKLIST_FALLBACK) + else: + cache.set(MIRROR_KEY, lines, timeout=None) cache.set(CACHE_KEY, lines, settings.GUEST_BLACKLIST_TTL) patterns = parse_blacklist_patterns(lines) diff --git a/backend/apps/library/serializers.py b/backend/apps/library/serializers.py index e966e91..d80afd2 100644 --- a/backend/apps/library/serializers.py +++ b/backend/apps/library/serializers.py @@ -1,10 +1,11 @@ import os +from pathlib import Path from django.core import signing from rest_framework import serializers from .models import MediaItem, MediaLocation, TempUpload -from .services import UPLOAD_FILE_SALT, VIDEO_EXTENSIONS +from .services import MEDIA_FILE_SALT, UPLOAD_FILE_SALT, VIDEO_EXTENSIONS class MediaLocationSerializer(serializers.ModelSerializer): @@ -99,6 +100,7 @@ class TempUploadSerializer(serializers.ModelSerializer): temp_id = serializers.UUIDField(source="id", read_only=True) library_j_id = serializers.SerializerMethodField() file_url = serializers.SerializerMethodField() + preview_url = serializers.SerializerMethodField() class Meta: model = TempUpload @@ -117,11 +119,19 @@ class TempUploadSerializer(serializers.ModelSerializer): "iqdb_data", "library_j_id", "file_url", + "preview_url", "created_at", "updated_at", ] read_only_fields = fields + def _request_user(self): + request = self.context.get("request") + user = getattr(request, "user", None) + if user is None or not getattr(user, "is_authenticated", False): + return None + return user + def get_library_j_id(self, obj): return f"J-{obj.library_item_id}" if obj.library_item_id else None @@ -129,12 +139,33 @@ class TempUploadSerializer(serializers.ModelSerializer): """Signed URL so /