]> git.0d.be Git - django-panik-nonstop.git/blob - nonstop/models.py
add model for recurring streams
[django-panik-nonstop.git] / nonstop / models.py
1 import datetime
2 import os
3
4 import mutagen
5
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 _
13
14 REMOTE_BASE_PATH = '/srv/soma/nonstop/'
15 LOCAL_BASE_PATH = '/media/nonstop/'
16
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',
27 }
28
29 class Artist(models.Model):
30     name = models.CharField(_('Name'), max_length=255)
31
32     class Meta:
33         ordering = ['name']
34
35     def __str__(self):
36         return self.name
37
38     def get_absolute_url(self):
39         return reverse('artist-view', kwargs={'pk': self.id})
40
41     def recent_diffusions(self):
42         return SomaLogLine.objects.filter(filepath__track__artist=self
43                 ).exclude(on_air=False).order_by('-play_timestamp')
44
45
46 class Album(models.Model):
47     name = models.CharField(_('Name'), max_length=255)
48
49
50 LANGUAGES = [
51     ('en', _('English')),
52     ('fr', _('French')),
53     ('nl', _('Dutch'))
54 ]
55
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)
66
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)
71
72     class Meta:
73         ordering = ['creation_timestamp']
74
75     def __str__(self):
76         return 'Track %s (%s)' % (self.title, self.artist or 'unknown')
77
78     def get_absolute_url(self):
79         return reverse('track-view', kwargs={'pk': self.id})
80
81     def recent_diffusions(self):
82         return SomaLogLine.objects.filter(filepath__track=self
83                 ).exclude(on_air=False).order_by('-play_timestamp')
84
85     def file_path(self):
86         nfile = None
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()
90         if nfile:
91            return nfile.get_local_filepath()
92         return None
93
94     def file_exists(self):
95         file_path = self.file_path()
96         if not file_path:
97             return False
98         try:
99             return os.path.exists(file_path)
100         except AttributeError:
101             return False
102
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()
108                 self.save()
109         else:
110             self.added_to_nonstop_timestamp = None
111             self.save()
112
113         if not self.file_exists():
114             return
115         nonstop_file = self.nonstopfile_set.order_by('creation_timestamp').last()
116         filename = nonstop_file.filename
117         from emissions.models import Nonstop
118
119         for zone in Nonstop.objects.all():
120             if not zone.slug in TRANCHE_SLUG_DIR_MAPPING:
121                 continue
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)
127             else:
128                 if os.path.exists(zone_path):
129                     os.unlink(zone_path)
130
131
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)
137
138     @property
139     def short(self):
140         return self.filepath[len(REMOTE_BASE_PATH):]
141
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)
145
146     def get_local_filepath(self):
147         if not self.short:
148             return None
149         return os.path.join(LOCAL_BASE_PATH, self.short)
150
151
152 class SomaLogLine(models.Model):
153     class Meta:
154         verbose_name = _('Soma log line')
155         verbose_name_plural = _('Soma log lines')
156         ordering = ['play_timestamp']
157
158     filepath = models.ForeignKey(NonstopFile)
159     play_timestamp = models.DateTimeField()
160     on_air = models.NullBooleanField('On Air')
161
162
163 class Jingle(models.Model):
164     class Meta:
165         verbose_name = _('Jingle')
166         verbose_name_plural = _('Jingles')
167         ordering = ['label']
168
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)
175
176     def __str__(self):
177         return self.label
178
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)
184
185     @property
186     def title(self):
187         return self.label
188
189
190 class Stream(models.Model):
191     class Meta:
192         verbose_name = _('Stream')
193         verbose_name_plural = _('Streams')
194         ordering = ['label']
195
196     label = models.CharField(_('Label'), max_length=100)
197     url = models.URLField(_('URL'), max_length=255)
198
199     def __str__(self):
200         return self.label
201
202
203 class ScheduledDiffusion(models.Model):
204     class Meta:
205         verbose_name = _('Scheduled diffusion')
206         verbose_name_plural = _('Scheduled diffusions')
207
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)
213
214     def __str__(self):
215         return 'Diffusion of %s' % self.diffusion
216
217     @property
218     def datetime(self):
219         return self.diffusion.datetime
220
221     @property
222     def end_datetime(self):
223         if self.is_stream():
224             return self.diffusion.end_datetime
225         dt = self.diffusion.datetime
226         if self.jingle:
227             dt += self.jingle.duration
228         dt += datetime.timedelta(seconds=self.soundfile.duration)
229         return dt
230
231     @property
232     def soundfile(self):
233         return self.diffusion.episode.soundfile_set.filter(fragment=False).first()
234
235     @property
236     def duration(self):
237         return (self.end_datetime - self.datetime).seconds
238
239     @property
240     def episode(self):
241         return self.diffusion.episode
242
243     @property
244     def soma_id(self):
245         if self.is_stream():
246             return '[stream:%s]' % self.id
247         else:
248             return '[sound:%s]' % self.id
249
250     def is_stream(self):
251         return bool(self.stream_id)
252
253
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)
258
259     def __str__(self):
260         return str(self.nonstop)
261
262
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)
268
269
270 @receiver(post_delete)
271 def remove_soundfile(sender, instance=None, **kwargs):
272     from emissions.models import SoundFile
273     if not issubclass(sender, SoundFile):
274         return
275     ScheduledDiffusion.objects.filter(
276             diffusion__episode_id=instance.episode_id,
277             stream_id=None).update(
278             diffusion=None)