kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
See #223: api for listing/managing library files
rodzic
2569f136b7
commit
c7782693bc
|
@ -38,6 +38,10 @@ v1_patterns += [
|
||||||
include(
|
include(
|
||||||
('funkwhale_api.instance.urls', 'instance'),
|
('funkwhale_api.instance.urls', 'instance'),
|
||||||
namespace='instance')),
|
namespace='instance')),
|
||||||
|
url(r'^manage/',
|
||||||
|
include(
|
||||||
|
('funkwhale_api.manage.urls', 'manage'),
|
||||||
|
namespace='manage')),
|
||||||
url(r'^federation/',
|
url(r'^federation/',
|
||||||
include(
|
include(
|
||||||
('funkwhale_api.federation.api_urls', 'federation'),
|
('funkwhale_api.federation.api_urls', 'federation'),
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
App that includes all views/serializers and stuff for management API
|
||||||
|
"""
|
|
@ -0,0 +1,25 @@
|
||||||
|
from django.db.models import Count
|
||||||
|
|
||||||
|
from django_filters import rest_framework as filters
|
||||||
|
|
||||||
|
from funkwhale_api.common import fields
|
||||||
|
from funkwhale_api.music import models as music_models
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTrackFileFilterSet(filters.FilterSet):
|
||||||
|
q = fields.SearchFilter(search_fields=[
|
||||||
|
'track__title',
|
||||||
|
'track__album__title',
|
||||||
|
'track__artist__name',
|
||||||
|
'source',
|
||||||
|
])
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = music_models.TrackFile
|
||||||
|
fields = [
|
||||||
|
'q',
|
||||||
|
'track__album',
|
||||||
|
'track__artist',
|
||||||
|
'track',
|
||||||
|
'library_track'
|
||||||
|
]
|
|
@ -0,0 +1,81 @@
|
||||||
|
from django.db import transaction
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from funkwhale_api.common import serializers as common_serializers
|
||||||
|
from funkwhale_api.music import models as music_models
|
||||||
|
|
||||||
|
from . import filters
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTrackFileArtistSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = music_models.Artist
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'mbid',
|
||||||
|
'creation_date',
|
||||||
|
'name',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTrackFileAlbumSerializer(serializers.ModelSerializer):
|
||||||
|
artist = ManageTrackFileArtistSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = music_models.Album
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'mbid',
|
||||||
|
'title',
|
||||||
|
'artist',
|
||||||
|
'release_date',
|
||||||
|
'cover',
|
||||||
|
'creation_date',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTrackFileTrackSerializer(serializers.ModelSerializer):
|
||||||
|
artist = ManageTrackFileArtistSerializer()
|
||||||
|
album = ManageTrackFileAlbumSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = music_models.Track
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'mbid',
|
||||||
|
'title',
|
||||||
|
'album',
|
||||||
|
'artist',
|
||||||
|
'creation_date',
|
||||||
|
'position',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTrackFileSerializer(serializers.ModelSerializer):
|
||||||
|
track = ManageTrackFileTrackSerializer()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = music_models.TrackFile
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'path',
|
||||||
|
'source',
|
||||||
|
'filename',
|
||||||
|
'mimetype',
|
||||||
|
'track',
|
||||||
|
'duration',
|
||||||
|
'mimetype',
|
||||||
|
'bitrate',
|
||||||
|
'size',
|
||||||
|
'path',
|
||||||
|
'library_track',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTrackFileActionSerializer(common_serializers.ActionSerializer):
|
||||||
|
actions = ['delete']
|
||||||
|
filterset_class = filters.ManageTrackFileFilterSet
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def handle_delete(self, objects):
|
||||||
|
return objects.delete()
|
|
@ -0,0 +1,11 @@
|
||||||
|
from django.conf.urls import include, url
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
from rest_framework import routers
|
||||||
|
library_router = routers.SimpleRouter()
|
||||||
|
library_router.register(r'track-files', views.ManageTrackFileViewSet, 'track-files')
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^library/',
|
||||||
|
include((library_router.urls, 'instance'), namespace='library')),
|
||||||
|
]
|
|
@ -0,0 +1,49 @@
|
||||||
|
from rest_framework import mixins
|
||||||
|
from rest_framework import response
|
||||||
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.decorators import list_route
|
||||||
|
|
||||||
|
from funkwhale_api.music import models as music_models
|
||||||
|
from funkwhale_api.users.permissions import HasUserPermission
|
||||||
|
|
||||||
|
from . import filters
|
||||||
|
from . import serializers
|
||||||
|
|
||||||
|
|
||||||
|
class ManageTrackFileViewSet(
|
||||||
|
mixins.ListModelMixin,
|
||||||
|
mixins.RetrieveModelMixin,
|
||||||
|
mixins.DestroyModelMixin,
|
||||||
|
viewsets.GenericViewSet):
|
||||||
|
queryset = (
|
||||||
|
music_models.TrackFile.objects.all()
|
||||||
|
.select_related(
|
||||||
|
'track__artist',
|
||||||
|
'track__album__artist',
|
||||||
|
'library_track')
|
||||||
|
.order_by('-id')
|
||||||
|
)
|
||||||
|
serializer_class = serializers.ManageTrackFileSerializer
|
||||||
|
filter_class = filters.ManageTrackFileFilterSet
|
||||||
|
permission_classes = (HasUserPermission,)
|
||||||
|
required_permissions = ['library']
|
||||||
|
ordering_fields = [
|
||||||
|
'accessed_date',
|
||||||
|
'modification_date',
|
||||||
|
'creation_date',
|
||||||
|
'track__artist__name',
|
||||||
|
'bitrate',
|
||||||
|
'size',
|
||||||
|
'duration',
|
||||||
|
]
|
||||||
|
|
||||||
|
@list_route(methods=['post'])
|
||||||
|
def action(self, request, *args, **kwargs):
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
serializer = serializers.ManageTrackFileActionSerializer(
|
||||||
|
request.data,
|
||||||
|
queryset=queryset,
|
||||||
|
)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
result = serializer.save()
|
||||||
|
return response.Response(result, status=200)
|
|
@ -117,6 +117,11 @@ class ImportJobFactory(factory.django.DjangoModelFactory):
|
||||||
status='finished',
|
status='finished',
|
||||||
audio_file=None,
|
audio_file=None,
|
||||||
)
|
)
|
||||||
|
with_audio_file = factory.Trait(
|
||||||
|
status='finished',
|
||||||
|
audio_file=factory.django.FileField(
|
||||||
|
from_path=os.path.join(SAMPLES_PATH, 'test.ogg')),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@registry.register(name='music.FileImportJob')
|
@registry.register(name='music.FileImportJob')
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
from funkwhale_api.manage import serializers
|
||||||
|
|
||||||
|
|
||||||
|
def test_manage_track_file_action_delete(factories):
|
||||||
|
tfs = factories['music.TrackFile'](size=5)
|
||||||
|
s = serializers.ManageTrackFileActionSerializer(queryset=None)
|
||||||
|
|
||||||
|
s.handle_delete(tfs.__class__.objects.all())
|
||||||
|
|
||||||
|
assert tfs.__class__.objects.count() == 0
|
|
@ -0,0 +1,25 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from funkwhale_api.manage import serializers
|
||||||
|
from funkwhale_api.manage import views
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('view,permissions,operator', [
|
||||||
|
(views.ManageTrackFileViewSet, ['library'], 'and'),
|
||||||
|
])
|
||||||
|
def test_permissions(assert_user_permission, view, permissions, operator):
|
||||||
|
assert_user_permission(view, permissions, operator)
|
||||||
|
|
||||||
|
|
||||||
|
def test_track_file_view(factories, superuser_api_client):
|
||||||
|
tfs = factories['music.TrackFile'].create_batch(size=5)
|
||||||
|
qs = tfs[0].__class__.objects.order_by('-creation_date')
|
||||||
|
url = reverse('api:v1:manage:library:track-files-list')
|
||||||
|
|
||||||
|
response = superuser_api_client.get(url, {'sort': '-creation_date'})
|
||||||
|
expected = serializers.ManageTrackFileSerializer(qs, many=True).data
|
||||||
|
|
||||||
|
assert response.data['count'] == len(tfs)
|
||||||
|
assert response.data['results'] == expected
|
Ładowanie…
Reference in New Issue