From ddea5f182570f9d558e195fcfe55ba533e547a12 Mon Sep 17 00:00:00 2001 From: Eliot Berriot <contact@eliotberriot.com> Date: Sun, 18 Feb 2018 23:46:15 +0100 Subject: [PATCH] Now store track file mimetype in database --- .../migrations/0018_auto_20180218_1554.py | 28 +++++++++++++++ .../migrations/0019_populate_mimetypes.py | 34 +++++++++++++++++++ api/funkwhale_api/music/models.py | 6 ++++ api/funkwhale_api/music/serializers.py | 9 ++++- api/funkwhale_api/music/utils.py | 8 +++++ api/tests/music/test_models.py | 15 ++++++++ 6 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 api/funkwhale_api/music/migrations/0018_auto_20180218_1554.py create mode 100644 api/funkwhale_api/music/migrations/0019_populate_mimetypes.py diff --git a/api/funkwhale_api/music/migrations/0018_auto_20180218_1554.py b/api/funkwhale_api/music/migrations/0018_auto_20180218_1554.py new file mode 100644 index 00000000..c4529879 --- /dev/null +++ b/api/funkwhale_api/music/migrations/0018_auto_20180218_1554.py @@ -0,0 +1,28 @@ +# Generated by Django 2.0.2 on 2018-02-18 15:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('music', '0017_auto_20171227_1728'), + ] + + operations = [ + migrations.AddField( + model_name='trackfile', + name='mimetype', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AlterField( + model_name='importjob', + name='source', + field=models.CharField(max_length=500), + ), + migrations.AlterField( + model_name='importjob', + name='status', + field=models.CharField(choices=[('pending', 'Pending'), ('finished', 'Finished'), ('errored', 'Errored'), ('skipped', 'Skipped')], default='pending', max_length=30), + ), + ] diff --git a/api/funkwhale_api/music/migrations/0019_populate_mimetypes.py b/api/funkwhale_api/music/migrations/0019_populate_mimetypes.py new file mode 100644 index 00000000..127aa5e6 --- /dev/null +++ b/api/funkwhale_api/music/migrations/0019_populate_mimetypes.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +import os + +from django.db import migrations, models +from funkwhale_api.music.utils import guess_mimetype + + +def populate_mimetype(apps, schema_editor): + TrackFile = apps.get_model("music", "TrackFile") + + for tf in TrackFile.objects.filter(audio_file__isnull=False, mimetype__isnull=True).only('audio_file'): + try: + tf.mimetype = guess_mimetype(tf.audio_file) + except Exception as e: + print('Error on track file {}: {}'.format(tf.pk, e)) + continue + print('Track file {}: {}'.format(tf.pk, tf.mimetype)) + tf.save(update_fields=['mimetype']) + + +def rewind(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('music', '0018_auto_20180218_1554'), + ] + + operations = [ + migrations.RunPython(populate_mimetype, rewind), + ] diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py index f8373ab4..3ebd0741 100644 --- a/api/funkwhale_api/music/models.py +++ b/api/funkwhale_api/music/models.py @@ -18,6 +18,7 @@ from versatileimagefield.fields import VersatileImageField from funkwhale_api import downloader from funkwhale_api import musicbrainz from . import importers +from . import utils class APIModelMixin(models.Model): @@ -364,6 +365,7 @@ class TrackFile(models.Model): source = models.URLField(null=True, blank=True) duration = models.IntegerField(null=True, blank=True) acoustid_track_id = models.UUIDField(null=True, blank=True) + mimetype = models.CharField(null=True, blank=True, max_length=200) def download_file(self): # import the track file, since there is not any @@ -393,6 +395,10 @@ class TrackFile(models.Model): self.track.full_name, os.path.splitext(self.audio_file.name)[-1]) + def save(self, **kwargs): + if not self.mimetype and self.audio_file: + self.mimetype = utils.guess_mimetype(self.audio_file) + return super().save(**kwargs) class ImportBatch(models.Model): IMPORT_BATCH_SOURCES = [ diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py index 506893a4..41de30f1 100644 --- a/api/funkwhale_api/music/serializers.py +++ b/api/funkwhale_api/music/serializers.py @@ -28,7 +28,14 @@ class TrackFileSerializer(serializers.ModelSerializer): class Meta: model = models.TrackFile - fields = ('id', 'path', 'duration', 'source', 'filename', 'track') + fields = ( + 'id', + 'path', + 'duration', + 'source', + 'filename', + 'mimetype', + 'track') def get_path(self, o): url = o.path diff --git a/api/funkwhale_api/music/utils.py b/api/funkwhale_api/music/utils.py index 32b1aeb4..0e4318e5 100644 --- a/api/funkwhale_api/music/utils.py +++ b/api/funkwhale_api/music/utils.py @@ -1,7 +1,9 @@ +import magic import re from django.db.models import Q + def normalize_query(query_string, findterms=re.compile(r'"([^"]+)"|(\S+)').findall, normspace=re.compile(r'\s{2,}').sub): @@ -15,6 +17,7 @@ def normalize_query(query_string, ''' return [normspace(' ', (t[0] or t[1]).strip()) for t in findterms(query_string)] + def get_query(query_string, search_fields): ''' Returns a query, that is a combination of Q objects. That combination aims to search keywords within a model by testing the given search fields. @@ -35,3 +38,8 @@ def get_query(query_string, search_fields): else: query = query & or_query return query + + +def guess_mimetype(f): + b = min(100000, f.size) + return magic.from_buffer(f.read(b), mime=True) diff --git a/api/tests/music/test_models.py b/api/tests/music/test_models.py index 16541546..2eb1f276 100644 --- a/api/tests/music/test_models.py +++ b/api/tests/music/test_models.py @@ -1,9 +1,12 @@ +import os import pytest from funkwhale_api.music import models from funkwhale_api.music import importers from funkwhale_api.music import tasks +DATA_DIR = os.path.dirname(os.path.abspath(__file__)) + def test_can_store_release_group_id_on_album(factories): album = factories['music.Album']() @@ -48,3 +51,15 @@ def test_import_job_is_bound_to_track_file(factories, mocker): tasks.import_job_run(import_job_id=job.pk) job.refresh_from_db() assert job.track_file.track == track + +@pytest.mark.parametrize('extention,mimetype', [ + ('ogg', 'audio/ogg'), + ('mp3', 'audio/mpeg'), +]) +def test_audio_track_mime_type(extention, mimetype, factories): + + name = '.'.join(['test', extention]) + path = os.path.join(DATA_DIR, name) + tf = factories['music.TrackFile'](audio_file__from_path=path) + + assert tf.mimetype == mimetype -- GitLab