音楽で楽しみましょう!-Let's have fun with music!-

Signed-off-by: Shin'ya Minazuki <shinyoukai@laidback.moe>
This commit is contained in:
Shin'ya Minazuki 2026-01-24 16:16:49 -03:00
commit 54c6d22102
517 changed files with 637 additions and 639 deletions

View file

View file

@ -0,0 +1,13 @@
from django.apps import AppConfig, apps
from . import record
class ActivityConfig(AppConfig):
name = "funkwhale_api.activity"
def ready(self):
super().ready()
app_names = [app.name for app in apps.app_configs.values()]
record.registry.autodiscover(app_names)

View file

@ -0,0 +1,37 @@
import persisting_theory
class ActivityRegistry(persisting_theory.Registry):
look_into = "activities"
def _register_for_model(self, model, attr, value):
key = model._meta.label
d = self.setdefault(key, {"consumers": []})
d[attr] = value
def register_serializer(self, serializer_class):
model = serializer_class.Meta.model
self._register_for_model(model, "serializer", serializer_class)
return serializer_class
def register_consumer(self, label):
def decorator(func):
consumers = self[label]["consumers"]
if func not in consumers:
consumers.append(func)
return func
return decorator
registry = ActivityRegistry()
def send(obj):
conf = registry[obj.__class__._meta.label]
consumers = conf["consumers"]
if not consumers:
return
serializer = conf["serializer"](obj)
for consumer in consumers:
consumer(data=serializer.data, obj=obj)

View file

@ -0,0 +1,23 @@
from rest_framework import serializers
from funkwhale_api.activity import record
class ModelSerializer(serializers.ModelSerializer):
id = serializers.CharField(source="get_activity_url")
local_id = serializers.IntegerField(source="id")
# url = serializers.SerializerMethodField()
def get_url(self, obj):
return self.get_id(obj)
class AutoSerializer(serializers.Serializer):
"""
A serializer that will automatically use registered activity serializers
to serialize an henerogeneous list of objects (favorites, listenings, etc.)
"""
def to_representation(self, instance):
serializer = record.registry[instance._meta.label]["serializer"](instance)
return serializer.data

View file

@ -0,0 +1,51 @@
from django.db import models
from funkwhale_api.common import fields
from funkwhale_api.favorites.models import TrackFavorite
from funkwhale_api.history.models import Listening
def combined_recent(limit, **kwargs):
datetime_field = kwargs.pop("datetime_field", "creation_date")
source_querysets = {qs.model._meta.label: qs for qs in kwargs.pop("querysets")}
querysets = {
k: qs.annotate(
__type=models.Value(qs.model._meta.label, output_field=models.CharField())
).values("pk", datetime_field, "__type")
for k, qs in source_querysets.items()
}
_qs_list = list(querysets.values())
union_qs = _qs_list[0].union(*_qs_list[1:])
records = []
for row in union_qs.order_by(f"-{datetime_field}")[:limit]:
records.append(
{"type": row["__type"], "when": row[datetime_field], "pk": row["pk"]}
)
# Now we bulk-load each object type in turn
to_load = {}
for record in records:
to_load.setdefault(record["type"], []).append(record["pk"])
fetched = {}
for key, pks in to_load.items():
for item in source_querysets[key].filter(pk__in=pks):
fetched[(key, item.pk)] = item
# Annotate 'records' with loaded objects
for record in records:
record["object"] = fetched[(record["type"], record["pk"])]
return records
def get_activity(user, limit=20):
query = fields.privacy_level_query(user, lookup_field="user__privacy_level")
querysets = [
Listening.objects.filter(query).select_related(
"track", "user", "track__artist", "track__album__artist"
),
TrackFavorite.objects.filter(query).select_related(
"track", "user", "track__artist", "track__album__artist"
),
]
records = combined_recent(limit=limit, querysets=querysets)
return [r["object"] for r in records]

View file

@ -0,0 +1,20 @@
from drf_spectacular.utils import extend_schema
from rest_framework import viewsets
from rest_framework.response import Response
from funkwhale_api.common.permissions import ConditionalAuthentication
from funkwhale_api.favorites.models import TrackFavorite
from . import serializers, utils
class ActivityViewSet(viewsets.GenericViewSet):
serializer_class = serializers.AutoSerializer
permission_classes = [ConditionalAuthentication]
queryset = TrackFavorite.objects.none()
@extend_schema(operation_id="get_activity")
def list(self, request, *args, **kwargs):
activity = utils.get_activity(user=request.user)
serializer = self.serializer_class(activity, many=True)
return Response({"results": serializer.data}, status=200)