From 3415d02028c3204bef6dce3ced1027339753c425 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 15 May 2018 21:57:31 +0200 Subject: [PATCH 01/11] Fixed wrong mimetype for flac files --- api/funkwhale_api/music/utils.py | 2 +- front/src/audio/formats.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/funkwhale_api/music/utils.py b/api/funkwhale_api/music/utils.py index 329a99bed..0c73f408f 100644 --- a/api/funkwhale_api/music/utils.py +++ b/api/funkwhale_api/music/utils.py @@ -66,7 +66,7 @@ def compute_status(jobs): AUDIO_EXTENSIONS_AND_MIMETYPE = [ ('ogg', 'audio/ogg'), ('mp3', 'audio/mpeg'), - ('flac', 'audio/flac'), + ('flac', 'audio/x-flac'), ] EXTENSION_TO_MIMETYPE = {ext: mt for ext, mt in AUDIO_EXTENSIONS_AND_MIMETYPE} diff --git a/front/src/audio/formats.js b/front/src/audio/formats.js index a4c2ecf0e..d8a5a4125 100644 --- a/front/src/audio/formats.js +++ b/front/src/audio/formats.js @@ -6,6 +6,6 @@ export default { formatsMap: { 'audio/ogg': 'ogg', 'audio/mpeg': 'mp3', - 'audio/flac': 'flac' + 'audio/x-flac': 'flac' } } From 7425a8ea4d4dd5977c03747b5dab130828fd8da2 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 15 May 2018 21:58:16 +0200 Subject: [PATCH 02/11] See #195: added bitrate and size fields on track file --- api/funkwhale_api/music/admin.py | 2 ++ .../migrations/0027_auto_20180515_1808.py | 29 +++++++++++++++++++ api/funkwhale_api/music/models.py | 2 ++ 3 files changed, 33 insertions(+) create mode 100644 api/funkwhale_api/music/migrations/0027_auto_20180515_1808.py diff --git a/api/funkwhale_api/music/admin.py b/api/funkwhale_api/music/admin.py index 667a7c2a1..1654428ba 100644 --- a/api/funkwhale_api/music/admin.py +++ b/api/funkwhale_api/music/admin.py @@ -74,6 +74,8 @@ class TrackFileAdmin(admin.ModelAdmin): 'source', 'duration', 'mimetype', + 'size', + 'bitrate' ] list_select_related = [ 'track' diff --git a/api/funkwhale_api/music/migrations/0027_auto_20180515_1808.py b/api/funkwhale_api/music/migrations/0027_auto_20180515_1808.py new file mode 100644 index 000000000..835e115a6 --- /dev/null +++ b/api/funkwhale_api/music/migrations/0027_auto_20180515_1808.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0.3 on 2018-05-15 18:08 + +from django.db import migrations, models +import taggit.managers + + +class Migration(migrations.Migration): + + dependencies = [ + ('music', '0026_trackfile_accessed_date'), + ] + + operations = [ + migrations.AddField( + model_name='trackfile', + name='bitrate', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='trackfile', + name='size', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name='track', + name='tags', + field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + ] diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py index 294bce354..0ab852890 100644 --- a/api/funkwhale_api/music/models.py +++ b/api/funkwhale_api/music/models.py @@ -429,6 +429,8 @@ class TrackFile(models.Model): modification_date = models.DateTimeField(auto_now=True) accessed_date = models.DateTimeField(null=True, blank=True) duration = models.IntegerField(null=True, blank=True) + size = models.IntegerField(null=True, blank=True) + bitrate = 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) From 1bc4ceab9e52c446ffedeb119da31c0dd451f322 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 15 May 2018 21:59:29 +0200 Subject: [PATCH 03/11] See #195: set bitrate, duration and size when importing file --- api/funkwhale_api/music/factories.py | 4 ++++ api/funkwhale_api/music/models.py | 18 ++++++++++++++++++ api/funkwhale_api/music/serializers.py | 15 +++++++++++++-- api/funkwhale_api/music/tasks.py | 13 +++++++++++++ api/funkwhale_api/music/utils.py | 10 ++++++++++ api/tests/music/test_import.py | 23 +++++++++++++++++++---- api/tests/music/test_models.py | 25 +++++++++++++++++++++++++ api/tests/music/test_tasks.py | 7 ++++++- api/tests/music/test_utils.py | 18 ++++++++++++++++++ 9 files changed, 126 insertions(+), 7 deletions(-) diff --git a/api/funkwhale_api/music/factories.py b/api/funkwhale_api/music/factories.py index 1df949904..412e2f798 100644 --- a/api/funkwhale_api/music/factories.py +++ b/api/funkwhale_api/music/factories.py @@ -54,6 +54,10 @@ class TrackFileFactory(factory.django.DjangoModelFactory): audio_file = factory.django.FileField( from_path=os.path.join(SAMPLES_PATH, 'test.ogg')) + bitrate = None + size = None + duration = None + class Meta: model = 'music.TrackFile' diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py index 0ab852890..7c5fbe9a6 100644 --- a/api/funkwhale_api/music/models.py +++ b/api/funkwhale_api/music/models.py @@ -479,6 +479,24 @@ class TrackFile(models.Model): return return os.path.splitext(self.audio_file.name)[-1].replace('.', '', 1) + def get_file_size(self): + if self.audio_file: + return self.audio_file.size + + if self.source.startswith('file://'): + return os.path.getsize(self.source.replace('file://', '', 1)) + + if self.library_track and self.library_track.audio_file: + return self.library_track.audio_file.size + + def get_audio_file(self): + if self.audio_file: + return self.audio_file.open() + if self.source.startswith('file://'): + return open(self.source.replace('file://', '', 1), 'rb') + if self.library_track and self.library_track.audio_file: + return self.library_track.audio_file.open() + def save(self, **kwargs): if not self.mimetype and self.audio_file: self.mimetype = utils.guess_mimetype(self.audio_file) diff --git a/api/funkwhale_api/music/serializers.py b/api/funkwhale_api/music/serializers.py index 9dfc91478..d9d48496e 100644 --- a/api/funkwhale_api/music/serializers.py +++ b/api/funkwhale_api/music/serializers.py @@ -27,6 +27,7 @@ class SimpleArtistSerializer(serializers.ModelSerializer): class ArtistSerializer(serializers.ModelSerializer): tags = TagSerializer(many=True, read_only=True) + class Meta: model = models.Artist fields = ('id', 'mbid', 'name', 'tags', 'creation_date') @@ -40,11 +41,21 @@ class TrackFileSerializer(serializers.ModelSerializer): fields = ( 'id', 'path', - 'duration', 'source', 'filename', 'mimetype', - 'track') + 'track', + 'duration', + 'mimetype', + 'bitrate', + 'size', + ) + read_only_fields = [ + 'duration', + 'mimetype', + 'bitrate', + 'size', + ] def get_path(self, o): url = o.path diff --git a/api/funkwhale_api/music/tasks.py b/api/funkwhale_api/music/tasks.py index bad0006aa..a6d76b962 100644 --- a/api/funkwhale_api/music/tasks.py +++ b/api/funkwhale_api/music/tasks.py @@ -134,6 +134,19 @@ def _do_import(import_job, replace=False, use_acoustid=True): # in place import, we set mimetype from extension path, ext = os.path.splitext(import_job.source) track_file.mimetype = music_utils.get_type_from_ext(ext) + audio_file = track_file.get_audio_file() + if audio_file: + with audio_file as f: + audio_data = music_utils.get_audio_file_data(f) + track_file.duration = int(audio_data['length']) + track_file.bitrate = audio_data['bitrate'] + track_file.size = track_file.get_file_size() + else: + lt = track_file.library_track + if lt: + track_file.duration = lt.get_metadata('length') + track_file.size = lt.get_metadata('size') + track_file.bitrate = lt.get_metadata('bitrate') track_file.save() import_job.status = 'finished' import_job.track_file = track_file diff --git a/api/funkwhale_api/music/utils.py b/api/funkwhale_api/music/utils.py index 0c73f408f..04ca208d6 100644 --- a/api/funkwhale_api/music/utils.py +++ b/api/funkwhale_api/music/utils.py @@ -1,5 +1,6 @@ import magic import mimetypes +import mutagen import re from django.db.models import Q @@ -82,3 +83,12 @@ def get_type_from_ext(extension): # we remove leading dot extension = extension[1:] return EXTENSION_TO_MIMETYPE.get(extension) + + +def get_audio_file_data(f): + data = mutagen.File(f) + d = {} + d['bitrate'] = data.info.bitrate + d['length'] = data.info.length + + return d diff --git a/api/tests/music/test_import.py b/api/tests/music/test_import.py index c7b40fb16..8453dca84 100644 --- a/api/tests/music/test_import.py +++ b/api/tests/music/test_import.py @@ -1,4 +1,5 @@ import json +import os import pytest from django.urls import reverse @@ -7,6 +8,8 @@ from funkwhale_api.federation import actors from funkwhale_api.federation import serializers as federation_serializers from funkwhale_api.music import tasks +DATA_DIR = os.path.dirname(os.path.abspath(__file__)) + def test_create_import_can_bind_to_request( artists, albums, mocker, factories, superuser_api_client): @@ -40,11 +43,20 @@ def test_create_import_can_bind_to_request( assert batch.import_request == request -def test_import_job_from_federation_no_musicbrainz(factories): +def test_import_job_from_federation_no_musicbrainz(factories, mocker): + mocker.patch( + 'funkwhale_api.music.utils.get_audio_file_data', + return_value={'bitrate': 24, 'length': 666}) + mocker.patch( + 'funkwhale_api.music.models.TrackFile.get_file_size', + return_value=42) lt = factories['federation.LibraryTrack']( artist_name='Hello', album_title='World', title='Ping', + metadata__length=42, + metadata__bitrate=43, + metadata__size=44, ) job = factories['music.ImportJob']( federation=True, @@ -56,6 +68,9 @@ def test_import_job_from_federation_no_musicbrainz(factories): tf = job.track_file assert tf.mimetype == lt.audio_mimetype + assert tf.duration == 42 + assert tf.bitrate == 43 + assert tf.size == 44 assert tf.library_track == job.library_track assert tf.track.title == 'Ping' assert tf.track.artist.name == 'Hello' @@ -234,13 +249,13 @@ def test_import_batch_notifies_followers( def test__do_import_in_place_mbid(factories, tmpfile): - path = '/test.ogg' + path = os.path.join(DATA_DIR, 'test.ogg') job = factories['music.ImportJob']( - in_place=True, source='file:///test.ogg') + in_place=True, source='file://{}'.format(path)) track = factories['music.Track'](mbid=job.mbid) tf = tasks._do_import(job, use_acoustid=False) assert bool(tf.audio_file) is False - assert tf.source == 'file:///test.ogg' + assert tf.source == 'file://{}'.format(path) assert tf.mimetype == 'audio/ogg' diff --git a/api/tests/music/test_models.py b/api/tests/music/test_models.py index d76c09a01..e926d07fa 100644 --- a/api/tests/music/test_models.py +++ b/api/tests/music/test_models.py @@ -85,3 +85,28 @@ def test_track_file_file_name(factories): tf = factories['music.TrackFile'](audio_file__from_path=path) assert tf.filename == tf.track.full_name + '.mp3' + + +def test_track_get_file_size(factories): + name = 'test.mp3' + path = os.path.join(DATA_DIR, name) + tf = factories['music.TrackFile'](audio_file__from_path=path) + + assert tf.get_file_size() == 297745 + + +def test_track_get_file_size_federation(factories): + tf = factories['music.TrackFile']( + federation=True, + library_track__with_audio_file=True) + + assert tf.get_file_size() == tf.library_track.audio_file.size + + +def test_track_get_file_size_in_place(factories): + name = 'test.mp3' + path = os.path.join(DATA_DIR, name) + tf = factories['music.TrackFile']( + in_place=True, source='file://{}'.format(path)) + + assert tf.get_file_size() == 297745 diff --git a/api/tests/music/test_tasks.py b/api/tests/music/test_tasks.py index ddbc4ba9a..c5839432b 100644 --- a/api/tests/music/test_tasks.py +++ b/api/tests/music/test_tasks.py @@ -62,6 +62,9 @@ def test_import_job_can_run_with_file_and_acoustid( 'score': 0.860825}], 'status': 'ok' } + mocker.patch( + 'funkwhale_api.music.utils.get_audio_file_data', + return_value={'bitrate': 42, 'length': 43}) mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', return_value=artists['get']['adhesive_wombat']) @@ -82,7 +85,9 @@ def test_import_job_can_run_with_file_and_acoustid( with open(path, 'rb') as f: assert track_file.audio_file.read() == f.read() - assert track_file.duration == 268 + assert track_file.bitrate == 42 + assert track_file.duration == 43 + assert track_file.size == os.path.getsize(path) # audio file is deleted from import job once persisted to audio file assert not job.audio_file assert job.status == 'finished' diff --git a/api/tests/music/test_utils.py b/api/tests/music/test_utils.py index 0a4f4b994..12b381a99 100644 --- a/api/tests/music/test_utils.py +++ b/api/tests/music/test_utils.py @@ -1,5 +1,10 @@ +import os +import pytest + from funkwhale_api.music import utils +DATA_DIR = os.path.dirname(os.path.abspath(__file__)) + def test_guess_mimetype_try_using_extension(factories, mocker): mocker.patch( @@ -17,3 +22,16 @@ def test_guess_mimetype_try_using_extension_if_fail(factories, mocker): audio_file__filename='test.mp3') assert utils.guess_mimetype(f.audio_file) == 'audio/mpeg' + + +@pytest.mark.parametrize('name, expected', [ + ('sample.flac', {'bitrate': 1608000, 'length': 0.001}), + ('test.mp3', {'bitrate': 8000, 'length': 267.70285714285717}), + ('test.ogg', {'bitrate': 128000, 'length': 229.18304166666667}), +]) +def test_get_audio_file_data(name, expected): + path = os.path.join(DATA_DIR, name) + with open(path, 'rb') as f: + result = utils.get_audio_file_data(f) + + assert result == expected From af82111adea6ba1857db17ce46adaf2aaae97d28 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 15 May 2018 21:59:40 +0200 Subject: [PATCH 04/11] Missing select related on trackfile viewset --- api/funkwhale_api/music/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py index f06f86f1d..226da0494 100644 --- a/api/funkwhale_api/music/views.py +++ b/api/funkwhale_api/music/views.py @@ -296,7 +296,11 @@ def handle_serve(track_file): class TrackFileViewSet(viewsets.ReadOnlyModelViewSet): - queryset = (models.TrackFile.objects.all().order_by('-id')) + queryset = ( + models.TrackFile.objects.all() + .select_related('track__artist', 'track__album') + .order_by('-id') + ) serializer_class = serializers.TrackFileSerializer authentication_classes = rest_settings.api_settings.DEFAULT_AUTHENTICATION_CLASSES + [ SignatureAuthentication From 4b21128e4651ee1ff1d93fbc8e0e2184655c6217 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 15 May 2018 22:00:26 +0200 Subject: [PATCH 05/11] See #195: fix_track_files command can now recompute size, bitrate and duration --- .../management/commands/fix_track_files.py | 60 +++++++++++++++++++ api/tests/music/test_commands.py | 45 ++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 api/tests/music/test_commands.py diff --git a/api/funkwhale_api/music/management/commands/fix_track_files.py b/api/funkwhale_api/music/management/commands/fix_track_files.py index f68bcf135..9adc1b9bf 100644 --- a/api/funkwhale_api/music/management/commands/fix_track_files.py +++ b/api/funkwhale_api/music/management/commands/fix_track_files.py @@ -2,6 +2,7 @@ import cacheops import os from django.db import transaction +from django.db.models import Q from django.conf import settings from django.core.management.base import BaseCommand, CommandError @@ -24,6 +25,8 @@ class Command(BaseCommand): if options['dry_run']: self.stdout.write('Dry-run on, will not commit anything') self.fix_mimetypes(**options) + self.fix_file_data(**options) + self.fix_file_size(**options) cacheops.invalidate_model(models.TrackFile) @transaction.atomic @@ -43,3 +46,60 @@ class Command(BaseCommand): if not dry_run: self.stdout.write('[mimetypes] commiting...') qs.update(mimetype=mimetype) + + def fix_file_data(self, dry_run, **kwargs): + self.stdout.write('Fixing missing bitrate or length...') + matching = models.TrackFile.objects.filter( + Q(bitrate__isnull=True) | Q(duration__isnull=True)) + total = matching.count() + self.stdout.write( + '[bitrate/length] {} entries found with missing values'.format( + total)) + if dry_run: + return + for i, tf in enumerate(matching.only('audio_file')): + self.stdout.write( + '[bitrate/length] {}/{} fixing file #{}'.format( + i+1, total, tf.pk + )) + + try: + audio_file = tf.get_audio_file() + if audio_file: + with audio_file as f: + data = utils.get_audio_file_data(audio_file) + tf.bitrate = data['bitrate'] + tf.duration = data['length'] + tf.save(update_fields=['duration', 'bitrate']) + else: + self.stderr.write('[bitrate/length] no file found') + except Exception as e: + self.stderr.write( + '[bitrate/length] error with file #{}: {}'.format( + tf.pk, str(e) + ) + ) + + def fix_file_size(self, dry_run, **kwargs): + self.stdout.write('Fixing missing size...') + matching = models.TrackFile.objects.filter(size__isnull=True) + total = matching.count() + self.stdout.write( + '[size] {} entries found with missing values'.format(total)) + if dry_run: + return + for i, tf in enumerate(matching.only('size')): + self.stdout.write( + '[size] {}/{} fixing file #{}'.format( + i+1, total, tf.pk + )) + + try: + tf.size = tf.get_file_size() + tf.save(update_fields=['size']) + except Exception as e: + self.stderr.write( + '[size] error with file #{}: {}'.format( + tf.pk, str(e) + ) + ) diff --git a/api/tests/music/test_commands.py b/api/tests/music/test_commands.py new file mode 100644 index 000000000..ff3343aa5 --- /dev/null +++ b/api/tests/music/test_commands.py @@ -0,0 +1,45 @@ +from funkwhale_api.music.management.commands import fix_track_files + + +def test_fix_track_files_bitrate_length(factories, mocker): + tf1 = factories['music.TrackFile'](bitrate=1, duration=2) + tf2 = factories['music.TrackFile'](bitrate=None, duration=None) + c = fix_track_files.Command() + + mocker.patch( + 'funkwhale_api.music.utils.get_audio_file_data', + return_value={'bitrate': 42, 'length': 43}) + + c.fix_file_data(dry_run=False) + + tf1.refresh_from_db() + tf2.refresh_from_db() + + # not updated + assert tf1.bitrate == 1 + assert tf1.duration == 2 + + # updated + assert tf2.bitrate == 42 + assert tf2.duration == 43 + + +def test_fix_track_files_size(factories, mocker): + tf1 = factories['music.TrackFile'](size=1) + tf2 = factories['music.TrackFile'](size=None) + c = fix_track_files.Command() + + mocker.patch( + 'funkwhale_api.music.models.TrackFile.get_file_size', + return_value=2) + + c.fix_file_size(dry_run=False) + + tf1.refresh_from_db() + tf2.refresh_from_db() + + # not updated + assert tf1.size == 1 + + # updated + assert tf2.size == 2 From 8994326634b686efa7fd4a02f377e207d7c571a0 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 15 May 2018 22:00:59 +0200 Subject: [PATCH 06/11] See #195: expose bitrate, duration and size in AP audio representations --- api/funkwhale_api/federation/factories.py | 3 +++ api/funkwhale_api/federation/models.py | 3 +++ api/funkwhale_api/federation/serializers.py | 9 +++++++++ api/tests/federation/test_serializers.py | 13 ++++++++++++- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/api/funkwhale_api/federation/factories.py b/api/funkwhale_api/federation/factories.py index 0754c4b2f..891609cba 100644 --- a/api/funkwhale_api/federation/factories.py +++ b/api/funkwhale_api/federation/factories.py @@ -233,6 +233,9 @@ class AudioMetadataFactory(factory.Factory): release = factory.LazyAttribute( lambda o: 'https://musicbrainz.org/release/{}'.format(uuid.uuid4()) ) + bitrate = 42 + length = 43 + size = 44 class Meta: model = dict diff --git a/api/funkwhale_api/federation/models.py b/api/funkwhale_api/federation/models.py index d91a00c8b..69d0ea925 100644 --- a/api/funkwhale_api/federation/models.py +++ b/api/funkwhale_api/federation/models.py @@ -216,3 +216,6 @@ class LibraryTrack(models.Model): for chunk in r.iter_content(chunk_size=512): tmp_file.write(chunk) self.audio_file.save(filename, tmp_file) + + def get_metadata(self, key): + return self.metadata.get(key) diff --git a/api/funkwhale_api/federation/serializers.py b/api/funkwhale_api/federation/serializers.py index 426aabd77..8d3dd6379 100644 --- a/api/funkwhale_api/federation/serializers.py +++ b/api/funkwhale_api/federation/serializers.py @@ -688,6 +688,12 @@ class AudioMetadataSerializer(serializers.Serializer): artist = ArtistMetadataSerializer() release = ReleaseMetadataSerializer() recording = RecordingMetadataSerializer() + bitrate = serializers.IntegerField( + required=False, allow_null=True, min_value=0) + size = serializers.IntegerField( + required=False, allow_null=True, min_value=0) + length = serializers.IntegerField( + required=False, allow_null=True, min_value=0) class AudioSerializer(serializers.Serializer): @@ -760,6 +766,9 @@ class AudioSerializer(serializers.Serializer): 'musicbrainz_id': str(track.mbid) if track.mbid else None, 'title': track.title, }, + 'bitrate': instance.bitrate, + 'size': instance.size, + 'length': instance.duration, }, 'url': { 'href': utils.full_url(instance.path), diff --git a/api/tests/federation/test_serializers.py b/api/tests/federation/test_serializers.py index 85208fa49..f298c61f5 100644 --- a/api/tests/federation/test_serializers.py +++ b/api/tests/federation/test_serializers.py @@ -533,7 +533,12 @@ def test_activity_pub_audio_serializer_to_library_track_no_duplicate( def test_activity_pub_audio_serializer_to_ap(factories): - tf = factories['music.TrackFile'](mimetype='audio/mp3') + tf = factories['music.TrackFile']( + mimetype='audio/mp3', + bitrate=42, + duration=43, + size=44, + ) library = actors.SYSTEM_ACTORS['library'].get_actor_instance() expected = { '@context': serializers.AP_CONTEXT, @@ -555,6 +560,9 @@ def test_activity_pub_audio_serializer_to_ap(factories): 'musicbrainz_id': tf.track.mbid, 'title': tf.track.title, }, + 'size': tf.size, + 'length': tf.duration, + 'bitrate': tf.bitrate, }, 'url': { 'href': utils.full_url(tf.path), @@ -599,6 +607,9 @@ def test_activity_pub_audio_serializer_to_ap_no_mbid(factories): 'title': tf.track.title, 'musicbrainz_id': None, }, + 'size': None, + 'length': None, + 'bitrate': None, }, 'url': { 'href': utils.full_url(tf.path), From 417a1b81ae4cf5efa42f53febd45196dd3dc228b Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 15 May 2018 22:01:23 +0200 Subject: [PATCH 07/11] Disabled cacheops in development --- .env.dev | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.dev b/.env.dev index 7e9eb3bf1..e117dbe56 100644 --- a/.env.dev +++ b/.env.dev @@ -10,3 +10,4 @@ PYTHONDONTWRITEBYTECODE=true WEBPACK_DEVSERVER_PORT=8080 MUSIC_DIRECTORY_PATH=/music BROWSABLE_API_ENABLED=True +CACHEOPS_ENABLED=False From 01cabc705d43ba0947455f8fa2d5b4edaddbbc80 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Tue, 15 May 2018 22:24:20 +0200 Subject: [PATCH 08/11] See #195: track detail page now includes bitrate, duration and size --- front/src/components/library/Track.vue | 51 +++++++++++++++++++++++++- front/src/filters.js | 19 ++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/front/src/components/library/Track.vue b/front/src/components/library/Track.vue index 940086e02..155a1245a 100644 --- a/front/src/components/library/Track.vue +++ b/front/src/components/library/Track.vue @@ -44,6 +44,46 @@ +
+

{{ $t('Track information') }}

+ + + + + + + + + + + + + + + + + + +
+ {{ $t('Duration') }} + + {{ time.parse(file.duration) }} + + {{ $t('N/A') }} +
+ {{ $t('Size') }} + + {{ file.size | humanSize }} + + {{ $t('N/A') }} +
+ {{ $t('Bitrate') }} + + {{ file.bitrate | humanSize }}/s + + {{ $t('N/A') }} +
+

@@ -64,6 +104,8 @@