class Series(models.Model): name = models.CharField(max_length=200) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) # uuid = UUIDField() uuid = models.UUIDField(default=uuid.uuid4, editable=False) description = extra.MarkdownTextField(blank=True, null=True) class Meta: app_label = 'alibrary' verbose_name = _('Series') verbose_name_plural = _('Series') ordering = ('-name', ) def __unicode__(self): return '%s' % (self.name)
class Shortcut(BaseModel): name = models.CharField(max_length=256, null=True, blank=True) #description = models.TextField(null=True, blank=True) description = extra.MarkdownTextField(blank=True, null=True) key = models.CharField(max_length=256, null=True, blank=True) position = models.PositiveIntegerField(default=0) url = models.URLField(_("link"), blank=True, null=True) page_link = models.ForeignKey(Page, verbose_name=_("page"), blank=True, null=True, help_text=_("A link to a page has priority over a text link.")) collection = models.ForeignKey('ShortcutCollection', null=True, blank=True, on_delete=models.SET_NULL, related_name='shortcuts') # meta class Meta: app_label = 'shortcutter' verbose_name = _('Shortcut') verbose_name_plural = _('Shortcuts') ordering = ('position', 'name', ) def __unicode__(self): return "%s" % self.name def get_link(self): if self.page_link: link = self.page_link.get_absolute_url() elif self.url: link = self.url else: link = None return link def save(self, *args, **kwargs): obj = self super(Shortcut, self).save(*args, **kwargs)
class Station(BaseModel): name = models.CharField(max_length=256, null=True, blank=True) teaser = models.CharField(max_length=512, null=True, blank=True) slug = AutoSlugField(populate_from='name') TYPE_CHOICES = ( ('stream', _('Stream')), ('djmon', _('DJ-Monitor')), ) type = models.CharField(verbose_name=_('Type'), max_length=12, default='stream', choices=TYPE_CHOICES) main_image = FilerImageField(null=True, blank=True, related_name="station_main_image", rel='') description = extra.MarkdownTextField(blank=True, null=True) members = models.ManyToManyField(User, through='StationMembers', blank=True) website = models.URLField(max_length=256, null=True, blank=True) phone = PhoneNumberField(_('phone'), blank=True, null=True) fax = PhoneNumberField(_('fax'), blank=True, null=True) address1 = models.CharField(_('address'), null=True, blank=True, max_length=100) address2 = models.CharField(_('address (secondary)'), null=True, blank=True, max_length=100) city = models.CharField(_('city'), null=True, blank=True, max_length=100) zip = models.CharField(_('zip'), null=True, blank=True, max_length=10) country = models.ForeignKey(Country, blank=True, null=True) class Meta: app_label = 'abcast' verbose_name = _('Station') verbose_name_plural = _('Stations') ordering = ('name', ) def __unicode__(self): return "%s" % self.name @models.permalink def get_absolute_url(self): return 'abcast-station-detail', [self.slug] def get_admin_url(self): return reverse("admin:abcast_station_change", args=(self.pk,))
class Playlist(MigrationMixin, CachingMixin, models.Model): name = models.CharField(max_length=200) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) uuid = UUIDField() STATUS_CHOICES = ( (0, _('Init')), (1, _('Ready')), (2, _('In progress')), (3, _('Scheduled')), (4, _('Descheduled')), (99, _('Error')), (11, _('Other')), ) status = models.PositiveIntegerField(default=0, choices=STATUS_CHOICES) TYPE_CHOICES = ( ('basket', _('Basket')), ('playlist', _('Playlist')), ('broadcast', _('Broadcast')), ('other', _('Other')), ) type = models.CharField(max_length=12, default='other', null=True, choices=TYPE_CHOICES) EDIT_MODE_CHOICES = ( (0, _('Compact')), (1, _('Medium')), (2, _('Extended')), ) edit_mode = models.PositiveIntegerField(default=2, choices=EDIT_MODE_CHOICES) rotation = models.BooleanField(default=False) main_image = models.ImageField(verbose_name=_('Image'), upload_to=filename_by_uuid, null=True, blank=True) # relations user = models.ForeignKey(User, null=True, blank=True, default=None) #media = models.ManyToManyField('Media', through='PlaylistMedia', blank=True, null=True) items = models.ManyToManyField('PlaylistItem', through='PlaylistItemPlaylist', blank=True, null=True) # tagging (d_tags = "display tags") d_tags = tagging.fields.TagField(max_length=1024, verbose_name="Tags", blank=True, null=True) # commenting enable_comments = models.BooleanField(_('Enable Comments'), default=True) # updated/calculated on save duration = models.IntegerField(max_length=12, null=True, default=0) target_duration = models.PositiveIntegerField( default=0, null=True, choices=TARGET_DURATION_CHOICES) dayparts = models.ManyToManyField(Daypart, null=True, blank=True, related_name='daypart_plalists') seasons = models.ManyToManyField('Season', null=True, blank=True, related_name='season_plalists') weather = models.ManyToManyField('Weather', null=True, blank=True, related_name='weather_plalists') #season = models.PositiveIntegerField(default=0, null=True, choices=TARGET_DURATION_CHOICES) # is currently selected as default? is_current = models.BooleanField(_('Currently selected?'), default=False) description = extra.MarkdownTextField(blank=True, null=True) # manager # objects = models.Manager() objects = CachingManager() # auto-update created = models.DateTimeField(auto_now_add=True, editable=False) updated = models.DateTimeField(auto_now=True, editable=False) # meta class Meta: app_label = 'alibrary' verbose_name = _('Playlist') verbose_name_plural = _('Playlists') ordering = ('-updated', ) permissions = ( ('view_playlist', 'View Playlist'), ('edit_playlist', 'Edit Playlist'), ('admin_playlist', 'Edit Playlist (extended)'), ) def __unicode__(self): return self.name @models.permalink def get_absolute_url(self): return ('alibrary-playlist-detail', [self.slug]) @models.permalink def get_edit_url(self): return ('alibrary-playlist-edit', [self.pk]) def get_duration(self): duration = 0 for item in self.items.all(): duration += item.content_object.get_duration() # TODO: think about what to use as s reference duration = self.target_duration * 1000 return duration #@models.permalink #def get_reorder_url(self): # return ('alibrary-playlist-reorder', [self.pk]) def get_api_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'playlist', 'pk': self.pk }) + '' def get_api_simple_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'simpleplaylist', 'pk': self.pk }) + '' def add_items_by_ids(self, ids, ct): from alibrary.models.mediamodels import Media log = logging.getLogger('alibrary.playlistmodels.add_items_by_ids') log.debug('Media ids: %s' % (ids)) log.debug('Content Type: %s' % (ct)) for id in ids: id = int(id) co = None if ct == 'media': co = Media.objects.get(pk=id) if ct == 'jingle': from abcast.models import Jingle co = Jingle.objects.get(pk=id) if co: i = PlaylistItem(content_object=co) i.save() """ ctype = ContentType.objects.get_for_model(co) item, created = PlaylistItem.objects.get_or_create(object_id=co.pk, content_type=ctype) """ pi, created = PlaylistItemPlaylist.objects.get_or_create( item=i, playlist=self, position=self.items.count()) # pi.save() self.save() def reorder_items_by_uuids(self, uuids): i = 0 for uuid in uuids: print '%s - %s' % (i, uuid) pi = PlaylistItemPlaylist.objects.get(uuid=uuid) pi.position = i pi.save() i += 1 self.save() """ old method - for non-generic playlists """ def add_media_by_ids(self, ids): from alibrary.models.mediamodels import Media log = logging.getLogger('alibrary.playlistmodels.add_media_by_id') log.debug('Media ids: %s' % (ids)) for id in ids: id = int(id) m = Media.objects.get(pk=id) pm = PlaylistMedia(media=m, playlist=self, position=self.media.count()) pm.save() print id print pm self.save() def convert_to(self, type): self.type = type self.save() def get_items(self): pis = PlaylistItemPlaylist.objects.filter(playlist=self) items = [] for pi in pis: item = pi.item item.cue_in = pi.cue_in item.cue_out = pi.cue_out item.fade_in = pi.fade_in item.fade_out = pi.fade_out item.fade_cross = pi.fade_cross items.append(item) return items def save(self, *args, **kwargs): # status update if self.status == 0: self.status = 2 """""" duration = 0 try: for item in self.items.all(): item.content_object.get_duration() if item.content_object.duration: duration += item.content_object.duration #duration -= item.cue_in #duration -= item.cue_out except Exception, e: #print e pass self.duration = duration # update d_tags try: t_tags = '' for tag in self.tags: t_tags += '%s, ' % tag self.tags = t_tags self.d_tags = t_tags except Exception, e: #print e pass
class Profile(MigrationMixin): """Profile model""" GENDER_CHOICES = ( (0, _('Male')), (1, _('Female')), (2, _('Other')), ) #user = models.ForeignKey(User, unique=True) user = models.OneToOneField(User, unique=True, on_delete=models.CASCADE) # uuid = UUIDField() # auto-update created = models.DateTimeField(auto_now_add=True, editable=False) updated = models.DateTimeField(auto_now=True, editable=False) # mentor = models.ForeignKey(User, blank=True, null=True, related_name="godchildren") #Personal gender = models.PositiveSmallIntegerField(_('gender'), choices=GENDER_CHOICES, blank=True, null=True) birth_date = models.DateField(_('Date of birth'), blank=True, null=True, help_text=_('Format: YYYY-MM-DD')) # Profile pseudonym = models.CharField( blank=True, null=True, max_length=250, help_text=_('Will appear instead of your first- & last name')) description = models.CharField(_('Disambiguation'), blank=True, null=True, max_length=250) biography = extra.MarkdownTextField(blank=True, null=True) image = models.ImageField(verbose_name=_('Profile Image'), upload_to=filename_by_uuid, null=True, blank=True) # Contact (personal) mobile = PhoneNumberField(_('mobile'), blank=True, null=True) phone = PhoneNumberField(_('phone'), blank=True, null=True) fax = PhoneNumberField(_('fax'), blank=True, null=True) address1 = models.CharField(_('address'), null=True, blank=True, max_length=100) address2 = models.CharField(_('address (secondary)'), null=True, blank=True, max_length=100) city = models.CharField(_('city'), null=True, blank=True, max_length=100) zip = models.CharField(_('zip'), null=True, blank=True, max_length=10) country = models.ForeignKey(Country, blank=True, null=True) iban = models.CharField(_('IBAN'), null=True, blank=True, max_length=120) paypal = models.EmailField(_('Paypal'), null=True, blank=True, max_length=200) # relations expertise = models.ManyToManyField('Expertise', verbose_name=_('Fields of expertise'), blank=True) # tagging (d_tags = "display tags") d_tags = tagging.fields.TagField(max_length=1024, verbose_name="Tags", blank=True, null=True) # alpha features enable_alpha_features = models.BooleanField(default=False) class Meta: app_label = 'profiles' verbose_name = _('user profile') verbose_name_plural = _('user profiles') db_table = 'user_profiles' ordering = ('-user__last_login', ) permissions = ( ('mentor_profiles', _('Mentoring profiles')), ('view_profiles_private', _('View private profile-data.')), ) def __unicode__(self): return u"%s" % self.get_display_name() def get_full_name(self): if self.user: return self.user.get_full_name() @property def name(self): return self.get_display_name() @property def main_image(self): return self.image def get_display_name(self): if self.pseudonym: return self.pseudonym if self.user.get_full_name(): return self.user.get_full_name() return self.user.username @property def is_approved(self): if self.user in Group.objects.get(name='Mentor').user_set.all(): return True return def approve(self, mentor, level): groups_to_add = [] if level == 'music_pro': groups_to_add = ( 'Music PRO', 'Mentor', ) if level == 'radio_pro': groups_to_add = ( 'Radio PRO', 'Mentor', ) groups = Group.objects.filter(name__in=groups_to_add) for group in groups: self.user.groups.add(group) self.user.groups.remove(Group.objects.get(name=DEFAULT_GROUP)) @property def age(self): TODAY = datetime.date.today() if self.birth_date: return u"%s" % relativedelta.relativedelta(TODAY, self.birth_date).years else: return None def get_absolute_url(self): return reverse('profiles-profile-detail', kwargs={'slug': self.user.username}) @models.permalink def get_edit_url(self): return ('profiles-profile-edit', ) def get_admin_url(self): return reverse("admin:profiles_profile_change", args=(self.pk, )) def get_api_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'profile', 'pk': self.pk }) @property def sms_address(self): if (self.mobile and self.mobile_provider): return u"%s@%s" % (re.sub( '-', '', self.mobile), self.mobile_provider.domain) def get_groups(self): return self.user.groups def save(self, *args, **kwargs): super(Profile, self).save(*args, **kwargs)
class Community(MigrationMixin): uuid = UUIDField(primary_key=False) name = models.CharField(max_length=200, db_index=True) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) group = models.OneToOneField(Group, unique=True, null=True, blank=True) members = models.ManyToManyField(User, blank=True) # auto-update created = models.DateTimeField(auto_now_add=True, editable=False) updated = models.DateTimeField(auto_now=True, editable=False) # Profile description = extra.MarkdownTextField(blank=True, null=True) image = models.ImageField(verbose_name=_('Profile Image'), upload_to=filename_by_uuid, null=True, blank=True) # Contact mobile = PhoneNumberField(_('mobile'), blank=True, null=True) phone = PhoneNumberField(_('phone'), blank=True, null=True) fax = PhoneNumberField(_('fax'), blank=True, null=True) email = models.EmailField(blank=True, null=True) address1 = models.CharField(_('address'), null=True, blank=True, max_length=100) address2 = models.CharField(_('address (secondary)'), null=True, blank=True, max_length=100) city = models.CharField(_('city'), null=True, blank=True, max_length=100) zip = models.CharField(_('zip'), null=True, blank=True, max_length=10) #country = CountryField(blank=True, null=True) country = models.ForeignKey(Country, blank=True, null=True) # relations expertise = models.ManyToManyField('Expertise', verbose_name=_('Fields of expertise'), blank=True) # tagging (d_tags = "display tags") d_tags = tagging.fields.TagField(verbose_name="Tags", blank=True, null=True) class Meta: app_label = 'profiles' verbose_name = _('Community') verbose_name_plural = _('Communities') """ permissions = ( ('mentor_profiles', 'Mentoring profiles'), ) """ def __unicode__(self): return u"%s" % self.name """ @permalink def get_absolute_url(self): return ('profiles-profile-detail', None, { 'slug': self.user.username }) """ def save(self, *args, **kwargs): t_tags = '' """""" for tag in self.tags: t_tags += '%s, ' % tag self.tags = t_tags self.d_tags = t_tags[:245] super(Community, self).save(*args, **kwargs)
class Label(MigrationMixin): uuid = models.UUIDField(default=uuid.uuid4, editable=False) name = models.CharField(max_length=400) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) labelcode = models.CharField(max_length=250, blank=True, null=True) address = models.TextField(blank=True, null=True) country = models.ForeignKey(Country, blank=True, null=True) email = models.EmailField(blank=True, null=True) phone = PhoneNumberField(blank=True, null=True) fax = PhoneNumberField(blank=True, null=True) main_image = models.ImageField(verbose_name=_('Logo Image'), upload_to=upload_image_to, storage=OverwriteStorage(), null=True, blank=True) description = extra.MarkdownTextField(blank=True, null=True) created = models.DateTimeField(auto_now_add=True, editable=False) updated = models.DateTimeField(auto_now=True, editable=False) date_start = ApproximateDateField(verbose_name="Life-span begin", blank=True, null=True) date_end = ApproximateDateField(verbose_name="Life-span end", blank=True, null=True) owner = models.ForeignKey(User, blank=True, null=True, related_name="labels_owner", on_delete=models.SET_NULL) creator = models.ForeignKey(User, blank=True, null=True, related_name="labels_creator", on_delete=models.SET_NULL) last_editor = models.ForeignKey(User, blank=True, null=True, related_name="labels_last_editor", on_delete=models.SET_NULL) publisher = models.ForeignKey(User, blank=True, null=True, related_name="labels_publisher", on_delete=models.SET_NULL) listed = models.BooleanField(verbose_name='Include in listings', default=True, help_text=_('Should this Label be shown on the default Label-list?')) disable_link = models.BooleanField(verbose_name='Disable Link', default=False, help_text=_('Disable Linking. Useful e.g. for "Unknown Label"')) disable_editing = models.BooleanField(verbose_name='Disable Editing', default=False, help_text=_('Disable Editing. Useful e.g. for "Unknown Label"')) type = models.CharField(verbose_name="Label type", max_length=128, default='unknown', choices=alibrary_settings.LABELTYPE_CHOICES) relations = GenericRelation('Relation') d_tags = tagging.fields.TagField(max_length=1024,verbose_name="Tags", blank=True, null=True) # refactoring parent handling parent = models.ForeignKey('self', null=True, blank=True, related_name='children') parent_temporary_id = models.PositiveIntegerField(null=True, blank=True) objects = LabelManager() class Meta: app_label = 'alibrary' verbose_name = _('Label') verbose_name_plural = _('Labels') ordering = ('name', ) permissions = ( ('merge_label', 'Merge Labels'), ) def __unicode__(self): return self.name def get_folder(self, name): return def get_lookup_providers(self): providers = [] for key, name in LOOKUP_PROVIDERS: relations = self.relations.filter(service=key) relation = None if relations.count() == 1: relation = relations[0] providers.append({'key': key, 'name': name, 'relation': relation}) return providers def get_absolute_url(self): if self.disable_link: return None return reverse('alibrary-label-detail', kwargs={ 'pk': self.pk, 'slug': self.slug, }) def get_edit_url(self): return reverse("alibrary-label-edit", args=(self.pk,)) def get_admin_url(self): return reverse("admin:alibrary_label_change", args=(self.pk,)) def get_api_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'library/label', 'pk': self.pk }) + '' def get_root(self): if not self.parent: return None if self.parent == self: return None parent = self.parent last_parent = None i = 0 while parent and i < 10: i += 1 parent = parent.parent if parent: last_parent = parent return last_parent def save(self, *args, **kwargs): unique_slugify(self, self.name) super(Label, self).save(*args, **kwargs)
class Profile(MigrationMixin): """Profile model""" GENDER_CHOICES = ( (0, _('Male')), (1, _('Female')), (2, _('Other')), ) #user = models.ForeignKey(User, unique=True) user = models.OneToOneField(User, unique=True) # uuid = UUIDField() # auto-update created = models.DateTimeField(auto_now_add=True, editable=False) updated = models.DateTimeField(auto_now=True, editable=False) # mentor = models.ForeignKey(User, blank=True, null=True, related_name="godchildren") #Personal gender = models.PositiveSmallIntegerField(_('gender'), choices=GENDER_CHOICES, blank=True, null=True) birth_date = models.DateField(_('Date of birth'), blank=True, null=True) # Profile description = extra.MarkdownTextField(blank=True, null=True) image = models.ImageField(verbose_name=_('Profile Image'), upload_to=filename_by_uuid, null=True, blank=True) # hm... # mugshot = models.FileField(_('mugshot'), upload_to='mugshots', null=True, blank=True) # Contact mobile = PhoneNumberField(_('mobile'), blank=True, null=True) phone = PhoneNumberField(_('phone'), blank=True, null=True) fax = PhoneNumberField(_('fax'), blank=True, null=True) address1 = models.CharField(_('address'), null=True, blank=True, max_length=100) address2 = models.CharField(_('address (secondary)'), null=True, blank=True, max_length=100) # state = models.CharField(_('state'), null=True, blank=True, max_length=100) city = models.CharField(_('city'), null=True, blank=True, max_length=100) zip = models.CharField(_('zip'), null=True, blank=True, max_length=10) #country = models.CharField(_('country'), null=True, blank=True, max_length=100) #country = CountryField(blank=True, null=True) country = models.ForeignKey(Country, blank=True, null=True) iban = models.CharField(_('IBAN'), null=True, blank=True, max_length=120) paypal = models.EmailField(_('Paypal'), null=True, blank=True, max_length=200) # relations expertise = models.ManyToManyField('Expertise', verbose_name=_('Fields of expertise'), null=True, blank=True) # tagging (d_tags = "display tags") d_tags = tagging.fields.TagField(max_length=1024, verbose_name="Tags", blank=True, null=True) class Meta: app_label = 'profiles' verbose_name = _('user profile') verbose_name_plural = _('user profiles') db_table = 'user_profiles' ordering = ('-user__last_login', ) permissions = (('mentor_profiles', 'Mentoring profiles'), ) def __unicode__(self): return u"%s" % self.user.get_full_name() @property def is_approved(self): if self.user in Group.objects.get(name='Member').user_set.all(): return True return def approve(self, mentor): groups = Group.objects.filter(name__in=APPROVED_GROUPS) for group in groups: self.user.groups.add(group) @property def age(self): TODAY = datetime.date.today() if self.birth_date: return u"%s" % relativedelta.relativedelta(TODAY, self.birth_date).years else: return None @permalink def get_absolute_url(self): return ('profiles-profile-detail', None, {'slug': self.user.username}) @property def sms_address(self): if (self.mobile and self.mobile_provider): return u"%s@%s" % (re.sub( '-', '', self.mobile), self.mobile_provider.domain) def get_groups(self): return self.user.groups def save(self, *args, **kwargs): t_tags = '' """""" for tag in self.tags: t_tags += '%s, ' % tag self.tags = t_tags self.d_tags = t_tags[:1000] super(Profile, self).save(*args, **kwargs)
class Release(MigrationMixin): # core fields uuid = UUIDField(primary_key=False) name = models.CharField(max_length=200, db_index=True) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) license = models.ForeignKey(License, blank=True, null=True, related_name='release_license') release_country = CountryField(blank=True, null=True) uuid = UUIDField() main_image = FilerImageField(null=True, blank=True, related_name="release_main_image", rel='') cover_image = FilerImageField( null=True, blank=True, related_name="release_cover_image", rel='', help_text=_( 'Cover close-up. Used e.g. for embedding in digital files.')) if FORCE_CATALOGNUMBER: catalognumber = models.CharField(max_length=50) else: catalognumber = models.CharField(max_length=50, blank=True, null=True) """ releasedate stores the 'real' time, approx is for inputs lik 2012-12 etc. """ releasedate = models.DateField(blank=True, null=True) releasedate_approx = ApproximateDateField(verbose_name="Releasedate", blank=True, null=True) pressings = models.PositiveIntegerField(max_length=12, default=0) totaltracks = models.IntegerField(null=True, blank=True) asin = models.CharField(max_length=150, blank=True) RELEASESTATUS_CHOICES = ( (None, _('Not set')), ('official', _('Official')), ('promo', _('Promo')), ('bootleg', _('Bootleg')), ('other', _('Other')), ) releasestatus = models.CharField(max_length=60, blank=True, choices=RELEASESTATUS_CHOICES) #publish_date = models.DateTimeField(default=datetime.now, blank=True, null=True, help_text=_('If set this Release will not be published on the site before the given date.')) publish_date = models.DateTimeField( blank=True, null=True, help_text= _('If set this Release will not be published on the site before the given date.' )) main_format = models.ForeignKey(Mediaformat, null=True, blank=True, on_delete=models.SET_NULL) # excerpt = models.TextField(blank=True, null=True) description = extra.MarkdownTextField(blank=True, null=True) # cms field placeholder_1 = PlaceholderField('placeholder_1') RELEASETYPE_CHOICES = ( ('ep', _('EP')), ('album', _('Album')), ('compilation', _('Compilation')), ('remix', _('Remix')), ('live', _('Live')), ('single', _('Single')), ('other', _('Other')), ) RELEASETYPE_CHOICES = ( (_('General'), ( ('ep', _('EP')), ('album', _('Album')), ('compilation', _('Compilation')), ('single', _('Single')), )), (_('Recording'), ( ('remix', _('Remix')), ('live', _('Live')), )), ('other', _('Other')), ('unknown', _('Unknown')), ) releasetype = models.CharField(verbose_name="Release type", max_length=24, default='other', choices=RELEASETYPE_CHOICES) # relations label = models.ForeignKey(Label, blank=True, null=True, related_name='release_label', on_delete=models.SET_NULL) folder = models.ForeignKey(Folder, blank=True, null=True, related_name='release_folder', on_delete=models.SET_NULL) # reworking media relationship media = models.ManyToManyField('Media', through='ReleaseMedia', blank=True, null=True, related_name="releases") #media = SortedManyToManyField('Media', blank=True, null=True, related_name="releases") # user relations owner = models.ForeignKey(User, blank=True, null=True, related_name="releases_owner", on_delete=models.SET_NULL) creator = models.ForeignKey(User, blank=True, null=True, related_name="releases_creator", on_delete=models.SET_NULL) publisher = models.ForeignKey(User, blank=True, null=True, related_name="releases_publisher", on_delete=models.SET_NULL) # extra-artists extra_artists = models.ManyToManyField('Artist', through='ReleaseExtraartists', blank=True, null=True) def get_extra_artists(self): ea = [] for artist in self.extra_artists.all(): ea.push(artist.name) return ea # special relation to provide 'multi-names' for artists. album_artists = models.ManyToManyField('Artist', through='ReleaseAlbumartists', related_name="release_albumartists", blank=True, null=True) # relations a.k.a. links relations = generic.GenericRelation(Relation) # tagging (d_tags = "display tags") d_tags = tagging.fields.TagField(max_length=1024, verbose_name="Tags", blank=True, null=True) enable_comments = models.BooleanField(_('Enable Comments'), default=True) # manager objects = ReleaseManager() # auto-update created = models.DateField(auto_now_add=True, editable=False) updated = models.DateField(auto_now=True, editable=False) # meta class Meta: app_label = 'alibrary' verbose_name = _('Release') verbose_name_plural = _('Releases') ordering = ('-created', ) permissions = ( ('view_release', 'View Release'), ('edit_release', 'Edit Release'), ('admin_release', 'Edit Release (extended)'), ) def __unicode__(self): return self.name @property def classname(self): return self.__class__.__name__ def get_versions(self): try: return reversion.get_for_object(self) except: return None def get_last_revision(self): try: return reversion.get_unique_for_object(self)[0].revision except: return None def get_last_editor(self): latest_revision = self.get_last_revision() if latest_revision: return latest_revision.user else: return None def is_active(self): now = date.today() try: if not self.releasedate: return True if self.releasedate <= now: return True except: pass return False def get_lookup_providers(self): providers = [] for key, name in LOOKUP_PROVIDERS: relations = self.relations.filter(service=key) relation = None if relations.count() == 1: relation = relations[0] providers.append({'key': key, 'name': name, 'relation': relation}) return providers @models.permalink def get_absolute_url(self): return ('alibrary-release-detail', [self.slug]) @models.permalink def get_edit_url(self): return ('alibrary-release-edit', [self.pk]) def get_admin_url(self): from lib.util.get_admin_url import change_url return change_url(self) def get_api_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'release', 'pk': self.pk }) + '' def get_media(self): return Media.objects.filter(release=self).select_related() def get_products(self): return self.releaseproduct.all() def get_media_indicator(self): media = self.get_media() indicator = [] if self.totaltracks: for i in range(self.totaltracks): indicator.append(0) for m in media: try: indicator[m.tracknumber - 1] = 3 except Exception, e: pass else:
class Label(MPTTModel, MigrationMixin): # core fields uuid = UUIDField(primary_key=False) name = models.CharField(max_length=400) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) labelcode = models.CharField(max_length=250, blank=True, null=True) address = models.TextField(blank=True, null=True) #country = CountryField(blank=True, null=True) country = models.ForeignKey(Country, blank=True, null=True) email = models.EmailField(blank=True, null=True) phone = PhoneNumberField(blank=True, null=True) fax = PhoneNumberField(blank=True, null=True) main_image = FilerImageField(null=True, blank=True, related_name="label_main_image", rel='') description = extra.MarkdownTextField(blank=True, null=True) first_placeholder = PlaceholderField('first_placeholder') # auto-update created = models.DateField(auto_now_add=True, editable=False) updated = models.DateField(auto_now=True, editable=False) # relations parent = TreeForeignKey('self', null=True, blank=True, related_name='label_children') folder = models.ForeignKey(Folder, blank=True, null=True, related_name='label_folder') # user relations owner = models.ForeignKey(User, blank=True, null=True, related_name="labels_owner", on_delete=models.SET_NULL) creator = models.ForeignKey(User, blank=True, null=True, related_name="labels_creator", on_delete=models.SET_NULL) publisher = models.ForeignKey(User, blank=True, null=True, related_name="labels_publisher", on_delete=models.SET_NULL) # properties to create 'special' objects. (like 'Unknown') listed = models.BooleanField( verbose_name='Include in listings', default=True, help_text=_('Should this Label be shown on the default Label-list?')) disable_link = models.BooleanField( verbose_name='Disable Link', default=False, help_text=_('Disable Linking. Useful e.g. for "Unknown Label"')) disable_editing = models.BooleanField( verbose_name='Disable Editing', default=False, help_text=_('Disable Editing. Useful e.g. for "Unknown Label"')) TYPE_CHOICES = ( ('unknown', _('Unknown')), ('major', _('Major Label')), ('indy', _('Independent Label')), ('net', _('Netlabel')), ('event', _('Event Label')), ) type = models.CharField(verbose_name="Label type", max_length=12, default='unknown', choices=TYPE_CHOICES) # relations a.k.a. links relations = generic.GenericRelation('Relation') # tagging (d_tags = "display tags") d_tags = tagging.fields.TagField(max_length=1024, verbose_name="Tags", blank=True, null=True) # manager objects = LabelManager() # meta class Meta: app_label = 'alibrary' verbose_name = _('Label') verbose_name_plural = _('Labels') ordering = ('name', ) class MPTTMeta: order_insertion_by = ['name'] def __unicode__(self): return self.name def get_folder(self, name): folder, created = Folder.objects.get_or_create(name=name, parent=self.folder) return folder @models.permalink def get_absolute_url(self): if self.disable_link: return None return ('alibrary-label-detail', [self.slug]) @models.permalink def get_edit_url(self): return ('alibrary-label-edit', [self.pk]) def save(self, *args, **kwargs): unique_slugify(self, self.name) # update d_tags t_tags = '' for tag in self.tags: t_tags += '%s, ' % tag self.tags = t_tags self.d_tags = t_tags super(Label, self).save(*args, **kwargs)
class Channel(BaseModel): name = models.CharField(max_length=256, null=True, blank=True) teaser = models.CharField(max_length=512, null=True, blank=True) slug = AutoSlugField(populate_from='name') TYPE_CHOICES = ( ('stream', _('Stream')), ('djmon', _('DJ-Monitor')), ) type = models.CharField(verbose_name=_('Type'), max_length=12, default='stream', choices=TYPE_CHOICES) stream_url = models.CharField(max_length=256, null=True, blank=True, help_text=_('setting the stream-url overrides server settings')) description = extra.MarkdownTextField(blank=True, null=True) station = models.ForeignKey('Station', null=True, blank=True, on_delete=models.SET_NULL) rtmp_app = models.CharField(max_length=256, null=True, blank=True) rtmp_path = models.CharField(max_length=256, null=True, blank=True) has_scheduler = models.BooleanField(default=False) mount = models.CharField(max_length=64, null=True, blank=True) # credentials for tunein api tunein_station_id = models.CharField(max_length=16, null=True, blank=True) tunein_partner_id = models.CharField(max_length=16, null=True, blank=True) tunein_partner_key = models.CharField(max_length=16, null=True, blank=True) # credentials for icecast2 metadata icecast2_server = models.CharField(max_length=256, null=True, blank=True) icecast2_mountpoint = models.CharField(max_length=128, null=True, blank=True) icecast2_admin_user = models.CharField(max_length=128, null=True, blank=True) icecast2_admin_pass = models.CharField(max_length=128, null=True, blank=True) on_air_type = models.ForeignKey(ContentType, null=True, blank=True) on_air_id = models.PositiveIntegerField(null=True, blank=True) on_air = GenericForeignKey('on_air_type', 'on_air_id') class Meta: app_label = 'abcast' verbose_name = _('Channel') verbose_name_plural = _('Channels') ordering = ('name', ) unique_together = ('on_air_type', 'on_air_id') def __unicode__(self): return "%s" % self.name def get_absolute_url(self): return reverse('abcast-station-detail', kwargs={ 'slug': self.station.slug }) def get_api_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'abcast/channel', 'pk': self.pk }) + '' def get_dayparts(self, day): dayparts = [] daypart_sets = self.daypartsets.filter(time_start__lte=day, time_end__gte=day, channel=self) daypart_set = None if daypart_sets.count() > 0: daypart_set = daypart_sets[0] if daypart_set: for dp in daypart_set.daypart_set.all(): dayparts.append(dp) return dayparts def get_on_air(self): """ merge currently playing item (told by pypo) with estimated scheduler entry for the emission """ now = datetime.datetime.now() emissions = self.scheduler_emissions.filter(channel__pk=self.pk, time_start__lte=now, time_end__gte=now) if emissions.count() > 0: emission = emissions[0] emission_url = emissions[0].get_api_url() emission_items = [] """ for e in emission.get_timestamped_media(): item = e.content_object emission_items.append({ 'pk': item.pk, 'time_start': e.timestamp, 'resource_uri': item.get_api_url() }) """ else: emission_url = None emission_items = [] try: item_url = self.on_air.get_api_url() except: item_url = None on_air = { 'item': item_url, 'emission': emission_url, 'emission_items': emission_items } return on_air
class Distributor(MigrationMixin): # core fields #uuid = UUIDField(primary_key=False) uuid = models.UUIDField(default=uuid.uuid4, editable=False) name = models.CharField(max_length=400) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) code = models.CharField(max_length=50) country = models.ForeignKey(Country, blank=True, null=True) address = models.TextField(blank=True, null=True) email = models.EmailField(blank=True, null=True) phone = PhoneNumberField(blank=True, null=True) fax = PhoneNumberField(blank=True, null=True) description = extra.MarkdownTextField(blank=True, null=True) first_placeholder = PlaceholderField('first_placeholder') # auto-update created = models.DateTimeField(auto_now_add=True, editable=False) updated = models.DateTimeField(auto_now=True, editable=False) # relations parent = models.ForeignKey('self', null=True, blank=True, related_name='children') labels = models.ManyToManyField('Label', through='DistributorLabel', blank=True, related_name="distributors") # user relations owner = models.ForeignKey(User, blank=True, null=True, related_name="distributors_owner", on_delete=models.SET_NULL) creator = models.ForeignKey(User, blank=True, null=True, related_name="distributors_creator", on_delete=models.SET_NULL) publisher = models.ForeignKey(User, blank=True, null=True, related_name="distributors_publisher", on_delete=models.SET_NULL) TYPE_CHOICES = ( ('unknown', _('Unknown')), ('major', _('Major')), ('indy', _('Independent')), ('other', _('Other')), ) type = models.CharField(verbose_name="Distributor type", max_length=12, default='unknown', choices=TYPE_CHOICES) # relations a.k.a. links relations = GenericRelation('Relation') # tagging (d_tags = "display tags") d_tags = tagging.fields.TagField(max_length=1024, verbose_name="Tags", blank=True, null=True) # manager objects = models.Manager() # meta class Meta: app_label = 'alibrary' verbose_name = _('Distributor') verbose_name_plural = _('Distributors') ordering = ('name', ) def __unicode__(self): return self.name @models.permalink def get_absolute_url(self): return ('alibrary-distributor-detail', [self.slug]) @models.permalink def get_edit_url(self): return ('alibrary-distributor-edit', [self.pk]) def save(self, *args, **kwargs): unique_slugify(self, self.name) # update d_tags t_tags = '' for tag in self.tags: t_tags += '%s, ' % tag self.tags = t_tags self.d_tags = t_tags super(Distributor, self).save(*args, **kwargs)
class Playlist(MigrationMixin, models.Model): name = models.CharField(max_length=200) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) # uuid = UUIDField() uuid = models.UUIDField(default=uuid.uuid4, editable=False) status = models.PositiveIntegerField( default=0, choices=alibrary_settings.PLAYLIST_STATUS_CHOICES) type = models.CharField(max_length=12, default='basket', null=True, choices=alibrary_settings.PLAYLIST_TYPE_CHOICES) broadcast_status = models.PositiveIntegerField( default=0, choices=alibrary_settings.PLAYLIST_BROADCAST_STATUS_CHOICES) broadcast_status_messages = JSONField(blank=True, null=True, default=None) EDIT_MODE_CHOICES = ( (0, _('Compact')), (1, _('Medium')), (2, _('Extended')), ) edit_mode = models.PositiveIntegerField(default=2, choices=EDIT_MODE_CHOICES) rotation = models.BooleanField(default=True) rotation_date_start = models.DateField(verbose_name=_('Rotate from'), blank=True, null=True) rotation_date_end = models.DateField(verbose_name=_('Rotate until'), blank=True, null=True) main_image = models.ImageField(verbose_name=_('Image'), upload_to=upload_image_to, storage=OverwriteStorage(), null=True, blank=True) # relations user = models.ForeignKey(User, null=True, blank=True, default=None) # media = models.ManyToManyField('Media', through='PlaylistMedia', blank=True, null=True) items = models.ManyToManyField('PlaylistItem', through='PlaylistItemPlaylist', blank=True) # tagging (d_tags = "display tags") d_tags = tagging.fields.TagField(max_length=1024, verbose_name="Tags", blank=True, null=True) # updated/calculated on save duration = models.IntegerField(null=True, default=0) target_duration = models.PositiveIntegerField( default=0, null=True, choices=alibrary_settings.PLAYLIST_TARGET_DURATION_CHOICES) dayparts = models.ManyToManyField(Daypart, blank=True, related_name='daypart_plalists') seasons = models.ManyToManyField('Season', blank=True, related_name='season_plalists') weather = models.ManyToManyField('Weather', blank=True, related_name='weather_plalists') # series series = models.ForeignKey(Series, null=True, blank=True, on_delete=models.SET_NULL) series_number = models.PositiveIntegerField(null=True, blank=True) # is currently selected as default? is_current = models.BooleanField(_('Currently selected?'), default=False) description = extra.MarkdownTextField(blank=True, null=True) # auto-update created = models.DateTimeField(auto_now_add=True, editable=False) updated = models.DateTimeField(auto_now=True, editable=False) mixdown_file = models.FileField(null=True, blank=True, upload_to=upload_mixdown_to) # meta class Meta: app_label = 'alibrary' verbose_name = _('Playlist') verbose_name_plural = _('Playlists') ordering = ('-updated', ) permissions = ( ('view_playlist', 'View Playlist'), ('edit_playlist', 'Edit Playlist'), ('schedule_playlist', 'Schedule Playlist'), ('admin_playlist', 'Edit Playlist (extended)'), ) def __unicode__(self): return self.name @property def sorted_items(self): return self.items.order_by('playlistitemplaylist__position') def get_absolute_url(self): return reverse('alibrary-playlist-detail', kwargs={ 'slug': self.slug, }) def get_edit_url(self): return reverse("alibrary-playlist-edit", args=(self.pk, )) def get_delete_url(self): return reverse("alibrary-playlist-delete", args=(self.pk, )) def get_admin_url(self): return reverse("admin:alibrary_playlist_change", args=(self.pk, )) def get_duration(self): duration = 0 try: for item in self.items.all(): duration += item.content_object.get_duration() pip = PlaylistItemPlaylist.objects.get(playlist=self, item=item) duration -= pip.cue_in duration -= pip.cue_out duration -= pip.fade_cross except: pass return duration def get_emissions(self): from abcast.models import Emission ctype = ContentType.objects.get_for_model(self) emissions = Emission.objects.filter( content_type__pk=ctype.id, object_id=self.id).order_by('-time_start') return emissions def get_api_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'library/playlist', 'pk': self.pk }) + '' def get_api_simple_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'library/simpleplaylist', 'pk': self.pk }) + '' def can_be_deleted(self): can_delete = False reason = _('This playlist cannot be deleted.') if self.type == 'basket': can_delete = True reason = None if self.type == 'playlist': can_delete = False reason = _( 'Playlist "%s" is public. It cannot be deleted anymore.' % self.name) if self.type == 'broadcast': can_delete = False reason = _( 'Playlist "%s" published for broadcast. It cannot be deleted anymore.' % self.name) return can_delete, reason def get_transform_status(self, target_type): """ check if transformation is possible / what needs to be done Not so nicely here - but... """ status = False """ criterias = [ { 'key': 'tags', 'name': _('Tags'), 'status': True, 'warning': _('Please add some tags'), }, { 'key': 'description', 'name': _('Description'), 'status': False, 'warning': _('Please add a description'), } ] """ criterias = [] # "basket" only used while dev... if target_type == 'basket': status = True if target_type == 'playlist': status = True # tags tag_count = self.tags.count() if tag_count < 1: status = False criteria = { 'key': 'tags', 'name': _('Tags'), 'status': tag_count > 0, 'warning': _('Please add some tags'), } criterias.append(criteria) # scheduled if self.type == 'broadcast': schedule_count = self.get_emissions().count() if schedule_count > 0: status = False criteria = { 'key': 'scheduled', 'name': _('Playlist already scheduled') if schedule_count > 0 else _('Playlist not scheduled'), 'status': schedule_count < 1, 'warning': _('This playlist has already ben scheduled %s times. Remove all scheduler entries to "un-broadcast" this playlist.' % schedule_count), } if schedule_count > 0: criterias.append(criteria) if target_type == 'broadcast': status = True # tags tag_count = self.tags.count() if tag_count < 1: status = False criteria = { 'key': 'tags', 'name': _('Tags'), 'status': tag_count > 0, 'warning': _('Please add some tags'), } criterias.append(criteria) # dayparts dp_count = self.dayparts.count() if not dp_count: status = False criteria = { 'key': 'dayparts', 'name': _('Dayparts'), 'status': dp_count > 0, 'warning': _('Please specify the dayparts'), } criterias.append(criteria) # duration if not self.broadcast_status == 1: status = False criteria = { 'key': 'duration', 'name': _('Duration'), 'status': True if self.broadcast_status == 1 else False, 'warning': _('Durations do not match'), # 'warning': ', '.join(self.broadcast_status_messages), } criterias.append(criteria) transformation = {'criterias': criterias, 'status': status} return transformation def add_items_by_ids(self, ids, ct, timing=None): from alibrary.models.mediamodels import Media log = logging.getLogger('alibrary.playlistmodels.add_items_by_ids') log.debug('Media ids: %s' % (ids)) log.debug('Content Type: %s' % (ct)) for id in ids: id = int(id) co = None if ct == 'media': co = Media.objects.get(pk=id) if ct == 'jingle': from abcast.models import Jingle co = Jingle.objects.get(pk=id) if co: i = PlaylistItem(content_object=co) i.save() """ ctype = ContentType.objects.get_for_model(co) item, created = PlaylistItem.objects.get_or_create(object_id=co.pk, content_type=ctype) """ pi, created = PlaylistItemPlaylist.objects.get_or_create( item=i, playlist=self, position=self.items.count()) if timing: try: pi.fade_in = timing['fade_in'] pi.fade_out = timing['fade_out'] pi.cue_in = timing['cue_in'] pi.cue_out = timing['cue_out'] pi.save() except: pass self.save() def reorder_items_by_uuids(self, uuids): i = 0 for uuid in uuids: pi = PlaylistItemPlaylist.objects.get(uuid=uuid) pi.position = i pi.save() i += 1 self.save() """ old method - for non-generic playlists """ def add_media_by_ids(self, ids): from alibrary.models.mediamodels import Media log = logging.getLogger('alibrary.playlistmodels.add_media_by_id') log.debug('Media ids: %s' % (ids)) for id in ids: id = int(id) m = Media.objects.get(pk=id) pm = PlaylistMedia(media=m, playlist=self, position=self.media.count()) pm.save() self.save() def convert_to(self, type): log.debug('requested to convert "%s" from %s to %s' % (self.name, self.type, type)) if type == 'broadcast': self.broadcast_status, self.broadcast_status_messages = self.self_check( ) transformation = self.get_transform_status(type) status = transformation['status'] if type == 'broadcast' and status: _status, messages = self.self_check() if _status == 1: status = True if status: self.type = type self.save() return self, status def get_items(self): pis = PlaylistItemPlaylist.objects.filter( playlist=self).order_by('position') items = [] for pi in pis: item = pi.item item.cue_in = pi.cue_in item.cue_out = pi.cue_out item.fade_in = pi.fade_in item.fade_out = pi.fade_out item.fade_cross = pi.fade_cross # get the actual playout duration try: # print '// getting duration for:' # print '%s - %s' % (item.content_object.pk, item.content_object.name) # print 'obj duration: %s' % item.content_object.duration_s item.playout_duration = item.content_object.duration_ms - item.cue_in - item.cue_out - item.fade_cross except Exception as e: print 'unable to get duration: %s' % e item.playout_duration = 0 items.append(item) return items def self_check(self): """ check if everything is fine to be 'schedulable' """ log.info('Self check requested for: %s' % self.name) status = 1 # set to 'OK' messages = [] try: # check ready-status of related media for item in self.items.all(): # log.debug('Self check content object: %s' % item.content_object) # log.debug('Self check master: %s' % item.content_object.master) # log.debug('Self check path: %s' % item.content_object.master.path) # check if file available try: with open(item.content_object.master.path): pass except IOError as e: log.warning( _('File does not exists: %s | %s') % (e, item.content_object.master.path)) status = 99 messages.append( _('File does not exists: %s | %s') % (e, item.content_object.master.path)) """ pip = PlaylistItemPlaylist.objects.get(playlist=self, item=item) duration -= pip.cue_in duration -= pip.cue_out duration -= pip.fade_cross """ # check duration & matching target_duration """ compare durations. target: in seconds | calculated duration in milliseconds """ diff = self.get_duration() - self.target_duration * 1000 if abs(diff) > DURATION_MAX_DIFF: messages.append( _('durations do not match. difference is: %s seconds' % int(diff / 1000))) log.warning( 'durations do not match. difference is: %s seconds' % int(diff / 1000)) status = 2 except Exception as e: messages.append(_('Validation error: %s ' % e)) log.warning('validation error: %s ' % e) status = 99 if status == 1: log.info('Playlist "%s" checked - all fine!' % (self.name)) return status, messages ################################################################### # playlist mixdown ################################################################### def get_mixdown(self): """ get mixdown from api """ return MixdownAPIClient().get_for_playlist(self) def request_mixdown(self): """ request (re-)creation of mixdown """ return MixdownAPIClient().request_for_playlist(self) def download_mixdown(self): """ download generated mixdown from api & store locally (in `mixdown_file` field) """ if not self.mixdown: log.info('mixdown not available on api') return if not self.mixdown['status'] == 3: log.info('mixdown not ready on api') return url = self.mixdown['mixdown_file'] log.debug('download mixdown from api: {} > {}'.format(url, self.name)) f_temp = NamedTemporaryFile(delete=True) f_temp.write(urlopen(url).read()) f_temp.flush() # wipe existing file try: self.mixdown_file.delete(False) except IOError: pass self.mixdown_file.save(url.split('/')[-1], File(f_temp)) return MixdownAPIClient().request_for_playlist(self) @cached_property def mixdown(self): return self.get_mixdown() def save(self, *args, **kwargs): # status update if self.status == 0: self.status = 2 duration = 0 try: duration = self.get_duration() except Exception as e: pass self.duration = duration """ TODO: maybe move """ self.broadcast_status, self.broadcast_status_messages = self.self_check( ) # print '%s - %s (id: %s)' % (self.broadcast_status, self.name, self.pk) # print ', '.join(self.broadcast_status_messages) # map to object status (not extremly dry - we know...) if self.broadcast_status == 1: self.status = 1 # 'ready' else: self.status = 99 # 'error' # self.user = request.user super(Playlist, self).save(*args, **kwargs)
class Release(MigrationMixin): # core fields name = models.CharField(max_length=200, db_index=True) slug = AutoSlugField(populate_from='name', editable=True, blank=True, overwrite=True) license = models.ForeignKey(License, blank=True, null=True, related_name='release_license') release_country = models.ForeignKey(Country, blank=True, null=True) #uuid = UUIDField(primary_key=False) #uuid = UUIDField() uuid = models.UUIDField(default=uuid.uuid4, editable=False) main_image = models.ImageField(verbose_name=_('Cover'), upload_to=upload_cover_to, storage=OverwriteStorage(), null=True, blank=True) if FORCE_CATALOGNUMBER: catalognumber = models.CharField(max_length=50) else: catalognumber = models.CharField(max_length=50, blank=True, null=True) """ releasedate stores the 'real' time, approx is for inputs lik 2012-12 etc. """ releasedate = models.DateField(blank=True, null=True) releasedate_approx = ApproximateDateField(verbose_name="Releasedate", blank=True, null=True) pressings = models.PositiveIntegerField(default=0) TOTALTRACKS_CHOICES = ((x, x) for x in range(1, 301)) totaltracks = models.IntegerField(verbose_name=_('Total Tracks'), blank=True, null=True, choices=TOTALTRACKS_CHOICES) asin = models.CharField(max_length=150, blank=True) RELEASESTATUS_CHOICES = ( (None, _('Not set')), ('official', _('Official')), ('promo', _('Promo')), ('bootleg', _('Bootleg')), ('other', _('Other')), ) releasestatus = models.CharField(max_length=60, blank=True, choices=RELEASESTATUS_CHOICES) excerpt = models.TextField(blank=True, null=True) description = extra.MarkdownTextField(blank=True, null=True) releasetype = models.CharField( verbose_name="Release type", max_length=24, blank=True, null=True, choices=alibrary_settings.RELEASETYPE_CHOICES) label = models.ForeignKey('alibrary.Label', blank=True, null=True, related_name='release_label', on_delete=models.SET_NULL) media = models.ManyToManyField('alibrary.Media', through='ReleaseMedia', blank=True, related_name="releases") owner = models.ForeignKey(User, blank=True, null=True, related_name="releases_owner", on_delete=models.SET_NULL) creator = models.ForeignKey(User, blank=True, null=True, related_name="releases_creator", on_delete=models.SET_NULL) last_editor = models.ForeignKey(User, blank=True, null=True, related_name="releases_last_editor", on_delete=models.SET_NULL) publisher = models.ForeignKey(User, blank=True, null=True, related_name="releases_publisher", on_delete=models.SET_NULL) barcode = models.CharField(max_length=32, blank=True, null=True) extra_artists = models.ManyToManyField('alibrary.Artist', through='ReleaseExtraartists', blank=True) album_artists = models.ManyToManyField('alibrary.Artist', through='ReleaseAlbumartists', related_name="release_albumartists", blank=True) relations = GenericRelation(Relation) d_tags = tagging.fields.TagField(max_length=1024, verbose_name="Tags", blank=True, null=True) created = models.DateTimeField(auto_now_add=True, editable=False) updated = models.DateTimeField(auto_now=True, editable=False) objects = ReleaseManager() # meta class Meta: app_label = 'alibrary' verbose_name = _('Release') verbose_name_plural = _('Releases') ordering = ('-created', ) permissions = ( ('view_release', 'View Release'), ('edit_release', 'Edit Release'), ('merge_release', 'Merge Releases'), ('admin_release', 'Edit Release (extended)'), ) def __unicode__(self): return self.name @property def classname(self): return self.__class__.__name__ @property def publish_date(self): # compatibility hack TODO: refactor all dependencies return datetime.utcnow() def get_extra_artists(self): ea = [] for artist in self.extra_artists.all(): ea.append(artist.name) return ea def is_active(self): now = date.today() try: if not self.releasedate: return True if self.releasedate <= now: return True except: pass return False @property def is_promotional(self): # TODO: refactor to license query if self.releasedate: if self.releasedate > datetime.now().date(): return True if License.objects.filter(media_license__in=self.get_media(), is_promotional=True).distinct().exists(): return True return False @property def is_new(self): if self.is_promotional: return False if self.releasedate and self.releasedate >= ( datetime.now() - timedelta(days=14)).date(): return True return False def get_lookup_providers(self): providers = [] for key, name in LOOKUP_PROVIDERS: relations = self.relations.filter(service=key) relation = None if relations.count() == 1: relation = relations[0] providers.append({'key': key, 'name': name, 'relation': relation}) return providers def get_absolute_url(self): return reverse('alibrary-release-detail', kwargs={ 'pk': self.pk, 'slug': self.slug, }) def get_edit_url(self): return reverse("alibrary-release-edit", args=(self.pk, )) def get_admin_url(self): return reverse("admin:alibrary_release_change", args=(self.pk, )) def get_api_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'library/release', 'pk': self.pk }) + '' def get_api_simple_url(self): return reverse('api_dispatch_detail', kwargs={ 'api_name': 'v1', 'resource_name': 'library/simplerelease', 'pk': self.pk }) + '' def get_media(self): from alibrary.models import Media return Media.objects.filter(release=self) def get_products(self): return self.releaseproduct.all() def get_media_indicator(self): media = self.get_media() indicator = [] if self.totaltracks: for i in range(self.totaltracks): indicator.append(0) for m in media: try: indicator[m.tracknumber - 1] = 3 except Exception as e: pass else: for m in media: indicator.append(2) return indicator def get_license(self): licenses = License.objects.filter( media_license__in=self.get_media()).distinct() if not licenses.exists(): return {'name': _(u'Not Defined')} if licenses.count() > 1: license, created = License.objects.get_or_create(name="Multiple") return license if licenses.count() == 1: return licenses[0] """ compose artist display as string """ def get_artist_display(self): artist_str = '' artists = self.get_artists() if len(artists) > 1: try: for artist in artists: if artist['join_phrase']: artist_str += ' %s ' % artist['join_phrase'] artist_str += artist['artist'].name except: artist_str = artists[0]['artist'].name else: try: artist_str = artists[0]['artist'].name except: try: artist_str = artists[0].name except: artist_str = _('Unknown Artist') return artist_str def get_artists(self): artists = [] if self.album_artists.count() > 0: for albumartist in self.release_albumartist_release.all(): artists.append({ 'artist': albumartist.artist, 'join_phrase': albumartist.join_phrase }) return artists medias = self.get_media() for media in medias: artists.append(media.artist) artists = list(set(artists)) if len(artists) > 1: from alibrary.models import Artist a, c = Artist.objects.get_or_create(name="Various Artists") artists = [a] return artists def get_extra_artists(self): artists = [] roles = ReleaseExtraartists.objects.filter(release=self.pk) for role in roles: try: role.artist.profession = role.profession.name artists.append(role.artist) except: pass return artists def get_downloads(self): return None def get_download_url(self, format, version): return '%sdownload/%s/%s/' % (self.get_absolute_url(), format, version) def get_cache_file_path(self, format, version): tmp_directory = TEMP_DIR file_name = '%s_%s_%s.%s' % (format, version, str(self.uuid), 'zip') tmp_path = '%s/%s' % (tmp_directory, file_name) return tmp_path def clear_cache_file(self): tmp_directory = TEMP_DIR pattern = '*%s.zip' % (str(self.uuid)) versions = glob.glob('%s/%s' % (tmp_directory, pattern)) try: for version in versions: os.remove(version) except Exception as e: pass def get_cache_file(self, format, version): cache_file_path = self.get_cache_file_path(format, version) if os.path.isfile(cache_file_path): logger.info('serving from cache: %s' % (cache_file_path)) return cache_file_path else: return self.build_cache_file(format, version) def build_cache_file(self, format, version): cache_file_path = self.get_cache_file_path(format, version) logger.info('building cache for: %s' % (cache_file_path)) try: os.remove(cache_file_path) except Exception as e: pass archive_file = ZipFile(cache_file_path, "w") """ adding corresponding media files """ for media in self.get_media(): media_cache_file = media.inject_metadata(format, version) # filename for the file archive file_name = '%02d - %s - %s' % (media.tracknumber, media.artist.name, media.name) file_name = '%s.%s' % (file_name.encode('ascii', 'ignore'), format) archive_file.write(media_cache_file.path, file_name) return cache_file_path def get_extraimages(self): return None # OBSOLETE def complete_by_mb_id(self, mb_id): obj = self log = logging.getLogger('alibrary.release.complete_by_mb_id') log.info('complete release, r: %s | mb_id: %s' % (obj.name, mb_id)) inc = ('artists', 'url-rels', 'aliases', 'tags', 'recording-rels', 'work-rels', 'work-level-rels', 'artist-credits') url = 'http://%s/ws/2/release/%s/?fmt=json&inc=%s' % ( MUSICBRAINZ_HOST, mb_id, "+".join(inc)) r = requests.get(url) result = r.json() return obj def save(self, *args, **kwargs): self.clear_cache_file() unique_slugify(self, self.name) # convert approx date to real one ad = self.releasedate_approx try: ad_y = ad.year ad_m = ad.month ad_d = ad.day if ad_m == 0: ad_m = 1 if ad_d == 0: ad_d = 1 rd = datetime.strptime('%s/%s/%s' % (ad_y, ad_m, ad_d), '%Y/%m/%d') self.releasedate = rd except: self.releasedate = None super(Release, self).save(*args, **kwargs)