6 from django.conf import settings
7 from django.core.urlresolvers import reverse
8 from django.db import models
9 from django.db.models.signals import post_delete
10 from django.dispatch import receiver
11 from django.utils.timezone import now
12 from django.utils.translation import ugettext_lazy as _
14 REMOTE_BASE_PATH = '/srv/soma/nonstop/'
15 LOCAL_BASE_PATH = '/media/nonstop/'
17 TRANCHE_SLUG_DIR_MAPPING = {
18 'acouphene': 'Acouphene',
19 'biodiversite': 'Biodiversite',
20 'l-heure-de-pointe': 'Heure_de_pointe',
21 'hop-bop-co': 'Hop_Bop_and_co',
22 'la-panique': 'la_panique',
23 'le-mange-disque': 'Mange_Disque',
24 'matin-tranquille': 'Matins_tranquilles',
25 'reveries': 'Reveries',
26 'up-beat-tempo': 'Up_Beat_Tempo',
29 class Artist(models.Model):
30 name = models.CharField(_('Name'), max_length=255)
38 def get_absolute_url(self):
39 return reverse('artist-view', kwargs={'pk': self.id})
41 def recent_diffusions(self):
42 return SomaLogLine.objects.filter(filepath__track__artist=self
43 ).exclude(on_air=False).order_by('-play_timestamp')
46 class Album(models.Model):
47 name = models.CharField(_('Name'), max_length=255)
56 class Track(models.Model):
57 title = models.CharField(_('Title'), max_length=255)
58 artist = models.ForeignKey(Artist, null=True)
59 album = models.ForeignKey(Album, null=True)
60 instru = models.BooleanField(_('Instru'), default=False)
61 language = models.CharField(max_length=3,
62 choices=LANGUAGES, blank=True)
63 sabam = models.BooleanField('SABAM', default=True)
64 cfwb = models.BooleanField('CFWB', default=False)
65 nonstop_zones = models.ManyToManyField('emissions.Nonstop', blank=True)
67 creation_timestamp = models.DateTimeField(auto_now_add=True, null=True)
68 added_to_nonstop_timestamp = models.DateTimeField(null=True)
69 uploader = models.ForeignKey(settings.AUTH_USER_MODEL, null=True)
70 duration = models.DurationField(_('Duration'), null=True)
73 ordering = ['creation_timestamp']
76 return 'Track %s (%s)' % (self.title, self.artist or 'unknown')
78 def get_absolute_url(self):
79 return reverse('track-view', kwargs={'pk': self.id})
81 def recent_diffusions(self):
82 return SomaLogLine.objects.filter(filepath__track=self
83 ).exclude(on_air=False).order_by('-play_timestamp')
87 for nfile in self.nonstopfile_set.all().order_by('creation_timestamp'):
88 if os.path.exists(nfile.get_local_filepath()):
89 return nfile.get_local_filepath()
91 return nfile.get_local_filepath()
94 def file_exists(self):
95 file_path = self.file_path()
99 return os.path.exists(file_path)
100 except AttributeError:
103 def sync_nonstop_zones(self):
104 current_zones = self.nonstop_zones.all()
105 if current_zones.count():
106 if not self.added_to_nonstop_timestamp:
107 self.added_to_nonstop_timestamp = now()
110 self.added_to_nonstop_timestamp = None
113 if not self.file_exists():
115 nonstop_file = self.nonstopfile_set.order_by('creation_timestamp').last()
116 filename = nonstop_file.filename
117 from emissions.models import Nonstop
119 for zone in Nonstop.objects.all():
120 if not zone.slug in TRANCHE_SLUG_DIR_MAPPING:
122 zone_dir = TRANCHE_SLUG_DIR_MAPPING[zone.slug]
123 zone_path = os.path.join(LOCAL_BASE_PATH, 'Tranches', zone_dir, filename)
124 if zone in current_zones:
125 if not os.path.exists(zone_path):
126 os.symlink(os.path.join('..', '..', nonstop_file.short), zone_path)
128 if os.path.exists(zone_path):
132 class NonstopFile(models.Model):
133 filepath = models.CharField(_('Filepath'), max_length=255)
134 filename = models.CharField(_('Filename'), max_length=255, null=True)
135 creation_timestamp = models.DateTimeField(auto_now_add=True, null=True)
136 track = models.ForeignKey(Track, null=True)
140 return self.filepath[len(REMOTE_BASE_PATH):]
142 def set_track_filepath(self, filepath):
143 self.filepath = os.path.join(REMOTE_BASE_PATH, 'tracks', filepath)
144 self.filename = os.path.basename(filepath)
146 def get_local_filepath(self):
149 return os.path.join(LOCAL_BASE_PATH, self.short)
152 class SomaLogLine(models.Model):
154 verbose_name = _('Soma log line')
155 verbose_name_plural = _('Soma log lines')
156 ordering = ['play_timestamp']
158 filepath = models.ForeignKey(NonstopFile)
159 play_timestamp = models.DateTimeField()
160 on_air = models.NullBooleanField('On Air')
163 class Jingle(models.Model):
165 verbose_name = _('Jingle')
166 verbose_name_plural = _('Jingles')
169 label = models.CharField(_('Label'), max_length=100)
170 filepath = models.CharField(_('File Path'), max_length=255)
171 duration = models.DurationField(_('Duration'), null=True, blank=True)
172 default_for_initial_diffusions = models.BooleanField(_('Default for initial diffusions'), default=False)
173 default_for_reruns = models.BooleanField(_('Default for reruns'), default=False)
174 default_for_streams = models.BooleanField(_('Default for streams'), default=False)
179 def save(self, **kwargs):
180 for attr in ('default_for_initial_diffusions', 'default_for_reruns', 'default_for_streams'):
181 if getattr(self, attr):
182 Jingle.objects.all().update(**{attr: False})
183 return super(Jingle, self).save(**kwargs)
190 class Stream(models.Model):
192 verbose_name = _('Stream')
193 verbose_name_plural = _('Streams')
196 label = models.CharField(_('Label'), max_length=100)
197 url = models.URLField(_('URL'), max_length=255)
203 class ScheduledDiffusion(models.Model):
205 verbose_name = _('Scheduled diffusion')
206 verbose_name_plural = _('Scheduled diffusions')
208 diffusion = models.ForeignKey('emissions.Diffusion', null=True, blank=True, on_delete=models.SET_NULL)
209 jingle = models.ForeignKey(Jingle, null=True, blank=True)
210 stream = models.ForeignKey(Stream, null=True, blank=True)
211 creation_timestamp = models.DateTimeField(auto_now_add=True, null=True)
212 added_to_nonstop_timestamp = models.DateTimeField(null=True)
215 return 'Diffusion of %s' % self.diffusion
219 return self.diffusion.datetime
222 def end_datetime(self):
224 return self.diffusion.end_datetime
225 dt = self.diffusion.datetime
227 dt += self.jingle.duration
228 dt += datetime.timedelta(seconds=self.soundfile.duration)
233 return self.diffusion.episode.soundfile_set.filter(fragment=False).first()
237 return (self.end_datetime - self.datetime).seconds
241 return self.diffusion.episode
246 return '[stream:%s]' % self.id
248 return '[sound:%s]' % self.id
251 return bool(self.stream_id)
254 class NonstopZoneSettings(models.Model):
255 nonstop = models.ForeignKey('emissions.Nonstop', on_delete=models.CASCADE)
256 intro_jingle = models.ForeignKey(Jingle, blank=True, null=True, related_name='+')
257 jingles = models.ManyToManyField(Jingle, blank=True)
260 return str(self.nonstop)
263 class RecurringStreamDiffusion(models.Model):
264 schedule = models.ForeignKey('emissions.Schedule', on_delete=models.CASCADE)
265 jingle = models.ForeignKey(Jingle, null=True, blank=True)
266 stream = models.ForeignKey(Stream)
267 is_active = models.BooleanField('Active', default=True)
270 @receiver(post_delete)
271 def remove_soundfile(sender, instance=None, **kwargs):
272 from emissions.models import SoundFile
273 if not issubclass(sender, SoundFile):
275 ScheduledDiffusion.objects.filter(
276 diffusion__episode_id=instance.episode_id,
277 stream_id=None).update(