]> git.0d.be Git - django-panik-nonstop.git/blob - nonstop/models.py
add model to store jingles for nonstop zones
[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 get_absolute_url(self):
36         return reverse('artist-view', kwargs={'pk': self.id})
37
38     def recent_diffusions(self):
39         return SomaLogLine.objects.filter(filepath__track__artist=self
40                 ).exclude(on_air=False).order_by('-play_timestamp')
41
42
43 class Album(models.Model):
44     name = models.CharField(_('Name'), max_length=255)
45
46
47 LANGUAGES = [
48     ('en', _('English')),
49     ('fr', _('French')),
50     ('nl', _('Dutch'))
51 ]
52
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)
63
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)
68
69     class Meta:
70         ordering = ['creation_timestamp']
71
72     def get_absolute_url(self):
73         return reverse('track-view', kwargs={'pk': self.id})
74
75     def recent_diffusions(self):
76         return SomaLogLine.objects.filter(filepath__track=self
77                 ).exclude(on_air=False).order_by('-play_timestamp')
78
79     def file_path(self):
80         nfile = None
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()
84         if nfile:
85            return nfile.get_local_filepath()
86         return None
87
88     def file_exists(self):
89         file_path = self.file_path()
90         if not file_path:
91             return False
92         try:
93             return os.path.exists(file_path)
94         except AttributeError:
95             return False
96
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()
102                 self.save()
103         else:
104             self.added_to_nonstop_timestamp = None
105             self.save()
106
107         if not self.file_exists():
108             return
109         nonstop_file = self.nonstopfile_set.order_by('creation_timestamp').last()
110         filename = nonstop_file.filename
111         from emissions.models import Nonstop
112
113         for zone in Nonstop.objects.all():
114             if not zone.slug in TRANCHE_SLUG_DIR_MAPPING:
115                 continue
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)
121             else:
122                 if os.path.exists(zone_path):
123                     os.unlink(zone_path)
124
125
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)
131
132     @property
133     def short(self):
134         return self.filepath[len(REMOTE_BASE_PATH):]
135
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)
139
140     def get_local_filepath(self):
141         if not self.short:
142             return None
143         return os.path.join(LOCAL_BASE_PATH, self.short)
144
145
146 class SomaLogLine(models.Model):
147     class Meta:
148         verbose_name = _('Soma log line')
149         verbose_name_plural = _('Soma log lines')
150         ordering = ['play_timestamp']
151
152     filepath = models.ForeignKey(NonstopFile)
153     play_timestamp = models.DateTimeField()
154     on_air = models.NullBooleanField('On Air')
155
156
157 class Jingle(models.Model):
158     class Meta:
159         verbose_name = _('Jingle')
160         verbose_name_plural = _('Jingles')
161         ordering = ['label']
162
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)
169
170     def __str__(self):
171         return self.label
172
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)
178
179     @property
180     def title(self):
181         return self.label
182
183
184 class Stream(models.Model):
185     class Meta:
186         verbose_name = _('Stream')
187         verbose_name_plural = _('Streams')
188         ordering = ['label']
189
190     label = models.CharField(_('Label'), max_length=100)
191     url = models.URLField(_('URL'), max_length=255)
192
193     def __str__(self):
194         return self.label
195
196
197 class StreamedDiffusion(models.Model):
198     class Meta:
199         verbose_name = _('Streamed diffusion')
200         verbose_name_plural = _('Streamed diffusions')
201
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)
207
208     def __str__(self):
209         return 'Diffusion of %s' % self.diffusion
210
211     @property
212     def datetime(self):
213         return self.diffusion.datetime
214
215     @property
216     def end_datetime(self):
217         if self.is_stream():
218             return self.diffusion.end_datetime
219         dt = self.diffusion.datetime
220         if self.jingle:
221             dt += self.jingle.duration
222         dt += datetime.timedelta(seconds=self.soundfile.duration)
223         return dt
224
225     @property
226     def soundfile(self):
227         return self.diffusion.episode.soundfile_set.filter(fragment=False).first()
228
229     @property
230     def duration(self):
231         return (self.end_datetime - self.datetime).seconds
232
233     @property
234     def episode(self):
235         return self.diffusion.episode
236
237     @property
238     def soma_id(self):
239         if self.is_stream():
240             return '[stream:%s]' % self.id
241         else:
242             return '[sound:%s]' % self.id
243
244     def is_stream(self):
245         return bool(self.stream_id)
246
247
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)
252
253     def __str__(self):
254         return str(self.nonstop)
255
256
257 @receiver(post_delete)
258 def remove_soundfile(sender, instance=None, **kwargs):
259     from emissions.models import SoundFile
260     if not issubclass(sender, SoundFile):
261         return
262     StreamedDiffusion.objects.filter(
263             diffusion__episode_id=instance.episode_id,
264             stream_id=None).update(
265             diffusion=None)