mirror of
https://code.eliotberriot.com/funkwhale/funkwhale.git
synced 2025-10-05 14:29:42 +02:00
Resolve "Per-user libraries" (use !368 instead)
This commit is contained in:
parent
b0ca181016
commit
2ea21994ee
144 changed files with 6749 additions and 5347 deletions
|
@ -1,12 +1,13 @@
|
|||
from django.db.models import Q
|
||||
from django.db import transaction
|
||||
from rest_framework import serializers
|
||||
from taggit.models import Tag
|
||||
from versatileimagefield.serializers import VersatileImageFieldSerializer
|
||||
|
||||
from funkwhale_api.activity import serializers as activity_serializers
|
||||
from funkwhale_api.users.serializers import UserBasicSerializer
|
||||
from funkwhale_api.common import serializers as common_serializers
|
||||
from funkwhale_api.common import utils as common_utils
|
||||
|
||||
from . import models, tasks
|
||||
from . import filters, models, tasks
|
||||
|
||||
|
||||
cover_field = VersatileImageFieldSerializer(allow_null=True, sizes="square")
|
||||
|
@ -15,6 +16,7 @@ cover_field = VersatileImageFieldSerializer(allow_null=True, sizes="square")
|
|||
class ArtistAlbumSerializer(serializers.ModelSerializer):
|
||||
tracks_count = serializers.SerializerMethodField()
|
||||
cover = cover_field
|
||||
is_playable = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.Album
|
||||
|
@ -27,11 +29,18 @@ class ArtistAlbumSerializer(serializers.ModelSerializer):
|
|||
"cover",
|
||||
"creation_date",
|
||||
"tracks_count",
|
||||
"is_playable",
|
||||
)
|
||||
|
||||
def get_tracks_count(self, o):
|
||||
return o._tracks_count
|
||||
|
||||
def get_is_playable(self, obj):
|
||||
try:
|
||||
return bool(obj.is_playable_by_actor)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
|
||||
class ArtistWithAlbumsSerializer(serializers.ModelSerializer):
|
||||
albums = ArtistAlbumSerializer(many=True, read_only=True)
|
||||
|
@ -41,30 +50,6 @@ class ArtistWithAlbumsSerializer(serializers.ModelSerializer):
|
|||
fields = ("id", "mbid", "name", "creation_date", "albums")
|
||||
|
||||
|
||||
class TrackFileSerializer(serializers.ModelSerializer):
|
||||
path = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.TrackFile
|
||||
fields = (
|
||||
"id",
|
||||
"path",
|
||||
"source",
|
||||
"filename",
|
||||
"mimetype",
|
||||
"track",
|
||||
"duration",
|
||||
"mimetype",
|
||||
"bitrate",
|
||||
"size",
|
||||
)
|
||||
read_only_fields = ["duration", "mimetype", "bitrate", "size"]
|
||||
|
||||
def get_path(self, o):
|
||||
url = o.path
|
||||
return url
|
||||
|
||||
|
||||
class ArtistSimpleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.Artist
|
||||
|
@ -72,8 +57,9 @@ class ArtistSimpleSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class AlbumTrackSerializer(serializers.ModelSerializer):
|
||||
files = TrackFileSerializer(many=True, read_only=True)
|
||||
artist = ArtistSimpleSerializer(read_only=True)
|
||||
is_playable = serializers.SerializerMethodField()
|
||||
listen_url = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.Track
|
||||
|
@ -84,15 +70,26 @@ class AlbumTrackSerializer(serializers.ModelSerializer):
|
|||
"album",
|
||||
"artist",
|
||||
"creation_date",
|
||||
"files",
|
||||
"position",
|
||||
"is_playable",
|
||||
"listen_url",
|
||||
)
|
||||
|
||||
def get_is_playable(self, obj):
|
||||
try:
|
||||
return bool(obj.is_playable_by_actor)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def get_listen_url(self, obj):
|
||||
return obj.listen_url
|
||||
|
||||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
tracks = serializers.SerializerMethodField()
|
||||
artist = ArtistSimpleSerializer(read_only=True)
|
||||
cover = cover_field
|
||||
is_playable = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.Album
|
||||
|
@ -105,6 +102,7 @@ class AlbumSerializer(serializers.ModelSerializer):
|
|||
"release_date",
|
||||
"cover",
|
||||
"creation_date",
|
||||
"is_playable",
|
||||
)
|
||||
|
||||
def get_tracks(self, o):
|
||||
|
@ -114,6 +112,12 @@ class AlbumSerializer(serializers.ModelSerializer):
|
|||
)
|
||||
return AlbumTrackSerializer(ordered_tracks, many=True).data
|
||||
|
||||
def get_is_playable(self, obj):
|
||||
try:
|
||||
return any([bool(t.is_playable_by_actor) for t in obj.tracks.all()])
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
|
||||
class TrackAlbumSerializer(serializers.ModelSerializer):
|
||||
artist = ArtistSimpleSerializer(read_only=True)
|
||||
|
@ -133,10 +137,11 @@ class TrackAlbumSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class TrackSerializer(serializers.ModelSerializer):
|
||||
files = TrackFileSerializer(many=True, read_only=True)
|
||||
artist = ArtistSimpleSerializer(read_only=True)
|
||||
album = TrackAlbumSerializer(read_only=True)
|
||||
lyrics = serializers.SerializerMethodField()
|
||||
is_playable = serializers.SerializerMethodField()
|
||||
listen_url = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.Track
|
||||
|
@ -147,14 +152,146 @@ class TrackSerializer(serializers.ModelSerializer):
|
|||
"album",
|
||||
"artist",
|
||||
"creation_date",
|
||||
"files",
|
||||
"position",
|
||||
"lyrics",
|
||||
"is_playable",
|
||||
"listen_url",
|
||||
)
|
||||
|
||||
def get_lyrics(self, obj):
|
||||
return obj.get_lyrics_url()
|
||||
|
||||
def get_listen_url(self, obj):
|
||||
return obj.listen_url
|
||||
|
||||
def get_is_playable(self, obj):
|
||||
try:
|
||||
return bool(obj.is_playable_by_actor)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
|
||||
class LibraryForOwnerSerializer(serializers.ModelSerializer):
|
||||
files_count = serializers.SerializerMethodField()
|
||||
size = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.Library
|
||||
fields = [
|
||||
"uuid",
|
||||
"fid",
|
||||
"name",
|
||||
"description",
|
||||
"privacy_level",
|
||||
"files_count",
|
||||
"size",
|
||||
"creation_date",
|
||||
]
|
||||
read_only_fields = ["fid", "uuid", "creation_date", "actor"]
|
||||
|
||||
def get_files_count(self, o):
|
||||
return getattr(o, "_files_count", o.files_count)
|
||||
|
||||
def get_size(self, o):
|
||||
return getattr(o, "_size", 0)
|
||||
|
||||
|
||||
class TrackFileSerializer(serializers.ModelSerializer):
|
||||
track = TrackSerializer(required=False, allow_null=True)
|
||||
library = common_serializers.RelatedField(
|
||||
"uuid",
|
||||
LibraryForOwnerSerializer(),
|
||||
required=True,
|
||||
filters=lambda context: {"actor": context["user"].actor},
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.TrackFile
|
||||
fields = [
|
||||
"uuid",
|
||||
"filename",
|
||||
"creation_date",
|
||||
"mimetype",
|
||||
"track",
|
||||
"library",
|
||||
"duration",
|
||||
"mimetype",
|
||||
"bitrate",
|
||||
"size",
|
||||
"import_date",
|
||||
"import_status",
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
"uuid",
|
||||
"creation_date",
|
||||
"duration",
|
||||
"mimetype",
|
||||
"bitrate",
|
||||
"size",
|
||||
"track",
|
||||
"import_date",
|
||||
"import_status",
|
||||
]
|
||||
|
||||
|
||||
class TrackFileForOwnerSerializer(TrackFileSerializer):
|
||||
class Meta(TrackFileSerializer.Meta):
|
||||
fields = TrackFileSerializer.Meta.fields + [
|
||||
"import_details",
|
||||
"import_metadata",
|
||||
"import_reference",
|
||||
"metadata",
|
||||
"source",
|
||||
"audio_file",
|
||||
]
|
||||
write_only_fields = ["audio_file"]
|
||||
read_only_fields = TrackFileSerializer.Meta.read_only_fields + [
|
||||
"import_details",
|
||||
"import_metadata",
|
||||
"metadata",
|
||||
]
|
||||
|
||||
def to_representation(self, obj):
|
||||
r = super().to_representation(obj)
|
||||
if "audio_file" in r:
|
||||
del r["audio_file"]
|
||||
return r
|
||||
|
||||
def validate(self, validated_data):
|
||||
if "audio_file" in validated_data:
|
||||
self.validate_upload_quota(validated_data["audio_file"])
|
||||
|
||||
return super().validate(validated_data)
|
||||
|
||||
def validate_upload_quota(self, f):
|
||||
quota_status = self.context["user"].get_quota_status()
|
||||
if (f.size / 1000 / 1000) > quota_status["remaining"]:
|
||||
raise serializers.ValidationError("upload_quota_reached")
|
||||
|
||||
return f
|
||||
|
||||
|
||||
class TrackFileActionSerializer(common_serializers.ActionSerializer):
|
||||
actions = [
|
||||
common_serializers.Action("delete", allow_all=True),
|
||||
common_serializers.Action("relaunch_import", allow_all=True),
|
||||
]
|
||||
filterset_class = filters.TrackFileFilter
|
||||
pk_field = "uuid"
|
||||
|
||||
@transaction.atomic
|
||||
def handle_delete(self, objects):
|
||||
return objects.delete()
|
||||
|
||||
@transaction.atomic
|
||||
def handle_relaunch_import(self, objects):
|
||||
qs = objects.exclude(import_status="finished")
|
||||
pks = list(qs.values_list("id", flat=True))
|
||||
qs.update(import_status="pending")
|
||||
for pk in pks:
|
||||
common_utils.on_commit(tasks.import_track_file.delay, track_file_id=pk)
|
||||
|
||||
|
||||
class TagSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
@ -176,40 +313,6 @@ class LyricsSerializer(serializers.ModelSerializer):
|
|||
fields = ("id", "work", "content", "content_rendered")
|
||||
|
||||
|
||||
class ImportJobSerializer(serializers.ModelSerializer):
|
||||
track_file = TrackFileSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.ImportJob
|
||||
fields = ("id", "mbid", "batch", "source", "status", "track_file", "audio_file")
|
||||
read_only_fields = ("status", "track_file")
|
||||
|
||||
|
||||
class ImportBatchSerializer(serializers.ModelSerializer):
|
||||
submitted_by = UserBasicSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.ImportBatch
|
||||
fields = (
|
||||
"id",
|
||||
"submitted_by",
|
||||
"source",
|
||||
"status",
|
||||
"creation_date",
|
||||
"import_request",
|
||||
)
|
||||
read_only_fields = ("creation_date", "submitted_by", "source")
|
||||
|
||||
def to_representation(self, instance):
|
||||
repr = super().to_representation(instance)
|
||||
try:
|
||||
repr["job_count"] = instance.job_count
|
||||
except AttributeError:
|
||||
# Queryset was not annotated
|
||||
pass
|
||||
return repr
|
||||
|
||||
|
||||
class TrackActivitySerializer(activity_serializers.ModelSerializer):
|
||||
type = serializers.SerializerMethodField()
|
||||
name = serializers.CharField(source="title")
|
||||
|
@ -222,33 +325,3 @@ class TrackActivitySerializer(activity_serializers.ModelSerializer):
|
|||
|
||||
def get_type(self, obj):
|
||||
return "Audio"
|
||||
|
||||
|
||||
class ImportJobRunSerializer(serializers.Serializer):
|
||||
jobs = serializers.PrimaryKeyRelatedField(
|
||||
many=True,
|
||||
queryset=models.ImportJob.objects.filter(status__in=["pending", "errored"]),
|
||||
)
|
||||
batches = serializers.PrimaryKeyRelatedField(
|
||||
many=True, queryset=models.ImportBatch.objects.all()
|
||||
)
|
||||
|
||||
def validate(self, validated_data):
|
||||
jobs = validated_data["jobs"]
|
||||
batches_ids = [b.pk for b in validated_data["batches"]]
|
||||
query = Q(batch__pk__in=batches_ids)
|
||||
query |= Q(pk__in=[j.id for j in jobs])
|
||||
queryset = (
|
||||
models.ImportJob.objects.filter(query)
|
||||
.filter(status__in=["pending", "errored"])
|
||||
.distinct()
|
||||
)
|
||||
validated_data["_jobs"] = queryset
|
||||
return validated_data
|
||||
|
||||
def create(self, validated_data):
|
||||
ids = validated_data["_jobs"].values_list("id", flat=True)
|
||||
validated_data["_jobs"].update(status="pending")
|
||||
for id in ids:
|
||||
tasks.import_job_run.delay(import_job_id=id)
|
||||
return {"jobs": list(ids)}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue