From e7622729a9f2f642ff08a54d9ec161010c577281 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Wed, 8 Apr 2020 11:47:34 +0200 Subject: [PATCH] See #170: fixed fetch of channel track not working --- api/funkwhale_api/federation/models.py | 23 ++++++++------- api/funkwhale_api/federation/serializers.py | 15 +++++++++- api/funkwhale_api/federation/tasks.py | 14 +++++++--- api/funkwhale_api/music/factories.py | 10 +++++++ api/tests/federation/test_tasks.py | 24 ++++++++++------ front/src/components/library/UploadDetail.vue | 28 +++++++++++++++++++ front/src/router/index.js | 9 ++++++ 7 files changed, 100 insertions(+), 23 deletions(-) create mode 100644 front/src/components/library/UploadDetail.vue diff --git a/api/funkwhale_api/federation/models.py b/api/funkwhale_api/federation/models.py index 7f8121641..fa285f3cf 100644 --- a/api/funkwhale_api/federation/models.py +++ b/api/funkwhale_api/federation/models.py @@ -388,16 +388,19 @@ class Fetch(models.Model): from . import serializers return { - contexts.FW.Artist: serializers.ArtistSerializer, - contexts.FW.Album: serializers.AlbumSerializer, - contexts.FW.Track: serializers.TrackSerializer, - contexts.AS.Audio: serializers.UploadSerializer, - contexts.FW.Library: serializers.LibrarySerializer, - contexts.AS.Group: serializers.ActorSerializer, - contexts.AS.Person: serializers.ActorSerializer, - contexts.AS.Organization: serializers.ActorSerializer, - contexts.AS.Service: serializers.ActorSerializer, - contexts.AS.Application: serializers.ActorSerializer, + contexts.FW.Artist: [serializers.ArtistSerializer], + contexts.FW.Album: [serializers.AlbumSerializer], + contexts.FW.Track: [serializers.TrackSerializer], + contexts.AS.Audio: [ + serializers.UploadSerializer, + serializers.ChannelUploadSerializer, + ], + contexts.FW.Library: [serializers.LibrarySerializer], + contexts.AS.Group: [serializers.ActorSerializer], + contexts.AS.Person: [serializers.ActorSerializer], + contexts.AS.Organization: [serializers.ActorSerializer], + contexts.AS.Service: [serializers.ActorSerializer], + contexts.AS.Application: [serializers.ActorSerializer], } diff --git a/api/funkwhale_api/federation/serializers.py b/api/funkwhale_api/federation/serializers.py index 313f225f7..33ecec3ab 100644 --- a/api/funkwhale_api/federation/serializers.py +++ b/api/funkwhale_api/federation/serializers.py @@ -1766,6 +1766,7 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer): disc = serializers.IntegerField(min_value=1, allow_null=True, required=False) album = serializers.URLField(max_length=500, required=False) license = serializers.URLField(allow_null=True, required=False) + attributedTo = serializers.URLField(max_length=500, required=False) copyright = TruncatedCharField( truncate_length=music_models.MAX_LENGTHS["COPYRIGHT"], allow_null=True, @@ -1808,9 +1809,10 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer): "position": jsonld.first_val(contexts.FW.position), "image": jsonld.first_obj(contexts.AS.image), "tags": jsonld.raw(contexts.AS.tag), + "attributedTo": jsonld.first_id(contexts.AS.attributedTo), } - def validate_album(self, v): + def _validate_album(self, v): return utils.retrieve_ap_object( v, actor=actors.get_service_actor(), @@ -1821,6 +1823,17 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer): ) def validate(self, data): + if not self.context.get("channel"): + if not data.get("attributedTo"): + raise serializers.ValidationError( + "Missing channel context and no attributedTo available" + ) + actor = actors.get_actor(data["attributedTo"]) + if not actor.get_channel(): + raise serializers.ValidationError("Not a channel") + self.context["channel"] = actor.get_channel() + if data.get("album"): + data["album"] = self._validate_album(data["album"]) validated_data = super().validate(data) if data.get("content"): validated_data["description"] = { diff --git a/api/funkwhale_api/federation/tasks.py b/api/funkwhale_api/federation/tasks.py index b84e4c5f9..6ab04db0d 100644 --- a/api/funkwhale_api/federation/tasks.py +++ b/api/funkwhale_api/federation/tasks.py @@ -374,8 +374,8 @@ def fetch(fetch_obj): except IndexError: return error("missing_jsonld_type") try: - serializer_class = fetch_obj.serializers[type] - model = serializer_class.Meta.model + serializer_classes = fetch_obj.serializers[type] + model = serializer_classes[0].Meta.model except (KeyError, AttributeError): fetch_obj.status = "skipped" fetch_obj.fetch_date = timezone.now() @@ -388,8 +388,14 @@ def fetch(fetch_obj): else: existing = model.objects.filter(fid=id).first() - serializer = serializer_class(existing, data=payload) - if not serializer.is_valid(): + serializer = None + for serializer_class in serializer_classes: + serializer = serializer_class(existing, data=payload) + if not serializer.is_valid(): + continue + else: + break + if serializer.errors: return error("validation", validation_errors=serializer.errors) try: obj = serializer.save() diff --git a/api/funkwhale_api/music/factories.py b/api/funkwhale_api/music/factories.py index 8537c5c50..14bcecb86 100644 --- a/api/funkwhale_api/music/factories.py +++ b/api/funkwhale_api/music/factories.py @@ -185,6 +185,16 @@ class UploadFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory): import_status="finished", library__privacy_level="everyone" ) + @factory.post_generation + def channel(self, created, extracted, **kwargs): + if not extracted: + return + from funkwhale_api.audio import factories as audio_factories + + audio_factories.ChannelFactory( + library=self.library, artist=self.track.artist, **kwargs + ) + @registry.register class UploadVersionFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory): diff --git a/api/tests/federation/test_tasks.py b/api/tests/federation/test_tasks.py index ce4736503..79573ff45 100644 --- a/api/tests/federation/test_tasks.py +++ b/api/tests/federation/test_tasks.py @@ -461,17 +461,25 @@ def test_fetch_rel_alternate(factories, r_mock, mocker): @pytest.mark.parametrize( - "factory_name, serializer_class", + "factory_name, factory_kwargs, serializer_class", [ - ("federation.Actor", serializers.ActorSerializer), - ("music.Library", serializers.LibrarySerializer), - ("music.Artist", serializers.ArtistSerializer), - ("music.Album", serializers.AlbumSerializer), - ("music.Track", serializers.TrackSerializer), + ("federation.Actor", {}, serializers.ActorSerializer), + ("music.Library", {}, serializers.LibrarySerializer), + ("music.Artist", {}, serializers.ArtistSerializer), + ("music.Album", {}, serializers.AlbumSerializer), + ("music.Track", {}, serializers.TrackSerializer), + ( + "music.Upload", + {"bitrate": 200, "duration": 20}, + serializers.UploadSerializer, + ), + ("music.Upload", {"channel": True}, serializers.ChannelUploadSerializer), ], ) -def test_fetch_url(factory_name, serializer_class, factories, r_mock, mocker): - obj = factories[factory_name]() +def test_fetch_url( + factory_name, factory_kwargs, serializer_class, factories, r_mock, mocker +): + obj = factories[factory_name](**factory_kwargs) fetch = factories["federation.Fetch"](url=obj.fid) payload = serializer_class(obj).data init = mocker.spy(serializer_class, "__init__") diff --git a/front/src/components/library/UploadDetail.vue b/front/src/components/library/UploadDetail.vue new file mode 100644 index 000000000..ce3a1e3a8 --- /dev/null +++ b/front/src/components/library/UploadDetail.vue @@ -0,0 +1,28 @@ + + + diff --git a/front/src/router/index.js b/front/src/router/index.js index 4581dd0d7..5c6a42e24 100644 --- a/front/src/router/index.js +++ b/front/src/router/index.js @@ -838,6 +838,15 @@ export default new Router({ } ] }, + { + path: "uploads/:id", + name: "library.uploads.detail", + props: true, + component: () => + import( + /* webpackChunkName: "uploads" */ "@/components/library/UploadDetail" + ), + }, { // browse a single library via it's uuid path: ":id([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})",