add track to sound when scanning, using file's metadata (add mutagen as dep)
This commit is contained in:
parent
aa1c21a8c8
commit
0141d5174d
|
@ -116,21 +116,30 @@ class SoundInfo:
|
|||
self.sound = 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 \
|
||||
as import_playlist
|
||||
|
||||
# no playlist, try to retrieve metadata
|
||||
path = os.path.splitext(self.sound.path)[0] + '.csv'
|
||||
if not os.path.exists(path):
|
||||
if use_default:
|
||||
track = sound.file_metadata()
|
||||
if track:
|
||||
track.save()
|
||||
return
|
||||
|
||||
old = Track.objects.get_for(object = sound)
|
||||
if old:
|
||||
return
|
||||
|
||||
# else, import
|
||||
import_playlist.Importer(sound, path, save=True)
|
||||
|
||||
def find_diffusion(self, program, save = True):
|
||||
|
@ -326,8 +335,10 @@ class Command(BaseCommand):
|
|||
if check:
|
||||
self.check_sounds(sounds)
|
||||
|
||||
files = [ sound.path for sound in sounds
|
||||
if os.path.exists(sound.path) ]
|
||||
files = [
|
||||
sound.path for sound in sounds
|
||||
if os.path.exists(sound.path) and sound.good_quality is None
|
||||
]
|
||||
|
||||
# check quality
|
||||
logger.info('quality check...',)
|
||||
|
|
151
aircox/models.py
151
aircox/models.py
|
@ -10,7 +10,7 @@ from django.template.defaultfilters import slugify
|
|||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.utils import timezone as tz
|
||||
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.conf import settings as main_settings
|
||||
|
||||
|
@ -115,6 +115,54 @@ class Nameable(models.Model):
|
|||
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
|
||||
#
|
||||
|
@ -208,8 +256,20 @@ class Station(Nameable):
|
|||
logs.filter(date__gt = diff.end, date__lt = diff_.start) \
|
||||
if diff_ else \
|
||||
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
|
||||
items.extend(logs_)
|
||||
items.append(diff)
|
||||
|
@ -252,6 +312,7 @@ class Station(Nameable):
|
|||
logs = Log.objects.get_for(model = Track) \
|
||||
.filter(station = self) \
|
||||
.order_by('-date')
|
||||
|
||||
if date:
|
||||
logs = logs.filter(date__contains = date)
|
||||
diffs = Diffusion.objects.get_at(date)
|
||||
|
@ -730,6 +791,8 @@ class Diffusion(models.Model):
|
|||
start = models.DateTimeField( _('start of the diffusion') )
|
||||
end = models.DateTimeField( _('end of the diffusion') )
|
||||
|
||||
tracks = GenericRelation(Track, 'related_id', 'related_type')
|
||||
|
||||
@property
|
||||
def duration(self):
|
||||
return self.end - self.start
|
||||
|
@ -855,10 +918,10 @@ class Sound(Nameable):
|
|||
blank = True, null = True,
|
||||
help_text = _('last modification date and time'),
|
||||
)
|
||||
good_quality = models.BooleanField(
|
||||
good_quality = models.NullBooleanField(
|
||||
_('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'),
|
||||
|
@ -866,6 +929,8 @@ class Sound(Nameable):
|
|||
help_text = _('the sound is accessible to the public')
|
||||
)
|
||||
|
||||
tracks = GenericRelation(Track, 'related_id', 'related_type')
|
||||
|
||||
def get_mtime(self):
|
||||
"""
|
||||
Get the last modification date from file
|
||||
|
@ -891,6 +956,36 @@ class Sound(Nameable):
|
|||
"""
|
||||
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):
|
||||
"""
|
||||
Check sound file info again'st self, and update informations if
|
||||
|
@ -915,7 +1010,7 @@ class Sound(Nameable):
|
|||
mtime = self.get_mtime()
|
||||
if 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',
|
||||
self.path)
|
||||
return True
|
||||
|
@ -965,50 +1060,6 @@ class Sound(Nameable):
|
|||
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
|
||||
#
|
||||
|
|
|
@ -40,7 +40,7 @@ def on_air(request):
|
|||
else:
|
||||
station = stations.stations.first()
|
||||
|
||||
last = station.on_air(count = 1)
|
||||
last = station.on_air(count = 10)
|
||||
if not last:
|
||||
return HttpResponse('')
|
||||
|
||||
|
|
|
@ -462,6 +462,7 @@ class DatedListBase(models.Model):
|
|||
# context dict
|
||||
return {
|
||||
'nav_dates': {
|
||||
'today': today,
|
||||
'date': date,
|
||||
'next': next,
|
||||
'prev': prev,
|
||||
|
@ -935,12 +936,14 @@ class SectionLogsList(SectionItem):
|
|||
print(log, type(log))
|
||||
if type(log) == aircox.models.Diffusion:
|
||||
return DiffusionPage.as_item(log)
|
||||
|
||||
related = log.related
|
||||
return ListItem(
|
||||
title = '{artist} -- {title}'.format(
|
||||
artist = log.related.artist,
|
||||
title = log.related.title,
|
||||
artist = related.artist,
|
||||
title = related.title,
|
||||
),
|
||||
summary = log.related.info,
|
||||
summary = related.info,
|
||||
date = log.date,
|
||||
info = '♫',
|
||||
css_class = 'track'
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
<div class="list date_list">
|
||||
{% if nav_dates %}
|
||||
<nav class="nav_dates">
|
||||
<a href="?date={{ nav_dates.today|date:"Y-m-d" }}" title="{% trans "today" %}">○</a>
|
||||
|
||||
{% if nav_dates.prev %}
|
||||
<a href="?date={{ nav_dates.prev|date:"Y-m-d" }}" title="{% trans "previous days" %}">◀</a>
|
||||
{% endif %}
|
||||
|
@ -11,7 +13,9 @@
|
|||
{% for day in nav_dates.dates %}
|
||||
<a onclick="select_tab(this, '.panel[data-date=\'{{day|date:"Y-m-d"}}\']');"
|
||||
{% 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' }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
Django>=1.9a1
|
||||
Django>=1.10.3
|
||||
django-taggit>=0.18.3
|
||||
mutagen=1.35.1
|
||||
watchdog>=0.8.3
|
||||
wagtail>=1.5.3
|
||||
Pillow>=3.3.0
|
||||
django-honeypot>=0.5.0
|
||||
dateutils>=0.6.6
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user