add track to sound when scanning, using file's metadata (add mutagen as dep)

This commit is contained in:
bkfox 2016-11-28 17:09:46 +01:00
parent aa1c21a8c8
commit 0141d5174d
6 changed files with 141 additions and 67 deletions

View File

@ -116,21 +116,30 @@ class SoundInfo:
self.sound = sound self.sound = sound
return sound return sound
def find_playlist(self, sound): def find_playlist(self, sound, use_default = True):
""" """
Find a playlist file corresponding to the sound path Find a playlist file corresponding to the sound path, such as:
my_sound.ogg => my_sound.csv
If use_default is True and there is no playlist find found,
use sound file's metadata.
""" """
if sound.tracks.count():
return
import aircox.management.commands.import_playlist \ import aircox.management.commands.import_playlist \
as import_playlist as import_playlist
# no playlist, try to retrieve metadata
path = os.path.splitext(self.sound.path)[0] + '.csv' path = os.path.splitext(self.sound.path)[0] + '.csv'
if not os.path.exists(path): if not os.path.exists(path):
if use_default:
track = sound.file_metadata()
if track:
track.save()
return return
old = Track.objects.get_for(object = sound) # else, import
if old:
return
import_playlist.Importer(sound, path, save=True) import_playlist.Importer(sound, path, save=True)
def find_diffusion(self, program, save = True): def find_diffusion(self, program, save = True):
@ -326,8 +335,10 @@ class Command(BaseCommand):
if check: if check:
self.check_sounds(sounds) self.check_sounds(sounds)
files = [ sound.path for sound in sounds files = [
if os.path.exists(sound.path) ] sound.path for sound in sounds
if os.path.exists(sound.path) and sound.good_quality is None
]
# check quality # check quality
logger.info('quality check...',) logger.info('quality check...',)

View File

