Merge branch 'feature/44-bind-import-to-track-file' into 'develop'
Fixed #44: now bind track file to import job Closes #44 See merge request funkwhale/funkwhale!23
This commit is contained in:
commit
684fc3609a
|
@ -8,6 +8,7 @@ Changelog
|
|||
Features:
|
||||
|
||||
- Models: now store relese group mbid on Album model (#7)
|
||||
- Models: now bind import job to track files (#44)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
|
|
|
@ -336,3 +336,4 @@ CACHALOT_ENABLED = env.bool('CACHALOT_ENABLED', default=True)
|
|||
|
||||
# Custom Admin URL, use {% url 'admin:index' %}
|
||||
ADMIN_URL = env('DJANGO_ADMIN_URL', default='^api/admin/')
|
||||
CSRF_USE_SESSIONS = True
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-12-14 22:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.contrib.sites.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sites', '0002_set_site_domain_and_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelManagers(
|
||||
name='site',
|
||||
managers=[
|
||||
('objects', django.contrib.sites.models.SiteManager()),
|
||||
],
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='site',
|
||||
name='domain',
|
||||
field=models.CharField(max_length=100, unique=True, validators=[django.contrib.sites.models._simple_domain_name_validator], verbose_name='domain name'),
|
||||
),
|
||||
]
|
|
@ -2,30 +2,35 @@ from django.contrib import admin
|
|||
|
||||
from . import models
|
||||
|
||||
|
||||
@admin.register(models.Artist)
|
||||
class ArtistAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'mbid', 'creation_date']
|
||||
search_fields = ['name', 'mbid']
|
||||
|
||||
|
||||
@admin.register(models.Album)
|
||||
class AlbumAdmin(admin.ModelAdmin):
|
||||
list_display = ['title', 'artist', 'mbid', 'release_date', 'creation_date']
|
||||
search_fields = ['title', 'artist__name', 'mbid']
|
||||
list_select_related = True
|
||||
|
||||
|
||||
@admin.register(models.Track)
|
||||
class TrackAdmin(admin.ModelAdmin):
|
||||
list_display = ['title', 'artist', 'album', 'mbid']
|
||||
search_fields = ['title', 'artist__name', 'album__title', 'mbid']
|
||||
list_select_related = True
|
||||
|
||||
|
||||
@admin.register(models.ImportBatch)
|
||||
class ImportBatchAdmin(admin.ModelAdmin):
|
||||
list_display = ['creation_date', 'status']
|
||||
|
||||
|
||||
@admin.register(models.ImportJob)
|
||||
class ImportJobAdmin(admin.ModelAdmin):
|
||||
list_display = ['source', 'batch', 'status', 'mbid']
|
||||
list_display = ['source', 'batch', 'track_file', 'status', 'mbid']
|
||||
list_select_related = True
|
||||
search_fields = ['source', 'batch__pk', 'mbid']
|
||||
list_filter = ['status']
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-12-14 21:14
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('music', '0013_auto_20171213_2211'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='importjob',
|
||||
name='track_file',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='music.TrackFile'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,34 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import os
|
||||
|
||||
from django.db import migrations, models
|
||||
from funkwhale_api.common.utils import rename_file
|
||||
|
||||
|
||||
def bind_jobs(apps, schema_editor):
|
||||
TrackFile = apps.get_model("music", "TrackFile")
|
||||
ImportJob = apps.get_model("music", "ImportJob")
|
||||
|
||||
for job in ImportJob.objects.all().only('mbid'):
|
||||
f = TrackFile.objects.filter(track__mbid=job.mbid).first()
|
||||
if not f:
|
||||
print('No file for mbid {}'.format(job.mbid))
|
||||
continue
|
||||
job.track_file = f
|
||||
job.save(update_fields=['track_file'])
|
||||
|
||||
|
||||
def rewind(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('music', '0014_importjob_track_file'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(bind_jobs, rewind),
|
||||
]
|
|
@ -393,6 +393,8 @@ class ImportBatch(models.Model):
|
|||
|
||||
class ImportJob(models.Model):
|
||||
batch = models.ForeignKey(ImportBatch, related_name='jobs')
|
||||
track_file = models.ForeignKey(
|
||||
TrackFile, related_name='jobs', null=True, blank=True)
|
||||
source = models.URLField()
|
||||
mbid = models.UUIDField(editable=False)
|
||||
STATUS_CHOICES = (
|
||||
|
@ -413,10 +415,12 @@ class ImportJob(models.Model):
|
|||
elif track.files.count() > 0:
|
||||
return
|
||||
|
||||
track_file = track_file or TrackFile(track=track, source=self.source)
|
||||
track_file = track_file or TrackFile(
|
||||
track=track, source=self.source)
|
||||
track_file.download_file()
|
||||
track_file.save()
|
||||
self.status = 'finished'
|
||||
self.track_file = track_file
|
||||
self.save()
|
||||
return track.pk
|
||||
|
||||
|
|
|
@ -9,35 +9,26 @@ class TagSerializer(serializers.ModelSerializer):
|
|||
model = Tag
|
||||
fields = ('id', 'name', 'slug')
|
||||
|
||||
|
||||
class SimpleArtistSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.Artist
|
||||
fields = ('id', 'mbid', 'name')
|
||||
|
||||
|
||||
class ArtistSerializer(serializers.ModelSerializer):
|
||||
tags = TagSerializer(many=True, read_only=True)
|
||||
class Meta:
|
||||
model = models.Artist
|
||||
fields = ('id', 'mbid', 'name', 'tags')
|
||||
|
||||
class ImportJobSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.ImportJob
|
||||
fields = ('id', 'mbid', 'source', 'status')
|
||||
|
||||
class ImportBatchSerializer(serializers.ModelSerializer):
|
||||
jobs = ImportJobSerializer(many=True, read_only=True)
|
||||
class Meta:
|
||||
model = models.ImportBatch
|
||||
fields = ('id', 'jobs', 'status', 'creation_date')
|
||||
|
||||
|
||||
class TrackFileSerializer(serializers.ModelSerializer):
|
||||
path = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.TrackFile
|
||||
fields = ('id', 'path', 'duration', 'source', 'filename')
|
||||
fields = ('id', 'path', 'duration', 'source', 'filename', 'track')
|
||||
|
||||
def get_path(self, o):
|
||||
request = self.context.get('request')
|
||||
|
@ -46,12 +37,14 @@ class TrackFileSerializer(serializers.ModelSerializer):
|
|||
url = request.build_absolute_uri(url)
|
||||
return url
|
||||
|
||||
|
||||
class SimpleAlbumSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = models.Album
|
||||
fields = ('id', 'mbid', 'title', 'release_date', 'cover')
|
||||
|
||||
|
||||
class AlbumSerializer(serializers.ModelSerializer):
|
||||
tags = TagSerializer(many=True, read_only=True)
|
||||
class Meta:
|
||||
|
@ -81,6 +74,7 @@ class TrackSerializer(LyricsMixin):
|
|||
'position',
|
||||
'lyrics')
|
||||
|
||||
|
||||
class TrackSerializerNested(LyricsMixin):
|
||||
artist = ArtistSerializer()
|
||||
files = TrackFileSerializer(many=True, read_only=True)
|
||||
|
@ -90,6 +84,7 @@ class TrackSerializerNested(LyricsMixin):
|
|||
model = models.Track
|
||||
fields = ('id', 'mbid', 'title', 'artist', 'files', 'album', 'tags', 'lyrics')
|
||||
|
||||
|
||||
class AlbumSerializerNested(serializers.ModelSerializer):
|
||||
tracks = TrackSerializer(many=True, read_only=True)
|
||||
artist = SimpleArtistSerializer()
|
||||
|
@ -99,6 +94,7 @@ class AlbumSerializerNested(serializers.ModelSerializer):
|
|||
model = models.Album
|
||||
fields = ('id', 'mbid', 'title', 'cover', 'artist', 'release_date', 'tracks', 'tags')
|
||||
|
||||
|
||||
class ArtistSerializerNested(serializers.ModelSerializer):
|
||||
albums = AlbumSerializerNested(many=True, read_only=True)
|
||||
tags = TagSerializer(many=True, read_only=True)
|
||||
|
@ -111,3 +107,17 @@ class LyricsSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = models.Lyrics
|
||||
fields = ('id', 'work', 'content', 'content_rendered')
|
||||
|
||||
|
||||
class ImportJobSerializer(serializers.ModelSerializer):
|
||||
track_file = TrackFileSerializer(read_only=True)
|
||||
class Meta:
|
||||
model = models.ImportJob
|
||||
fields = ('id', 'mbid', 'source', 'status', 'track_file')
|
||||
|
||||
|
||||
class ImportBatchSerializer(serializers.ModelSerializer):
|
||||
jobs = ImportJobSerializer(many=True, read_only=True)
|
||||
class Meta:
|
||||
model = models.ImportBatch
|
||||
fields = ('id', 'jobs', 'status', 'creation_date')
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import factory
|
||||
import os
|
||||
|
||||
from funkwhale_api.users.tests.factories import UserFactory
|
||||
|
||||
SAMPLES_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
|
@ -42,3 +44,18 @@ class TrackFileFactory(factory.django.DjangoModelFactory):
|
|||
|
||||
class Meta:
|
||||
model = 'music.TrackFile'
|
||||
|
||||
|
||||
class ImportBatchFactory(factory.django.DjangoModelFactory):
|
||||
submitted_by = factory.SubFactory(UserFactory)
|
||||
|
||||
class Meta:
|
||||
model = 'music.ImportBatch'
|
||||
|
||||
|
||||
class ImportJobFactory(factory.django.DjangoModelFactory):
|
||||
batch = factory.SubFactory(ImportBatchFactory)
|
||||
source = factory.Faker('url')
|
||||
|
||||
class Meta:
|
||||
model = 'music.ImportJob'
|
||||
|
|
|
@ -39,3 +39,13 @@ def test_import_album_stores_release_group(db):
|
|||
|
||||
assert album.release_group_id == album_data['release-group']['id']
|
||||
assert album.artist == artist
|
||||
|
||||
|
||||
def test_import_job_is_bound_to_track_file(db, mocker):
|
||||
track = factories.TrackFactory()
|
||||
job = factories.ImportJobFactory(mbid=track.mbid)
|
||||
|
||||
mocker.patch('funkwhale_api.music.models.TrackFile.download_file')
|
||||
job.run()
|
||||
job.refresh_from_db()
|
||||
assert job.track_file.track == track
|
||||
|
|
|
@ -72,7 +72,10 @@ class AlbumViewSet(SearchMixin, viewsets.ReadOnlyModelViewSet):
|
|||
|
||||
|
||||
class ImportBatchViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = models.ImportBatch.objects.all().order_by('-creation_date')
|
||||
queryset = (
|
||||
models.ImportBatch.objects.all()
|
||||
.prefetch_related('jobs__track_file')
|
||||
.order_by('-creation_date'))
|
||||
serializer_class = serializers.ImportBatchSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-12-14 22:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelManagers(
|
||||
name='user',
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='username',
|
||||
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'),
|
||||
),
|
||||
]
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
flake8==2.5.0
|
||||
model-mommy==1.3.2
|
||||
tox==2.7.0
|
||||
pytest
|
||||
pytest-django
|
||||
pytest-mock
|
||||
pytest-sugar
|
||||
pytest-xdist
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<th>Recording MusicBrainz ID</th>
|
||||
<th>Source</th>
|
||||
<th>Status</th>
|
||||
<th>Track</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -38,6 +39,9 @@
|
|||
<span
|
||||
:class="['ui', {'yellow': job.status === 'pending'}, {'green': job.status === 'finished'}, 'label']">{{ job.status }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<router-link v-if="job.track_file" :to="{name: 'library.tracks.detail', params: {id: job.track_file.track }}">{{ job.track_file.track }}</router-link>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
Loading…
Reference in New Issue