[search] Add scrobble searching
All checks were successful
build & deploy / test (push) Successful in 1m44s
build & deploy / deploy (push) Successful in 22s

This commit is contained in:
2026-03-24 14:23:24 -04:00
parent 6766ea7dbb
commit cce2db0ea1
4 changed files with 259 additions and 0 deletions

View File

@ -968,6 +968,123 @@ class EmbeddableTopArtistsWidget(TemplateView):
return context
class ScrobbleSearchView(LoginRequiredMixin, TemplateView):
template_name = "scrobbles/scrobble_search.html"
MEDIA_FIELDS = {
"Video": ("video__title", "video__overview"),
"Track": ("track__title", None),
"PodcastEpisode": ("podcast_episode__title", None),
"Book": ("book__title", "book__description"),
"Paper": ("paper__title", None),
"VideoGame": ("video_game__title", "video_game__description"),
"BoardGame": ("board_game__title", "board_game__description"),
"Beer": ("beer__name", "beer__style"),
"Food": ("food__name", "food__description"),
"Puzzle": ("puzzle__name", "puzzle__description"),
"Trail": ("trail__name", "trail__location"),
"GeoLocation": ("geo_location__name", "geo_location__description"),
"Task": ("task__title", "task__description"),
"WebPage": ("web_page__title", None),
"LifeEvent": ("life_event__title", "life_event__description"),
"Mood": ("mood__title", "mood__description"),
"BrickSet": ("brick_set__title", None),
}
def get(self, request, *args, **kwargs):
if not request.GET.get("q") and not request.GET.get("media_type"):
return super().get(request, *args, **kwargs)
return self.search(request)
def search(self, request):
query = request.GET.get("q", "").strip()
media_type = request.GET.get("media_type", "")
user = request.user
scrobbles = Scrobble.objects.filter(user=user)
if media_type:
scrobbles = scrobbles.filter(media_type=media_type)
if query:
from django.contrib.contenttypes.models import ContentType
from taggit.models import TaggedItem
scrobble_ct = ContentType.objects.get_for_model(Scrobble)
tag_match_ids = list(
TaggedItem.objects.filter(
content_type=scrobble_ct,
tag__name__icontains=query,
).values_list("object_id", flat=True)
)
q = Q()
q |= Q(log__notes__icontains=query)
q |= Q(log__title__icontains=query)
q |= Q(log__description__icontains=query)
q |= Q(source__icontains=query)
if media_type and media_type in self.MEDIA_FIELDS:
title_field, desc_field = self.MEDIA_FIELDS[media_type]
if title_field:
q |= Q(**{f"{title_field}__icontains": query})
if desc_field:
q |= Q(**{f"{desc_field}__icontains": query})
scrobbles = scrobbles.filter(q).distinct()
else:
all_matching_ids = set()
base_matches = scrobbles.filter(q).values_list("id", flat=True)
all_matching_ids.update(base_matches)
for mt, (title_field, desc_field) in self.MEDIA_FIELDS.items():
mt_queries = []
if title_field:
mt_queries.append(Q(**{f"{title_field}__icontains": query}))
if desc_field:
mt_queries.append(Q(**{f"{desc_field}__icontains": query}))
if mt_queries:
mt_q = mt_queries[0]
for mq in mt_queries[1:]:
mt_q |= mq
try:
mt_matches = scrobbles.filter(mt_q).values_list(
"id", flat=True
)
all_matching_ids.update(mt_matches)
except Exception:
pass
all_matching_ids.update(tag_match_ids)
scrobbles = Scrobble.objects.filter(id__in=all_matching_ids, user=user)
scrobbles = scrobbles.order_by("-timestamp")[:100]
context = self.get_context_data(
scrobbles=scrobbles,
query=query,
media_type=media_type,
media_types=Scrobble.MediaType.choices,
)
return self.render_to_response(context)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
if "scrobbles" not in ctx:
ctx["scrobbles"] = []
if "query" not in ctx:
ctx["query"] = ""
if "media_type" not in ctx:
ctx["media_type"] = ""
if "media_types" not in ctx:
ctx["media_types"] = Scrobble.MediaType.choices
return ctx
class EmbeddableTopBoardGamesWidget(BaseEmbeddableWidget):
media_type = "Board Games"
count_label = "plays"