@ -10,7 +10,7 @@ from django.template.defaultfilters import slugify
from django.utils.translation import ugettext as _, ugettext_lazy from django.utils.translation import ugettext as _, ugettext_lazy
from django.utils import timezone as tz from django.utils import timezone as tz
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.conf import settings as main_settings from django.conf import settings as main_settings
@ -115,6 +115,54 @@ class Nameable(models.Model):
abstract = True abstract = True
#
# Small common models
#
class Track(Related):
"""
Track of a playlist of an object. The position can either be expressed
as the position in the playlist or as the moment in seconds it started.
"""
# There are no nice solution for M2M relations ship (even without
# through) in django-admin. So we unfortunately need to make one-
# to-one relations and add a position argument
title = models.CharField (
_('title'),
max_length = 128,
)
artist = models.CharField(
_('artist'),
max_length = 128,
)
tags = TaggableManager(
verbose_name=_('tags'),
blank=True,
)
info = models.CharField(
_('information'),
max_length = 128,
blank = True, null = True,
help_text=_('additional informations about this track, such as '
'the version, if is it a remix, features, etc.'),
)
position = models.SmallIntegerField(
default = 0,
help_text=_('position in the playlist'),
)
in_seconds = models.BooleanField(
_('in seconds'),
default = False,
help_text=_('position in the playlist is expressed in seconds')
)
def __str__(self):
return '{self.artist} -- {self.title}'.format(self=self)
class Meta:
verbose_name = _('Track')
verbose_name_plural = _('Tracks')
# #
# Station related classes # Station related classes
# #
@ -208,8 +256,20 @@ class Station(Nameable):
logs.filter(date__gt = diff.end, date__lt = diff_.start) \ logs.filter(date__gt = diff.end, date__lt = diff_.start) \
if diff_ else \ if diff_ else \
logs.filter(date__gt = diff.end) logs.filter(date__gt = diff.end)
print(diff.end, *[str(log.date > diff.end) + " " + str(log.date) for log in logs])
# a log can be started before the end of the diffusion and
# still is running => need to add it to the list and change
# the start date
partial_log = logs.filter(
date__gt = diff.start, date__lt = diff.end
).last()
if partial_log:
next_log = logs.filter(pk__gt = partial_log.pk).first()
if not next_log or next_log.date > diff.date:
partial_log.date = diff.end
logs_ = [partial_log] + list(logs_[:count])
# append to list
diff_ = diff diff_ = diff
items.extend(logs_) items.extend(logs_)
items.append(diff) items.append(diff)
@ -252,6 +312,7 @@ class Station(Nameable):
logs = Log.objects.get_for(model = Track) \ logs = Log.objects.get_for(model = Track) \
.filter(station = self) \ .filter(station = self) \
.order_by('-date') .order_by('-date')
if date: if date:
logs = logs.filter(date__contains = date) logs = logs.filter(date__contains = date)
diffs = Diffusion.objects.get_at(date) diffs = Diffusion.objects.get_at(date)
@ -730,6 +791,8 @@ class Diffusion(models.Model):
start = models.DateTimeField( _('start of the diffusion') ) start = models.DateTimeField( _('start of the diffusion') )
end = models.DateTimeField( _('end of the diffusion') ) end = models.DateTimeField( _('end of the diffusion') )
tracks = GenericRelation(Track, 'related_id', 'related_type')
@property @property
def duration(self): def duration(self):
return self.end - self.start return self.end - self.start
@ -855,10 +918,10 @@ class Sound(Nameable):
blank = True, null = True, blank = True, null = True,
help_text = _('last modification date and time'), help_text = _('last modification date and time'),
) )
good_quality = models.BooleanField( good_quality = models.NullBooleanField(
_('good quality'), _('good quality'),
default = False, help_text = _('sound\'s quality is okay'),
help_text = _('sound\'s quality is okay') blank = True, null = True
) )
public = models.BooleanField( public = models.BooleanField(
_('public'), _('public'),
@ -866,6 +929,8 @@ class Sound(Nameable):
help_text = _('the sound is accessible to the public') help_text = _('the sound is accessible to the public')
) )
tracks = GenericRelation(Track, 'related_id', 'related_type')
def get_mtime(self): def get_mtime(self):
""" """
Get the last modification date from file Get the last modification date from file
@ -891,6 +956,36 @@ class Sound(Nameable):
""" """
return os.path.exists(self.path) return os.path.exists(self.path)
def file_metadata(self):
"""
Get metadata from sound file and return a Track object if succeed,
else None.
"""
if not self.file_exists():
return None
import mutagen
meta = mutagen.File(self.path)
def get_meta(key, cast=str):
value = meta.get(key)
return cast(value[0]) if value else None
info = '{} ({})'.format(get_meta('album'), get_meta('year')) \
if 'album' and 'year' in meta else \
get_meta('album') \
if 'album' else \
('year' in meta) and get_meta('year') or ''
track = Track(
related = self,
title = get_meta('title') or self.name,
artist = get_meta('artist') or _('unknown'),
info = info,
position = get_meta('tracknumber', int) or 0,
)
return track
def check_on_file(self): def check_on_file(self):
""" """
Check sound file info again'st self, and update informations if Check sound file info again'st self, and update informations if
@ -915,7 +1010,7 @@ class Sound(Nameable):
mtime = self.get_mtime() mtime = self.get_mtime()
if self.mtime != mtime: if self.mtime != mtime:
self.mtime = mtime self.mtime = mtime
self.good_quality = False self.good_quality = None
logger.info('sound %s: m_time has changed. Reset quality info', logger.info('sound %s: m_time has changed. Reset quality info',
self.path) self.path)
return True return True
@ -965,50 +1060,6 @@ class Sound(Nameable):
verbose_name_plural = _('Sounds') verbose_name_plural = _('Sounds')
class Track(Related):
"""
Track of a playlist of an object. The position can either be expressed
as the position in the playlist or as the moment in seconds it started.
"""
# There are no nice solution for M2M relations ship (even without
# through) in django-admin. So we unfortunately need to make one-
# to-one relations and add a position argument
title = models.CharField (
_('title'),
max_length = 128,
)
artist = models.CharField(
_('artist'),
max_length = 128,
)
tags = TaggableManager(
verbose_name=_('tags'),
blank=True,
)
info = models.CharField(
_('information'),
max_length = 128,
blank = True, null = True,
help_text=_('additional informations about this track, such as '
'the version, if is it a remix, features, etc.'),
)
position = models.SmallIntegerField(
default = 0,
help_text=_('position in the playlist'),
)
in_seconds = models.BooleanField(
_('in seconds'),
default = False,
help_text=_('position in the playlist is expressed in seconds')
)
def __str__(self):
return '{self.artist} -- {self.title}'.format(self=self)
class Meta:
verbose_name = _('Track')
verbose_name_plural = _('Tracks')
# #
# Controls and audio output # Controls and audio output
# #

