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)
35 def get_absolute_url(self):
36 return reverse('artist-view', kwargs={'pk': self.id})
38 def recent_diffusions(self):
39 return SomaLogLine.objects.filter(filepath__track__artist=self
40 ).exclude(on_air=False).order_by('-play_timestamp')
43 class Album(models.Model):
44 name = models.CharField(_('Name'), max_length=255)
53 class Track(models.Model):
54 title = models.CharField(_('Title'), max_length=255)
55 artist = models.ForeignKey(Artist, null=True)
56 album = models.ForeignKey(Album, null=True)
57 instru = models.BooleanField(_('Instru'), default=False)
58 language = models.CharField(max_length=3,
59 choices=LANGUAGES, blank=True)
60 sabam = models.BooleanField('SABAM', default=True)
61 cfwb = models.BooleanField('CFWB', default=False)
62 nonstop_zones = models.ManyToManyField('emissions.Nonstop', blank=True)
64 creation_timestamp = models.DateTimeField(auto_now_add=True, null=True)
65 added_to_nonstop_timestamp = models.DateTimeField(null=True)
66 uploader = models.ForeignKey(settings.AUTH_USER_MODEL, null=True)
67 duration = models.DurationField(_('Duration'), null=True)
70 ordering = ['creation_timestamp']
72 def get_absolute_url(self):
73 return reverse('track-view', kwargs={'pk': self.id})
75 def recent_diffusions(self):
76 return SomaLogLine.objects.filter(filepath__track=self
77 ).exclude(on_air=False).order_by('-play_timestamp')
81 for nfile in self.nonstopfile_set.all().order_by('creation_timestamp'):
82 if os.path.exists(nfile.get_local_filepath()):
83 return nfile.get_local_filepath()
85 return nfile.get_local_filepath()
88 def file_exists(self):
89 file_path = self.file_path()
93 return os.path.exists(file_path)
94 except AttributeError:
97 def sync_nonstop_zones(self):
98 current_zones = self.nonstop_zones.all()
99 if current_zones.count():
100 if not self.added_to_nonstop_timestamp:
101 self.added_to_nonstop_timestamp = now()
104 self.added_to_nonstop_timestamp = None
107 if not self.file_exists():
109 nonstop_file = self.nonstopfile_set.order_by('creation_timestamp').last()
110 filename = nonstop_file.filename
111 from emissions.models import Nonstop
113 for zone in Nonstop.objects.all():
114 if not zone.slug in TRANCHE_SLUG_DIR_MAPPING:
116 zone_dir = TRANCHE_SLUG_DIR_MAPPING[zone.slug]
117 zone_path = os.path.join(LOCAL_BASE_PATH, 'Tranches', zone_dir, filename)
118 if zone in current_zones:
119 if not os.path.exists(zone_path):
120 os.symlink(os.path.join('..', '..', nonstop_file.short), zone_path)
122 if os.path.exists(zone_path):
126 class NonstopFile(models.Model):
127 filepath = models.CharField(_('Filepath'), max_length=255)
128 filename = models.CharField(_('Filename'), max_length=255, null=True)
129 creation_timestamp = models.DateTimeField(auto_now_add=True, null=True)
130 track = models.ForeignKey(Track, null=True)
134 return self.filepath[len(REMOTE_BASE_PATH):]
136 def set_track_filepath(self, filepath):
137 self.filepath = os.path.join(REMOTE_BASE_PATH, 'tracks', filepath)
138 self.filename = os.path.basename(filepath)
140 def get_local_filepath(self):
143 return os.path.join(LOCAL_BASE_PATH, self.short)
146 class SomaLogLine(models.Model):
148 verbose_name = _('Soma log line')
149 verbose_name_plural = _('Soma log lines')
150 ordering = ['play_timestamp']
152 filepath = models.ForeignKey(NonstopFile)
153 play_timestamp = models.DateTimeField()
154 on_air = models.NullBooleanField('On Air')
157 class Jingle(models.Model):
159 verbose_name = _('Jingle')
160 verbose_name_plural = _('Jingles')
163 label = models.CharField(_('Label'), max_length=100)
164 filepath = models.CharField(_('File Path'), max_length=255)
165 duration = models.DurationField(_('Duration'), null=True, blank=True)
166 default_for_initial_diffusions = models.BooleanField(_('Default for initial diffusions'), default=False)
167 default_for_reruns = models.BooleanField(_('Default for reruns'), default=False)
168 default_for_streams = models.BooleanField(_('Default for streams'), default=False)
173 def save(self, **kwargs):
174 for attr in ('default_for_initial_diffusions', 'default_for_reruns', 'default_for_streams'):
175 if getattr(self, attr):
176 Jingle.objects.all().update(**{attr: False})
177 return super(Jingle, self).save(**kwargs)
184 class Stream(models.Model):
186 verbose_name = _('Stream')
187 verbose_name_plural = _('Streams')
190 label = models.CharField(_('Label'), max_length=100)
191 url = models.URLField(_('URL'), max_length=255)
197 class StreamedDiffusion(models.Model):
199 verbose_name = _('Streamed diffusion')
200 verbose_name_plural = _('Streamed diffusions')
202 diffusion = models.ForeignKey('emissions.Diffusion', null=True, blank=True, on_delete=models.SET_NULL)
203 jingle = models.ForeignKey(Jingle, null=True, blank=True)
204 stream = models.ForeignKey(Stream, null=True, blank=True)
205 creation_timestamp = models.DateTimeField(auto_now_add=True, null=True)
206 added_to_nonstop_timestamp = models.DateTimeField(null=True)
209 return 'Diffusion of %s' % self.diffusion
213 return self.diffusion.datetime
216 def end_datetime(self):
218 return self.diffusion.end_datetime
219 dt = self.diffusion.datetime
221 dt += self.jingle.duration
222 dt += datetime.timedelta(seconds=self.soundfile.duration)
227 return self.diffusion.episode.soundfile_set.filter(fragment=False).first()
231 return (self.end_datetime - self.datetime).seconds
235 return self.diffusion.episode
240 return '[stream:%s]' % self.id
242 return '[sound:%s]' % self.id
245 return bool(self.stream_id)
248 class NonstopZoneSettings(models.Model):
249 nonstop = models.ForeignKey('emissions.Nonstop', on_delete=models.CASCADE)
250 intro_jingle = models.ForeignKey(Jingle, blank=True, null=True, related_name='+')
251 jingles = models.ManyToManyField(Jingle, blank=True)
254 return str(self.nonstop)
257 @receiver(post_delete)
258 def remove_soundfile(sender, instance=None, **kwargs):
259 from emissions.models import SoundFile
260 if not issubclass(sender, SoundFile):
262 StreamedDiffusion.objects.filter(
263 diffusion__episode_id=instance.episode_id,
264 stream_id=None).update(