Merge branch '170-fetch-track' into 'develop'
See #170: fixed fetch of channel track not working See merge request funkwhale/funkwhale!1080
This commit is contained in:
commit
51a44d6d5a
|
@ -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],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1780,6 +1780,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,
|
||||||
|
@ -1823,9 +1824,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(),
|
||||||
|
@ -1836,6 +1838,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"] = {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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__")
|
||||||
|
|
|
@ -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>
|
|
@ -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})",
|
||||||
|
|
Loading…
Reference in New Issue