See #170: fixed fetch of channel track not working

This commit is contained in:
Eliot Berriot 2020-04-08 11:47:34 +02:00
parent 1d37a2c819
commit e7622729a9
No known key found for this signature in database
GPG Key ID: 6B501DFD73514E14
7 changed files with 100 additions and 23 deletions

View File

@ -388,16 +388,19 @@ class Fetch(models.Model):
from . import serializers from . import serializers
return { return {
contexts.FW.Artist: serializers.ArtistSerializer, contexts.FW.Artist: [serializers.ArtistSerializer],
contexts.FW.Album: serializers.AlbumSerializer, contexts.FW.Album: [serializers.AlbumSerializer],
contexts.FW.Track: serializers.TrackSerializer, contexts.FW.Track: [serializers.TrackSerializer],
contexts.AS.Audio: serializers.UploadSerializer, contexts.AS.Audio: [
contexts.FW.Library: serializers.LibrarySerializer, serializers.UploadSerializer,
contexts.AS.Group: serializers.ActorSerializer, serializers.ChannelUploadSerializer,
contexts.AS.Person: serializers.ActorSerializer, ],
contexts.AS.Organization: serializers.ActorSerializer, contexts.FW.Library: [serializers.LibrarySerializer],
contexts.AS.Service: serializers.ActorSerializer, contexts.AS.Group: [serializers.ActorSerializer],
contexts.AS.Application: serializers.ActorSerializer, contexts.AS.Person: [serializers.ActorSerializer],
contexts.AS.Organization: [serializers.ActorSerializer],
contexts.AS.Service: [serializers.ActorSerializer],
contexts.AS.Application: [serializers.ActorSerializer],
} }

View File

@ -1766,6 +1766,7 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer):
disc = serializers.IntegerField(min_value=1, allow_null=True, required=False) disc = serializers.IntegerField(min_value=1, allow_null=True, required=False)
album = serializers.URLField(max_length=500, required=False) album = serializers.URLField(max_length=500, required=False)
license = serializers.URLField(allow_null=True, required=False) license = serializers.URLField(allow_null=True, required=False)
attributedTo = serializers.URLField(max_length=500, required=False)
copyright = TruncatedCharField( copyright = TruncatedCharField(
truncate_length=music_models.MAX_LENGTHS["COPYRIGHT"], truncate_length=music_models.MAX_LENGTHS["COPYRIGHT"],
allow_null=True, allow_null=True,
@ -1808,9 +1809,10 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer):
"position": jsonld.first_val(contexts.FW.position), "position": jsonld.first_val(contexts.FW.position),
"image": jsonld.first_obj(contexts.AS.image), "image": jsonld.first_obj(contexts.AS.image),
"tags": jsonld.raw(contexts.AS.tag), "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( return utils.retrieve_ap_object(
v, v,
actor=actors.get_service_actor(), actor=actors.get_service_actor(),
@ -1821,6 +1823,17 @@ class ChannelUploadSerializer(jsonld.JsonLdSerializer):
) )
def validate(self, data): 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) validated_data = super().validate(data)
if data.get("content"): if data.get("content"):
validated_data["description"] = { validated_data["description"] = {

View File

@ -374,8 +374,8 @@ def fetch(fetch_obj):
except IndexError: except IndexError:
return error("missing_jsonld_type") return error("missing_jsonld_type")
try: try:
serializer_class = fetch_obj.serializers[type] serializer_classes = fetch_obj.serializers[type]
model = serializer_class.Meta.model model = serializer_classes[0].Meta.model
except (KeyError, AttributeError): except (KeyError, AttributeError):
fetch_obj.status = "skipped" fetch_obj.status = "skipped"
fetch_obj.fetch_date = timezone.now() fetch_obj.fetch_date = timezone.now()
@ -388,8 +388,14 @@ def fetch(fetch_obj):
else: else:
existing = model.objects.filter(fid=id).first() existing = model.objects.filter(fid=id).first()
serializer = serializer_class(existing, data=payload) serializer = None
if not serializer.is_valid(): 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) return error("validation", validation_errors=serializer.errors)
try: try:
obj = serializer.save() obj = serializer.save()

View File

@ -185,6 +185,16 @@ class UploadFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
import_status="finished", library__privacy_level="everyone" 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 @registry.register
class UploadVersionFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory): class UploadVersionFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):

View File

@ -461,17 +461,25 @@ def test_fetch_rel_alternate(factories, r_mock, mocker):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"factory_name, serializer_class", "factory_name, factory_kwargs, serializer_class",
[ [
("federation.Actor", serializers.ActorSerializer), ("federation.Actor", {}, serializers.ActorSerializer),
("music.Library", serializers.LibrarySerializer), ("music.Library", {}, serializers.LibrarySerializer),
("music.Artist", serializers.ArtistSerializer), ("music.Artist", {}, serializers.ArtistSerializer),
("music.Album", serializers.AlbumSerializer), ("music.Album", {}, serializers.AlbumSerializer),
("music.Track", serializers.TrackSerializer), ("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): def test_fetch_url(
obj = factories[factory_name]() factory_name, factory_kwargs, serializer_class, factories, r_mock, mocker
):
obj = factories[factory_name](**factory_kwargs)
fetch = factories["federation.Fetch"](url=obj.fid) fetch = factories["federation.Fetch"](url=obj.fid)
payload = serializer_class(obj).data payload = serializer_class(obj).data
init = mocker.spy(serializer_class, "__init__") init = mocker.spy(serializer_class, "__init__")

View File

@ -0,0 +1,28 @@
<template>
<main>
<div v-if="isLoading" class="ui vertical segment">
<div class="ui centered active inline loader"></div>
</div>
</main>
</template>
<script>
import axios from "axios"
export default {
props: ["id"],
async created() {
let upload = await this.fetchData()
this.$router.replace({name: "library.tracks.detail", params: {id: upload.track.id}})
},
methods: {
async fetchData() {
this.isLoading = true
let response = await axios.get(`uploads/${this.id}/`, {params: {refresh: 'true', include_channels: 'true'}})
this.isLoading = false
return response.data
},
}
}
</script>

View File

@ -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 // 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})", 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})",