View File

@ -40,7 +40,7 @@ def on_air(request):
else: else:
station = stations.stations.first() station = stations.stations.first()
last = station.on_air(count = 1) last = station.on_air(count = 10)
if not last: if not last:
return HttpResponse('') return HttpResponse('')

View File

@ -462,6 +462,7 @@ class DatedListBase(models.Model):
# context dict # context dict
return { return {
'nav_dates': { 'nav_dates': {
'today': today,
'date': date, 'date': date,
'next': next, 'next': next,
'prev': prev, 'prev': prev,
@ -935,12 +936,14 @@ class SectionLogsList(SectionItem):
print(log, type(log)) print(log, type(log))
if type(log) == aircox.models.Diffusion: if type(log) == aircox.models.Diffusion:
return DiffusionPage.as_item(log) return DiffusionPage.as_item(log)
related = log.related
return ListItem( return ListItem(
title = '{artist} -- {title}'.format( title = '{artist} -- {title}'.format(
artist = log.related.artist, artist = related.artist,
title = log.related.title, title = related.title,
), ),
summary = log.related.info, summary = related.info,
date = log.date, date = log.date,
info = '', info = '',
css_class = 'track' css_class = 'track'

View File

@ -4,6 +4,8 @@
<div class="list date_list"> <div class="list date_list">
{% if nav_dates %} {% if nav_dates %}
<nav class="nav_dates"> <nav class="nav_dates">
<a href="?date={{ nav_dates.today|date:"Y-m-d" }}" title="{% trans "today" %}"></a>
{% if nav_dates.prev %} {% if nav_dates.prev %}
<a href="?date={{ nav_dates.prev|date:"Y-m-d" }}" title="{% trans "previous days" %}"></a> <a href="?date={{ nav_dates.prev|date:"Y-m-d" }}" title="{% trans "previous days" %}"></a>
{% endif %} {% endif %}
@ -11,7 +13,9 @@
{% for day in nav_dates.dates %} {% for day in nav_dates.dates %}
<a onclick="select_tab(this, '.panel[data-date=\'{{day|date:"Y-m-d"}}\']');" <a onclick="select_tab(this, '.panel[data-date=\'{{day|date:"Y-m-d"}}\']');"
{% if day == nav_dates.date %}selected{% endif %} {% if day == nav_dates.date %}selected{% endif %}
class="tab {% if day == nav_dates.date %}today{% endif %}"> class="tab {% if day == nav_dates.date %}today{% endif %}"
title="{{ day|date:"l d F Y" }}"
>
{{ day|date:'D. d' }} {{ day|date:'D. d' }}
</a> </a>
{% endfor %} {% endfor %}

View File

@ -1,9 +1,14 @@
Django>=1.9a1 Django>=1.10.3
django-taggit>=0.18.3 django-taggit>=0.18.3
mutagen=1.35.1
watchdog>=0.8.3 watchdog>=0.8.3
wagtail>=1.5.3
Pillow>=3.3.0
django-honeypot>=0.5.0
dateutils>=0.6.6 dateutils>=0.6.6
bleach>=1.4.3 bleach>=1.4.3
django-htmlmin>=0.10.0
wagtail>=1.5.3
django-overextend>=0.4.2
Pillow>=3.3.0
django-modelcluster=2.0
django-honeypot>=0.5.0
django-jet>=1.0.3