From 57bf43bb961c38e259688b830c5a407703221b85 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Thu, 12 Apr 2018 20:38:06 +0200 Subject: [PATCH] API Endpoint to list lirary tracks --- api/funkwhale_api/federation/api_urls.py | 4 +++ api/funkwhale_api/federation/filters.py | 14 ++++++++ api/funkwhale_api/federation/serializers.py | 23 +++++++++++++ api/funkwhale_api/federation/tasks.py | 4 +++ api/funkwhale_api/federation/views.py | 37 ++++++++++++++++++--- api/tests/federation/test_tasks.py | 4 +++ api/tests/federation/test_views.py | 19 ++++++++++- 7 files changed, 100 insertions(+), 5 deletions(-) diff --git a/api/funkwhale_api/federation/api_urls.py b/api/funkwhale_api/federation/api_urls.py index ecb5c38f1..41dd1c0f9 100644 --- a/api/funkwhale_api/federation/api_urls.py +++ b/api/funkwhale_api/federation/api_urls.py @@ -7,5 +7,9 @@ router.register( r'libraries', views.LibraryViewSet, 'libraries') +router.register( + r'library-tracks', + views.LibraryTrackViewSet, + 'library-tracks') urlpatterns = router.urls diff --git a/api/funkwhale_api/federation/filters.py b/api/funkwhale_api/federation/filters.py index d76fe7ad0..8166fe64d 100644 --- a/api/funkwhale_api/federation/filters.py +++ b/api/funkwhale_api/federation/filters.py @@ -17,6 +17,20 @@ class LibraryFilter(django_filters.FilterSet): } +class LibraryTrackFilter(django_filters.FilterSet): + library = django_filters.CharFilter('library__uuid') + + class Meta: + model = models.LibraryTrack + fields = { + 'library': ['exact'], + 'artist_name': ['exact', 'icontains'], + 'title': ['exact', 'icontains'], + 'album_title': ['exact', 'icontains'], + 'audio_mimetype': ['exact', 'icontains'], + } + + class FollowFilter(django_filters.FilterSet): ordering = django_filters.OrderingFilter( # tuple-mapping retains order diff --git a/api/funkwhale_api/federation/serializers.py b/api/funkwhale_api/federation/serializers.py index 25ed8d920..e6ad0c0be 100644 --- a/api/funkwhale_api/federation/serializers.py +++ b/api/funkwhale_api/federation/serializers.py @@ -253,6 +253,29 @@ class APILibraryCreateSerializer(serializers.ModelSerializer): return library +class APILibraryTrackSerializer(serializers.ModelSerializer): + library = APILibrarySerializer() + + class Meta: + model = models.LibraryTrack + fields = [ + 'id', + 'url', + 'audio_url', + 'audio_mimetype', + 'creation_date', + 'modification_date', + 'fetched_date', + 'published_date', + 'metadata', + 'artist_name', + 'album_title', + 'title', + 'library', + 'local_track_file', + ] + + class FollowSerializer(serializers.Serializer): id = serializers.URLField() object = serializers.URLField() diff --git a/api/funkwhale_api/federation/tasks.py b/api/funkwhale_api/federation/tasks.py index 5140eff6e..c6a70174f 100644 --- a/api/funkwhale_api/federation/tasks.py +++ b/api/funkwhale_api/federation/tasks.py @@ -2,6 +2,7 @@ import json import logging from django.conf import settings +from django.utils import timezone from requests.exceptions import RequestException @@ -58,6 +59,9 @@ def scan_library(library, until=None): data = lb.get_library_data(library.url) scan_library_page.delay( library_id=library.id, page_url=data['first'], until=until) + library.fetched_date = timezone.now() + library.tracks_count = data['totalItems'] + library.save(update_fields=['fetched_date', 'tracks_count']) @celery.app.task( diff --git a/api/funkwhale_api/federation/views.py b/api/funkwhale_api/federation/views.py index 1aaddf96d..a3f02a372 100644 --- a/api/funkwhale_api/federation/views.py +++ b/api/funkwhale_api/federation/views.py @@ -14,6 +14,7 @@ from rest_framework.decorators import list_route, detail_route from rest_framework.serializers import ValidationError from funkwhale_api.common import utils as funkwhale_utils +from funkwhale_api.common.permissions import HasModelPermission from funkwhale_api.music.models import TrackFile from . import activity @@ -166,12 +167,16 @@ class MusicFilesViewSet(FederationMixin, viewsets.GenericViewSet): return response.Response(data) +class LibraryPermission(HasModelPermission): + model = models.Library + + class LibraryViewSet( mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): - permission_classes = [rest_permissions.DjangoModelPermissions] + permission_classes = [LibraryPermission] queryset = models.Library.objects.all().select_related( 'actor', 'follow', @@ -184,6 +189,7 @@ class LibraryViewSet( 'creation_date', 'fetched_date', 'actor__domain', + 'tracks_count', ) @list_route(methods=['get']) @@ -203,11 +209,11 @@ class LibraryViewSet( data=request.data ) serializer.is_valid(raise_exception=True) - id = tasks.scan_library.delay( + result = tasks.scan_library.delay( library_id=library.pk, - until=serializer.validated_data['until'] + until=serializer.validated_data.get('until') ) - return response.Response({'task': id}) + return response.Response({'task': result.id}) @list_route(methods=['get']) def following(self, request, *args, **kwargs): @@ -249,3 +255,26 @@ class LibraryViewSet( serializer.is_valid(raise_exception=True) library = serializer.save() return response.Response(serializer.data, status=201) + + +class LibraryTrackViewSet( + mixins.ListModelMixin, + viewsets.GenericViewSet): + permission_classes = [LibraryPermission] + queryset = models.LibraryTrack.objects.all().select_related( + 'library__actor', + 'library__follow', + 'local_track_file', + ) + filter_class = filters.LibraryTrackFilter + serializer_class = serializers.APILibraryTrackSerializer + ordering_fields = ( + 'id', + 'artist_name', + 'title', + 'album_title', + 'creation_date', + 'modification_date', + 'fetched_date', + 'published_date', + ) diff --git a/api/tests/federation/test_tasks.py b/api/tests/federation/test_tasks.py index 80f2365b6..d164d62a6 100644 --- a/api/tests/federation/test_tasks.py +++ b/api/tests/federation/test_tasks.py @@ -1,4 +1,5 @@ from django.core.paginator import Paginator +from django.utils import timezone from funkwhale_api.federation import serializers from funkwhale_api.federation import tasks @@ -21,6 +22,7 @@ def test_scan_library_page_does_nothing_if_federation_disabled( def test_scan_library_fetches_page_and_calls_scan_page( mocker, factories, r_mock): + now = timezone.now() library = factories['federation.Library'](federation_enabled=True) collection_conf = { 'actor': library.actor, @@ -39,6 +41,8 @@ def test_scan_library_fetches_page_and_calls_scan_page( page_url=collection.data['first'], until=None, ) + library.refresh_from_db() + assert library.fetched_date > now def test_scan_page_fetches_page_and_creates_tracks( diff --git a/api/tests/federation/test_views.py b/api/tests/federation/test_views.py index c54cc873e..3e5bdf1a5 100644 --- a/api/tests/federation/test_views.py +++ b/api/tests/federation/test_views.py @@ -312,7 +312,7 @@ def test_can_patch_library(factories, superuser_api_client): def test_scan_library(factories, mocker, superuser_api_client): scan = mocker.patch( 'funkwhale_api.federation.tasks.scan_library.delay', - return_value='id') + return_value=mocker.Mock(id='id')) library = factories['federation.Library']() now = timezone.now() data = { @@ -329,3 +329,20 @@ def test_scan_library(factories, mocker, superuser_api_client): library_id=library.pk, until=now ) + + +def test_list_library_tracks(factories, superuser_api_client): + library = factories['federation.Library']() + lts = list(reversed(factories['federation.LibraryTrack'].create_batch( + size=5, library=library))) + factories['federation.LibraryTrack'].create_batch(size=5) + url = reverse('api:v1:federation:library-tracks-list') + response = superuser_api_client.get(url, {'library': library.uuid}) + + assert response.status_code == 200 + assert response.data == { + 'results': serializers.APILibraryTrackSerializer(lts, many=True).data, + 'count': 5, + 'previous': None, + 'next': None, + }