mirror of
https://code.eliotberriot.com/funkwhale/funkwhale.git
synced 2025-10-06 09:49:55 +02:00
Brand new file importer
This commit is contained in:
parent
2e616282fd
commit
1c8f055490
16 changed files with 302 additions and 15 deletions
|
@ -15,6 +15,7 @@ router.register(r'trackfiles', views.TrackFileViewSet, 'trackfiles')
|
|||
router.register(r'artists', views.ArtistViewSet, 'artists')
|
||||
router.register(r'albums', views.AlbumViewSet, 'albums')
|
||||
router.register(r'import-batches', views.ImportBatchViewSet, 'import-batches')
|
||||
router.register(r'import-jobs', views.ImportJobViewSet, 'import-jobs')
|
||||
router.register(r'submit', views.SubmitViewSet, 'submit')
|
||||
router.register(r'playlists', playlists_views.PlaylistViewSet, 'playlists')
|
||||
router.register(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.conf import settings
|
||||
|
||||
from rest_framework.permissions import BasePermission
|
||||
from rest_framework.permissions import BasePermission, DjangoModelPermissions
|
||||
|
||||
|
||||
class ConditionalAuthentication(BasePermission):
|
||||
|
@ -9,3 +9,14 @@ class ConditionalAuthentication(BasePermission):
|
|||
if settings.API_AUTHENTICATION_REQUIRED:
|
||||
return request.user and request.user.is_authenticated
|
||||
return True
|
||||
|
||||
|
||||
class HasModelPermission(DjangoModelPermissions):
|
||||
"""
|
||||
Same as DjangoModelPermissions, but we pin the model:
|
||||
|
||||
class MyModelPermission(HasModelPermission):
|
||||
model = User
|
||||
"""
|
||||
def get_required_permissions(self, method, model_cls):
|
||||
return super().get_required_permissions(method, self.model)
|
||||
|
|
|
@ -405,8 +405,11 @@ class ImportBatch(models.Model):
|
|||
@property
|
||||
def status(self):
|
||||
pending = any([job.status == 'pending' for job in self.jobs.all()])
|
||||
errored = any([job.status == 'errored' for job in self.jobs.all()])
|
||||
if pending:
|
||||
return 'pending'
|
||||
if errored:
|
||||
return 'errored'
|
||||
return 'finished'
|
||||
|
||||
class ImportJob(models.Model):
|
||||
|
@ -418,7 +421,7 @@ class ImportJob(models.Model):
|
|||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.CASCADE)
|
||||
source = models.URLField()
|
||||
source = models.CharField(max_length=500)
|
||||
mbid = models.UUIDField(editable=False, null=True, blank=True)
|
||||
STATUS_CHOICES = (
|
||||
('pending', 'Pending'),
|
||||
|
|
|
@ -113,7 +113,8 @@ class ImportJobSerializer(serializers.ModelSerializer):
|
|||
track_file = TrackFileSerializer(read_only=True)
|
||||
class Meta:
|
||||
model = models.ImportJob
|
||||
fields = ('id', 'mbid', 'source', 'status', 'track_file')
|
||||
fields = ('id', 'mbid', 'batch', 'source', 'status', 'track_file', 'audio_file')
|
||||
read_only_fields = ('status', 'track_file')
|
||||
|
||||
|
||||
class ImportBatchSerializer(serializers.ModelSerializer):
|
||||
|
@ -121,3 +122,4 @@ class ImportBatchSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = models.ImportBatch
|
||||
fields = ('id', 'jobs', 'status', 'creation_date')
|
||||
read_only_fields = ('creation_date',)
|
||||
|
|
|
@ -2,6 +2,7 @@ from django.core.files.base import ContentFile
|
|||
|
||||
from funkwhale_api.taskapp import celery
|
||||
from funkwhale_api.providers.acoustid import get_acoustid_client
|
||||
from funkwhale_api.providers.audiofile.tasks import import_track_data_from_path
|
||||
|
||||
from django.conf import settings
|
||||
from . import models
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.urls import reverse
|
|||
from django.db import models, transaction
|
||||
from django.db.models.functions import Length
|
||||
from django.conf import settings
|
||||
from rest_framework import viewsets, views
|
||||
from rest_framework import viewsets, views, mixins
|
||||
from rest_framework.decorators import detail_route, list_route
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions
|
||||
|
@ -15,7 +15,8 @@ from django.contrib.auth.decorators import login_required
|
|||
from django.utils.decorators import method_decorator
|
||||
|
||||
from funkwhale_api.musicbrainz import api
|
||||
from funkwhale_api.common.permissions import ConditionalAuthentication
|
||||
from funkwhale_api.common.permissions import (
|
||||
ConditionalAuthentication, HasModelPermission)
|
||||
from taggit.models import Tag
|
||||
|
||||
from . import models
|
||||
|
@ -71,16 +72,45 @@ class AlbumViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
|
|||
ordering_fields = ('creation_date',)
|
||||
|
||||
|
||||
class ImportBatchViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
class ImportBatchViewSet(
|
||||
mixins.CreateModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
queryset = (
|
||||
models.ImportBatch.objects.all()
|
||||
.prefetch_related('jobs__track_file')
|
||||
.order_by('-creation_date'))
|
||||
serializer_class = serializers.ImportBatchSerializer
|
||||
permission_classes = (permissions.DjangoModelPermissions, )
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(submitted_by=self.request.user)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(submitted_by=self.request.user)
|
||||
|
||||
|
||||
class ImportJobPermission(HasModelPermission):
|
||||
# not a typo, perms on import job is proxied to import batch
|
||||
model = models.ImportBatch
|
||||
|
||||
|
||||
class ImportJobViewSet(
|
||||
mixins.CreateModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
queryset = (models.ImportJob.objects.all())
|
||||
serializer_class = serializers.ImportJobSerializer
|
||||
permission_classes = (ImportJobPermission, )
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(batch__submitted_by=self.request.user)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
source = 'file://' + serializer.validated_data['audio_file'].name
|
||||
serializer.save(source=source)
|
||||
tasks.import_job_run.delay(import_job_id=serializer.instance.pk)
|
||||
|
||||
|
||||
class TrackViewSet(TagViewSetMixin, SearchMixin, viewsets.ReadOnlyModelViewSet):
|
||||
"""
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import os
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
|
||||
|
@ -8,6 +9,8 @@ from funkwhale_api.music import serializers
|
|||
|
||||
from . import data as api_data
|
||||
|
||||
DATA_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def test_can_submit_youtube_url_for_track_import(mocker, superuser_client):
|
||||
mocker.patch(
|
||||
|
@ -189,6 +192,48 @@ def test_user_can_query_api_for_his_own_batches(client, factories):
|
|||
assert results['results'][0]['jobs'][0]['mbid'] == job.mbid
|
||||
|
||||
|
||||
def test_user_can_create_an_empty_batch(client, factories):
|
||||
user = factories['users.SuperUser']()
|
||||
url = reverse('api:v1:import-batches-list')
|
||||
client.login(username=user.username, password='test')
|
||||
response = client.post(url)
|
||||
|
||||
assert response.status_code == 201
|
||||
|
||||
batch = user.imports.latest('id')
|
||||
|
||||
assert batch.submitted_by == user
|
||||
assert batch.source == 'api'
|
||||
|
||||
|
||||
def test_user_can_create_import_job_with_file(client, factories, mocker):
|
||||
path = os.path.join(DATA_DIR, 'test.ogg')
|
||||
m = mocker.patch('funkwhale_api.music.tasks.import_job_run.delay')
|
||||
user = factories['users.SuperUser']()
|
||||
batch = factories['music.ImportBatch'](submitted_by=user)
|
||||
url = reverse('api:v1:import-jobs-list')
|
||||
client.login(username=user.username, password='test')
|
||||
with open(path, 'rb') as f:
|
||||
content = f.read()
|
||||
f.seek(0)
|
||||
response = client.post(url, {
|
||||
'batch': batch.pk,
|
||||
'audio_file': f,
|
||||
'source': 'file://'
|
||||
}, format='multipart')
|
||||
|
||||
assert response.status_code == 201
|
||||
|
||||
job = batch.jobs.latest('id')
|
||||
|
||||
assert job.status == 'pending'
|
||||
assert job.source.startswith('file://')
|
||||
assert 'test.ogg' in job.source
|
||||
assert job.audio_file.read() == content
|
||||
|
||||
m.assert_called_once_with(import_job_id=job.pk)
|
||||
|
||||
|
||||
def test_can_search_artist(factories, client):
|
||||
artist1 = factories['music.Artist']()
|
||||
artist2 = factories['music.Artist']()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue