forked from rc/aircox
Reviewed-on: rc/aircox#40
This commit is contained in:
@ -7,6 +7,7 @@ import os
|
||||
import shutil
|
||||
|
||||
import pytz
|
||||
from django.conf import settings as conf
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.db.models import F, Q
|
||||
@ -72,15 +73,18 @@ class Program(Page):
|
||||
return os.path.join(settings.AIRCOX_PROGRAMS_DIR,
|
||||
self.slug.replace('-', '_'))
|
||||
|
||||
@property
|
||||
def abspath(self):
|
||||
""" Return absolute path to program's dir """
|
||||
return os.path.join(conf.MEDIA_ROOT, self.path)
|
||||
|
||||
@property
|
||||
def archives_path(self):
|
||||
return os.path.join(self.path, settings.AIRCOX_SOUND_ARCHIVES_SUBDIR)
|
||||
|
||||
@property
|
||||
def excerpts_path(self):
|
||||
return os.path.join(
|
||||
self.path, settings.AIRCOX_SOUND_ARCHIVES_SUBDIR
|
||||
)
|
||||
return os.path.join(self.path, settings.AIRCOX_SOUND_ARCHIVES_SUBDIR)
|
||||
|
||||
def __init__(self, *kargs, **kwargs):
|
||||
super().__init__(*kargs, **kwargs)
|
||||
@ -94,6 +98,8 @@ class Program(Page):
|
||||
Return a Program from the given path. We assume the path has been
|
||||
given in a previous time by this model (Program.path getter).
|
||||
"""
|
||||
if path.startswith(conf.MEDIA_ROOT):
|
||||
path = path.replace(conf.MEDIA_ROOT + '/', '')
|
||||
path = path.replace(settings.AIRCOX_PROGRAMS_DIR, '')
|
||||
|
||||
while path[0] == '/':
|
||||
@ -107,10 +113,9 @@ class Program(Page):
|
||||
Make sur the program's dir exists (and optionally subdir). Return True
|
||||
if the dir (or subdir) exists.
|
||||
"""
|
||||
path = os.path.join(self.path, subdir) if subdir else \
|
||||
self.path
|
||||
path = os.path.join(self.abspath, subdir) if subdir else \
|
||||
self.abspath
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
return os.path.exists(path)
|
||||
|
||||
class Meta:
|
||||
@ -127,14 +132,15 @@ class Program(Page):
|
||||
|
||||
# TODO: move in signals
|
||||
path_ = getattr(self, '__initial_path', None)
|
||||
abspath = os.path.join(conf.MEDIA_ROOT, path_)
|
||||
if path_ is not None and path_ != self.path and \
|
||||
os.path.exists(path_) and not os.path.exists(self.path):
|
||||
os.path.exists(abspath) and not os.path.exists(self.abspath):
|
||||
logger.info('program #%s\'s dir changed to %s - update it.',
|
||||
self.id, self.title)
|
||||
|
||||
shutil.move(path_, self.path)
|
||||
shutil.move(abspath, self.abspath)
|
||||
Sound.objects.filter(path__startswith=path_) \
|
||||
.update(path=Concat('path', Substr(F('path'), len(path_))))
|
||||
.update(file=Concat('file', Substr(F('file'), len(path_))))
|
||||
|
||||
|
||||
class ProgramChildQuerySet(PageQuerySet):
|
||||
|
@ -2,9 +2,10 @@ from enum import IntEnum
|
||||
import logging
|
||||
import os
|
||||
|
||||
from django.conf import settings as main_settings
|
||||
from django.conf import settings as conf
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.db.models import Q, Value as V
|
||||
from django.db.models.functions import Concat
|
||||
from django.utils import timezone as tz
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
@ -47,18 +48,19 @@ class SoundQuerySet(models.QuerySet):
|
||||
|
||||
def paths(self, archive=True, order_by=True):
|
||||
"""
|
||||
Return paths as a flat list (exclude sound without path).
|
||||
Return files absolute paths as a flat list (exclude sound without path).
|
||||
If `order_by` is True, order by path.
|
||||
"""
|
||||
if archive:
|
||||
self = self.archive()
|
||||
if order_by:
|
||||
self = self.order_by('path')
|
||||
return self.filter(path__isnull=False).values_list('path', flat=True)
|
||||
self = self.order_by('file')
|
||||
return [os.path.join(conf.MEDIA_ROOT, file) for file in self.filter(file__isnull=False) \
|
||||
.values_list('file', flat=True)]
|
||||
|
||||
def search(self, query):
|
||||
return self.filter(
|
||||
Q(name__icontains=query) | Q(path__icontains=query) |
|
||||
Q(name__icontains=query) | Q(file__icontains=query) |
|
||||
Q(program__title__icontains=query) |
|
||||
Q(episode__title__icontains=query)
|
||||
)
|
||||
@ -94,21 +96,15 @@ class Sound(models.Model):
|
||||
position = models.PositiveSmallIntegerField(
|
||||
_('order'), default=0, help_text=_('position in the playlist'),
|
||||
)
|
||||
# FIXME: url() does not use the same directory than here
|
||||
# should we use FileField for more reliability?
|
||||
path = models.FilePathField(
|
||||
_('file'),
|
||||
path=settings.AIRCOX_PROGRAMS_DIR,
|
||||
match=r'(' + '|'.join(settings.AIRCOX_SOUND_FILE_EXT)
|
||||
.replace('.', r'\.') + ')$',
|
||||
recursive=True, max_length=255,
|
||||
blank=True, null=True, unique=True,
|
||||
|
||||
def _upload_to(self, filename):
|
||||
subdir = AIRCOX_SOUND_ARCHIVES_SUBDIR if self.type == self.TYPE_ARCHIVE else \
|
||||
AIRCOX_SOUND_EXCERPTS_SUBDIR
|
||||
return os.path.join(self.program.path, subdir, filename)
|
||||
|
||||
file = models.FileField(
|
||||
_('file'), upload_to=_upload_to
|
||||
)
|
||||
#embed = models.TextField(
|
||||
# _('embed'),
|
||||
# blank=True, null=True,
|
||||
# help_text=_('HTML code to embed a sound from an external plateform'),
|
||||
#)
|
||||
duration = models.TimeField(
|
||||
_('duration'),
|
||||
blank=True, null=True,
|
||||
@ -134,8 +130,12 @@ class Sound(models.Model):
|
||||
verbose_name = _('Sound')
|
||||
verbose_name_plural = _('Sounds')
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.file and self.file.url
|
||||
|
||||
def __str__(self):
|
||||
return '/'.join(self.path.split('/')[-3:])
|
||||
return '/'.join(self.file.path.split('/')[-3:])
|
||||
|
||||
def save(self, check=True, *args, **kwargs):
|
||||
if self.episode is not None and self.program is None:
|
||||
@ -145,17 +145,12 @@ class Sound(models.Model):
|
||||
self.__check_name()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def url(self):
|
||||
""" Return an url to the file. """
|
||||
path = self.path.replace(main_settings.MEDIA_ROOT, '', 1)
|
||||
return (main_settings.MEDIA_URL + path).replace('//','/')
|
||||
|
||||
# TODO: rename get_file_mtime(self)
|
||||
def get_mtime(self):
|
||||
"""
|
||||
Get the last modification date from file
|
||||
"""
|
||||
mtime = os.stat(self.path).st_mtime
|
||||
mtime = os.stat(self.file.path).st_mtime
|
||||
mtime = tz.datetime.fromtimestamp(mtime)
|
||||
mtime = mtime.replace(microsecond=0)
|
||||
return tz.make_aware(mtime, tz.get_current_timezone())
|
||||
@ -163,7 +158,7 @@ class Sound(models.Model):
|
||||
def file_exists(self):
|
||||
""" Return true if the file still exists. """
|
||||
|
||||
return os.path.exists(self.path)
|
||||
return os.path.exists(self.file.path)
|
||||
|
||||
def check_on_file(self):
|
||||
"""
|
||||
@ -173,7 +168,7 @@ class Sound(models.Model):
|
||||
if not self.file_exists():
|
||||
if self.type == self.TYPE_REMOVED:
|
||||
return
|
||||
logger.info('sound %s: has been removed', self.path)
|
||||
logger.info('sound %s: has been removed', self.file.name)
|
||||
self.type = self.TYPE_REMOVED
|
||||
return True
|
||||
|
||||
@ -183,7 +178,7 @@ class Sound(models.Model):
|
||||
if self.type == self.TYPE_REMOVED and self.program:
|
||||
changed = True
|
||||
self.type = self.TYPE_ARCHIVE \
|
||||
if self.path.startswith(self.program.archives_path) else \
|
||||
if self.file.name.startswith(self.program.archives_path) else \
|
||||
self.TYPE_EXCERPT
|
||||
|
||||
# check mtime -> reset quality if changed (assume file changed)
|
||||
@ -193,15 +188,15 @@ class Sound(models.Model):
|
||||
self.mtime = mtime
|
||||
self.is_good_quality = None
|
||||
logger.info('sound %s: m_time has changed. Reset quality info',
|
||||
self.path)
|
||||
self.file.name)
|
||||
return True
|
||||
|
||||
return changed
|
||||
|
||||
def __check_name(self):
|
||||
if not self.name and self.path:
|
||||
if not self.name and self.file and self.file.name:
|
||||
# FIXME: later, remove date?
|
||||
self.name = os.path.basename(self.path)
|
||||
self.name = os.path.basename(self.file.name)
|
||||
self.name = os.path.splitext(self.name)[0]
|
||||
self.name = self.name.replace('_', ' ')
|
||||
|
||||
|
Reference in New Issue
Block a user