from django.db import models from django.utils.translation import gettext_lazy as _ from utils import generate_slug_for_model class PodcastCategory(models.Model): title = models.CharField(max_length=255, verbose_name=_('title')) slug = models.SlugField(allow_unicode=True, unique=True, verbose_name=_('slug')) status = models.BooleanField(default=True, verbose_name=_('status')) order = models.PositiveIntegerField(default=0, verbose_name=_('order')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def __str__(self): return self.title def save(self, *args, **kwargs): if not self.slug: self.slug = generate_slug_for_model(PodcastCategory, self.title) super().save(*args, **kwargs) class Meta: verbose_name = _('Podcast Category') verbose_name_plural = _('Podcast Categories') ordering = ['order'] class PodcastCollection(models.Model): class DisplayPosition(models.TextChoices): PINNED = 'pinned', _('Pinned') MIDDLE = 'middle', _('Middle Section') title = models.CharField(max_length=255, help_text="This title will not be displayed anywhere") slug = models.SlugField(max_length=255, unique=True) summary = models.CharField(max_length=512, null=True, blank=True, help_text=_('could be null')) pin_top = models.BooleanField(_('pin top'), default=True) thumbnail = models.ImageField(upload_to='podcast/collection/', null=True, blank=True, help_text=_('image allowed')) order = models.IntegerField(default=0, verbose_name=_('order')) status = models.BooleanField(default=True, verbose_name=_('status')) display_position = models.CharField( max_length=20, choices=DisplayPosition.choices, default=DisplayPosition.PINNED, verbose_name=_('Display Position') ) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def __str__(self): return f'Collection #{self.id}/{self.title}' def save(self, *args, **kwargs): if not self.slug: self.slug = generate_slug_for_model(PodcastCollection, self.title) super().save(*args, **kwargs) class Meta: verbose_name = _('Podcast Collection') verbose_name_plural = _('Podcasts Collections') class PinnedPodcastCollection(PodcastCollection): class Meta: proxy = True verbose_name = _('Pinned Collection (Top Section)') verbose_name_plural = _('Pinned Collections (Top Section)') class MiddlePodcastCollection(PodcastCollection): class Meta: proxy = True verbose_name = _('Regular Collection (Middle Section)') verbose_name_plural = _('Regular Collections (Middle Section)') class Podcast(models.Model): title = models.CharField(max_length=255, null=True) slug = models.SlugField(allow_unicode=True, unique=True) thumbnail = models.ImageField(upload_to='book_thumbnails/', null=True, blank=True, help_text=_('image allowed')) description = models.TextField(null=True) categories = models.ManyToManyField(PodcastCategory, related_name='podcasts', verbose_name=_('categories'), blank=True) audio_file = models.FileField(upload_to='podcast/audio/', null=True, blank=True) audio_time = models.TimeField() view_count = models.PositiveBigIntegerField(default=0, verbose_name=_('view count')) download_count = models.PositiveBigIntegerField(default=0, verbose_name=_('download_count view count')) status = models.BooleanField(default=True, verbose_name=_('status')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def __str__(self): return self.title def increment_view_count(self): self.view_count += 1 self.save(update_fields=['view_count']) return self.view_count def save(self, *args, **kwargs): if not self.slug: self.slug = generate_slug_for_model(Podcast, self.title) super().save(*args, **kwargs) class Meta: verbose_name = _('Podcast') verbose_name_plural = _('Podcasts') class PodcastPlaylist(models.Model): title = models.CharField(max_length=255, verbose_name=_('title')) slug = models.SlugField(allow_unicode=True, unique=True, null=True, blank=True, verbose_name=_('slug')) slogan = models.CharField(max_length=512, null=True, blank=True, verbose_name=_('slogan')) description = models.TextField(null=True, blank=True, verbose_name=_('description')) thumbnail = models.ImageField(upload_to='podcast/playlist/thumbnails/', null=True, blank=True, verbose_name=_('thumbnail')) categories = models.ManyToManyField( PodcastCategory, related_name='playlists', verbose_name=_('categories'), blank=True, ) collections = models.ManyToManyField( PodcastCollection, through='PodcastPlaylistInCollection', related_name='related_playlists', verbose_name=_('collections'), blank=True ) order = models.PositiveIntegerField(default=0, verbose_name=_('order')) status = models.BooleanField(default=True, verbose_name=_('status')) view_count = models.PositiveBigIntegerField(default=0, verbose_name=_('view count')) total_time = models.DurationField(null=True, blank=True, verbose_name=_('total time')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def __str__(self): return self.title def increment_view_count(self): """Increment the view count for this playlist""" self.view_count += 1 self.save(update_fields=['view_count']) return self.view_count def calculate_total_time(self): """Calculate total duration of all podcasts in this playlist""" from datetime import timedelta total_seconds = 0 for item in self.playlist_items.select_related('podcast'): audio_time = item.podcast.audio_time total_seconds += audio_time.hour * 3600 + audio_time.minute * 60 + audio_time.second return timedelta(seconds=total_seconds) def save(self, *args, **kwargs): if not self.slug: self.slug = generate_slug_for_model(PodcastPlaylist, self.title) super().save(*args, **kwargs) class Meta: verbose_name = _('Podcast Playlist') verbose_name_plural = _('Podcast Playlists') ordering = ['order', '-created_at'] class PodcastPlaylistInCollection(models.Model): collection = models.ForeignKey( PodcastCollection, on_delete=models.CASCADE, related_name='collection_playlists', verbose_name=_('collection') ) playlist = models.ForeignKey( PodcastPlaylist, on_delete=models.CASCADE, related_name='playlist_collections', verbose_name=_('playlist') ) order = models.PositiveIntegerField(default=0, verbose_name=_('order')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def __str__(self): return f"{self.collection.title} - {self.playlist.title}" class Meta: verbose_name = _('Podcast Playlist in Collection') verbose_name_plural = _('Podcast Playlists in Collections') ordering = ['order'] unique_together = ['collection', 'playlist'] class PlaylistItem(models.Model): playlist = models.ForeignKey( PodcastPlaylist, on_delete=models.CASCADE, related_name='playlist_items', verbose_name=_('playlist') ) podcast = models.ForeignKey( Podcast, on_delete=models.CASCADE, related_name='playlist_appearances', verbose_name=_('podcast') ) priority = models.PositiveIntegerField(default=0, verbose_name=_('priority')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def __str__(self): return f"{self.playlist.title} - {self.podcast.title} (Priority: {self.priority})" class Meta: verbose_name = _('Playlist Item') verbose_name_plural = _('Playlist Items') ordering = ['priority'] unique_together = ['playlist', 'podcast'] from django.contrib.auth import get_user_model User = get_user_model() class UserPlaylist(models.Model): """ Model to track which podcasts a user has added to their personal playlist """ user = models.ForeignKey( User, on_delete=models.CASCADE, related_name='podcast_playlists', verbose_name=_('user') ) podcast = models.ForeignKey( Podcast, on_delete=models.CASCADE, related_name='user_playlists', verbose_name=_('podcast') ) status = models.BooleanField(default=True, verbose_name=_('status')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) class Meta: verbose_name = _('User Playlist') verbose_name_plural = _('User Playlists') unique_together = ['user', 'podcast'] def __str__(self): return f"{self.user.username} - {self.podcast.title}" @classmethod def is_in_user_playlist(cls, user, podcast): """ Check if a podcast is in a user's playlist and active Args: user: User instance podcast: Podcast instance Returns: Boolean indicating if the podcast is in the user's playlist and active """ return cls.objects.filter( user=user, podcast=podcast, status=True ).exists()