fix(front):search fetch federation object was expecting a music object but got a activitypub obj

This commit is contained in:
Petitminion 2025-06-04 02:10:15 +02:00
parent 1be4b5204a
commit 7f6d066acc
8 changed files with 70 additions and 7 deletions

View File

@ -2462,6 +2462,9 @@ paths:
responses:
'201':
content:
application/activity+json:
schema:
$ref: '#/components/schemas/Fetch'
application/json:
schema:
$ref: '#/components/schemas/Fetch'
@ -2484,6 +2487,9 @@ paths:
responses:
'200':
content:
application/activity+json:
schema:
$ref: '#/components/schemas/Fetch'
application/json:
schema:
$ref: '#/components/schemas/Fetch'
@ -11972,6 +11978,9 @@ paths:
responses:
'201':
content:
application/activity+json:
schema:
$ref: '#/components/schemas/Fetch'
application/json:
schema:
$ref: '#/components/schemas/Fetch'
@ -11994,6 +12003,9 @@ paths:
responses:
'200':
content:
application/activity+json:
schema:
$ref: '#/components/schemas/Fetch'
application/json:
schema:
$ref: '#/components/schemas/Fetch'

View File

@ -13,7 +13,9 @@ from funkwhale_api.audio import models as audio_models
from funkwhale_api.audio import serializers as audio_serializers
from funkwhale_api.common import serializers as common_serializers
from funkwhale_api.music import models as music_models
from funkwhale_api.music import serializers as music_serializers
from funkwhale_api.playlists import models as playlists_models
from funkwhale_api.playlists import serializers as playlist_serializers
from funkwhale_api.users import serializers as users_serializers
from . import filters, models
@ -197,10 +199,19 @@ OBJECT_SERIALIZER_MAPPING = {
music_models.Artist: federation_serializers.ArtistSerializer,
music_models.Album: federation_serializers.AlbumSerializer,
music_models.Track: federation_serializers.TrackSerializer,
music_models.Library: federation_serializers.LibrarySerializer,
models.Actor: federation_serializers.APIActorSerializer,
audio_models.Channel: audio_serializers.ChannelSerializer,
playlists_models.Playlist: federation_serializers.PlaylistSerializer,
}
OBJECT_MUSIC_SERIALIZER_MAPPING = {
music_models.Artist: music_serializers.ArtistSerializer,
music_models.Album: music_serializers.AlbumSerializer,
music_models.Track: music_serializers.TrackSerializer,
models.Actor: federation_serializers.APIActorSerializer,
audio_models.Channel: audio_serializers.ChannelSerializer,
playlists_models.Playlist: playlist_serializers.PlaylistSerializer,
}
def convert_url_to_webfinger(url):
@ -283,6 +294,9 @@ class FetchSerializer(serializers.ModelSerializer):
return value
return f"webfinger://{value}"
# to do : this is incomplete, schema conflict because
# federation serializers have the same name than musi serializer -> upgrade fed serializers to new names
# and add the new object here
@extend_schema_field(
{
"oneOf": [
@ -300,7 +314,12 @@ class FetchSerializer(serializers.ModelSerializer):
if obj is None:
return None
serializer_class = OBJECT_SERIALIZER_MAPPING.get(type(obj))
media_type = self.context.get("media_type")
if media_type == "application/activity+json":
serializer_class = OBJECT_SERIALIZER_MAPPING.get(type(obj))
else:
serializer_class = OBJECT_MUSIC_SERIALIZER_MAPPING.get(type(obj))
if serializer_class:
return serializer_class(obj).data
return None

View File

@ -7,10 +7,13 @@ from django.db.models import Count, Q
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import decorators, mixins, permissions, response, viewsets
from rest_framework.exceptions import NotFound as RestNotFound
from rest_framework.negotiation import DefaultContentNegotiation
from rest_framework.renderers import JSONRenderer
from funkwhale_api.common import preferences
from funkwhale_api.common import utils as common_utils
from funkwhale_api.common.permissions import ConditionalAuthentication
from funkwhale_api.common.renderers import ActivityStreamRenderer
from funkwhale_api.music import models as music_models
from funkwhale_api.music import serializers as music_serializers
from funkwhale_api.music import views as music_views
@ -245,10 +248,24 @@ class FetchViewSet(
serializer_class = api_serializers.FetchSerializer
permission_classes = [permissions.IsAuthenticated]
throttling_scopes = {"create": {"authenticated": "fetch"}}
renderer_classes = [ActivityStreamRenderer, JSONRenderer]
def get_queryset(self):
return super().get_queryset().filter(actor=self.request.user.actor)
def get_serializer_context(self):
context = super().get_serializer_context()
negotiator = DefaultContentNegotiation()
try:
renderer, media_type = negotiator.select_renderer(
self.request, self.get_renderers()
)
context["media_type"] = media_type
except Exception:
context["media_type"] = None
return context
def perform_create(self, serializer):
fetch = serializer.save(actor=self.request.user.actor)
if fetch.status == "finished":

View File

@ -1066,6 +1066,7 @@ class LibrarySerializer(PaginatedCollectionSerializer):
privacy = {"": "me", "./": "me", None: "me", contexts.AS.Public: "everyone"}
library, created = music_models.Library.objects.update_or_create(
fid=validated_data["id"],
uuid=validated_data["id"].rstrip("/").split("/")[-1],
actor=actor,
defaults={
"uploads_count": validated_data["totalItems"],
@ -1449,7 +1450,7 @@ class AlbumSerializer(MusicEntitySerializer):
acs.append(
utils.retrieve_ap_object(
ac["id"],
actor=self.context.get("fetch_actor"),
actor=self.context.get("_actor"),
queryset=music_models.ArtistCredit,
serializer_class=ArtistCreditSerializer,
)

View File

@ -454,7 +454,6 @@ def fetch(fetch_obj):
max_pages=settings.FEDERATION_COLLECTION_MAX_PAGES - 1,
is_page=True,
)
fetch_obj.object = obj
fetch_obj.status = "finished"
fetch_obj.fetch_date = timezone.now()

View File

@ -173,7 +173,12 @@ def test_fetch_serializer_with_object(
"actor": serializers.APIActorSerializer(fetch.actor).data,
}
assert api_serializers.FetchSerializer(fetch).data == expected
assert (
api_serializers.FetchSerializer(
fetch, context={"media_type": "application/activity+json"}
).data
== expected
)
def test_fetch_serializer_unhandled_obj(factories, to_api_date):

View File

@ -210,13 +210,19 @@ export default (props: PlayOptionsProps) => {
if (!id) {
throw new Error("Library id not found in response.");
}
const fetchResponse = await axios.post('federation/fetches',
{ object: id }
const fetchResponse = await axios.post(
'federation/fetches',
{ object_uri: id },
{
headers: {
Accept: 'application/activity+json'
}
}
);
const response = await axios.post(
'federation/follows/library',
{ target: fetchResponse.data.object.uuid }
{ target: fetchResponse.data.object.id.split('/').pop() }
);
return response;

View File

@ -10590,6 +10590,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/activity+json": components["schemas"]["Fetch"];
"application/json": components["schemas"]["Fetch"];
};
};
@ -10612,6 +10613,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/activity+json": components["schemas"]["Fetch"];
"application/json": components["schemas"]["Fetch"];
};
};
@ -17537,6 +17539,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/activity+json": components["schemas"]["Fetch"];
"application/json": components["schemas"]["Fetch"];
};
};
@ -17559,6 +17562,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/activity+json": components["schemas"]["Fetch"];
"application/json": components["schemas"]["Fetch"];
};
};