Library search upgrades: tag search, tag cloud, status filter
Backend: - MediaItem gains search_tags (custom + e621 tags, lowercase) and has_custom_data, maintained on save with a data migration backfill - File list search accepts search_type=filename|tags|both (tag search is word-AND across the flattened tag text) and status=matched|custom| unknown filters - New /api/tags/cloud/ endpoint (cached 2 min per guest/auth, invalidated on item changes and deletions) returning the most-used tags, honouring guest visibility Frontend: - Library sidebar: Filename/Tags/Both selector, status pill toggles (persisted), and a clickable tag cloud that runs a tag search - Roadmap updated
This commit is contained in:
@@ -4,7 +4,7 @@ from urllib.parse import urlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.core import signing
|
||||
from django.db.models import Min
|
||||
from django.db.models import Min, Q
|
||||
from django.http import Http404, StreamingHttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
@@ -51,8 +51,20 @@ class MediaItemViewSet(
|
||||
numeric_ids.append(int(number))
|
||||
queryset = queryset.filter(pk__in=numeric_ids)
|
||||
search = self.request.query_params.get("search", "").strip()
|
||||
search_type = self.request.query_params.get("search_type", "filename").strip()
|
||||
if search:
|
||||
queryset = queryset.filter(locations__rel_path__icontains=search)
|
||||
words = [word for word in search.lower().split() if word]
|
||||
if search_type == "tags":
|
||||
for word in words:
|
||||
queryset = queryset.filter(search_tags__icontains=word)
|
||||
elif search_type == "both":
|
||||
query = Q(locations__rel_path__icontains=search)
|
||||
tag_query = Q()
|
||||
for word in words:
|
||||
tag_query &= Q(search_tags__icontains=word)
|
||||
queryset = queryset.filter(query | tag_query)
|
||||
else:
|
||||
queryset = queryset.filter(locations__rel_path__icontains=search)
|
||||
ratings = [
|
||||
value
|
||||
for value in self.request.query_params.get("rating", "").split(",")
|
||||
@@ -60,6 +72,25 @@ class MediaItemViewSet(
|
||||
]
|
||||
if ratings:
|
||||
queryset = queryset.filter(rating__in=ratings)
|
||||
statuses = []
|
||||
for value in self.request.query_params.getlist("status"):
|
||||
statuses.extend(
|
||||
part.strip() for part in value.split(",") if part.strip()
|
||||
)
|
||||
if statuses:
|
||||
status_query = Q()
|
||||
for status_value in set(statuses):
|
||||
if status_value == "custom":
|
||||
status_query |= Q(has_custom_data=True)
|
||||
elif status_value == "matched":
|
||||
status_query |= Q(
|
||||
has_custom_data=False, e621_post_id__isnull=False
|
||||
)
|
||||
elif status_value == "unknown":
|
||||
status_query |= Q(
|
||||
has_custom_data=False, e621_post_id__isnull=True
|
||||
)
|
||||
queryset = queryset.filter(status_query)
|
||||
ordering = self.request.query_params.get("ordering", "").strip()
|
||||
queryset = queryset.order_by(
|
||||
ordering if ordering in LIST_ORDERINGS else "-created_at"
|
||||
|
||||
Reference in New Issue
Block a user