forked from rc/aircox
		
	WIP - Sound.file instead of Sound.path; fix issues with player; program.path is now relative
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
 | 
			
		||||
@ -73,14 +74,17 @@ class Program(Page):
 | 
			
		||||
                            self.slug.replace('-', '_'))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def archives_path(self):
 | 
			
		||||
        return os.path.join(self.path, settings.AIRCOX_SOUND_ARCHIVES_SUBDIR)
 | 
			
		||||
    def abspath(self):
 | 
			
		||||
        """ Return absolute path to program's dir """
 | 
			
		||||
        return os.path.join(conf.MEDIA_ROOT, self.path)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def excerpts_path(self):
 | 
			
		||||
        return os.path.join(
 | 
			
		||||
            self.path, settings.AIRCOX_SOUND_ARCHIVES_SUBDIR
 | 
			
		||||
        )
 | 
			
		||||
    def archives_path(self, abs=False):
 | 
			
		||||
        return os.path.join(abs and self.abspath or self.path,
 | 
			
		||||
                            settings.AIRCOX_SOUND_ARCHIVES_SUBDIR)
 | 
			
		||||
 | 
			
		||||
    def excerpts_path(self, abs=False):
 | 
			
		||||
        return os.path.join(abs and self.abspath or 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):
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,8 @@ import os
 | 
			
		||||
 | 
			
		||||
from django.conf import settings as main_settings
 | 
			
		||||
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 _
 | 
			
		||||
 | 
			
		||||
@ -54,7 +55,9 @@ class SoundQuerySet(models.QuerySet):
 | 
			
		||||
            self = self.archive()
 | 
			
		||||
        if order_by:
 | 
			
		||||
            self = self.order_by('path')
 | 
			
		||||
        return self.filter(path__isnull=False).values_list('path', flat=True)
 | 
			
		||||
        return self.filter(file__isnull=False) \
 | 
			
		||||
                   .annotate(file_path=Concat(V(conf.MEDIA_ROOT), 'file'))  \
 | 
			
		||||
                   .values_list('file_path', flat=True)
 | 
			
		||||
 | 
			
		||||
    def search(self, query):
 | 
			
		||||
        return self.filter(
 | 
			
		||||
@ -94,21 +97,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(o.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 +131,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 +146,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 +159,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 +169,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 +179,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.path.startswith(self.program.archives_path) else \
 | 
			
		||||
                self.TYPE_EXCERPT
 | 
			
		||||
 | 
			
		||||
        # check mtime -> reset quality if changed (assume file changed)
 | 
			
		||||
@ -193,15 +189,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.path:
 | 
			
		||||
            # 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