From b882791f0f52fea9a92384b70de98a7de0c95a9e Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Tue, 14 Jul 2026 12:36:37 +0330 Subject: [PATCH] media duration auto calculate feature added --- apps/podcast/models.py | 33 +++++++++++++++++++++++++++++++++ apps/video/models.py | 33 +++++++++++++++++++++++++++++++++ utils/media.py | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 utils/media.py diff --git a/apps/podcast/models.py b/apps/podcast/models.py index a13fac0..5a9fb61 100644 --- a/apps/podcast/models.py +++ b/apps/podcast/models.py @@ -105,7 +105,40 @@ class Podcast(LowercaseSlugMixin, models.Model): return self.view_count def save(self, *args, **kwargs): + is_new = self.pk is None + file_changed = False + + if not is_new: + try: + old_instance = Podcast.objects.get(pk=self.pk) + if old_instance.audio_file != self.audio_file: + file_changed = True + except Podcast.DoesNotExist: + pass + else: + file_changed = True + + if not self.audio_time: + import datetime + self.audio_time = datetime.time(0, 0, 0) + super().save(*args, **kwargs) + + if (file_changed or self.audio_time == datetime.time(0, 0, 0)) and self.audio_file: + from utils.media import get_media_duration_time + try: + duration_time = get_media_duration_time(self.audio_file.path) + if duration_time: + self.audio_time = duration_time + Podcast.objects.filter(pk=self.pk).update(audio_time=duration_time) + + # Update playlists that contain this podcast + for appearance in self.playlist_appearances.all(): + playlist = appearance.playlist + playlist.total_time = playlist.calculate_total_time() + playlist.save(update_fields=['total_time']) + except Exception as e: + pass diff --git a/apps/video/models.py b/apps/video/models.py index 683a7f4..6a995a5 100644 --- a/apps/video/models.py +++ b/apps/video/models.py @@ -125,7 +125,40 @@ class Video(LowercaseSlugMixin, models.Model): }) def save(self, *args, **kwargs): + is_new = self.pk is None + file_changed = False + + if not is_new: + try: + old_instance = Video.objects.get(pk=self.pk) + if old_instance.video_file != self.video_file: + file_changed = True + except Video.DoesNotExist: + pass + else: + file_changed = True + + if not self.video_time: + import datetime + self.video_time = datetime.time(0, 0, 0) + super().save(*args, **kwargs) + + if (file_changed or self.video_time == datetime.time(0, 0, 0)) and self.video_file and self.video_type == self.VedioTypeChoices.VIDEO_FILE: + from utils.media import get_media_duration_time + try: + duration_time = get_media_duration_time(self.video_file.path) + if duration_time: + self.video_time = duration_time + Video.objects.filter(pk=self.pk).update(video_time=duration_time) + + # Update playlists that contain this video + for appearance in self.playlist_appearances.all(): + playlist = appearance.playlist + playlist.total_time = playlist.calculate_total_time() + playlist.save(update_fields=['total_time']) + except Exception as e: + pass diff --git a/utils/media.py b/utils/media.py new file mode 100644 index 0000000..a225229 --- /dev/null +++ b/utils/media.py @@ -0,0 +1,38 @@ +import datetime +import logging +from tinytag import TinyTag + +logger = logging.getLogger(__name__) + +def get_media_duration_seconds(file_path): + """ + Given a local file path, reads the duration using tinytag. + Returns duration in seconds (float) or None if error occurs. + """ + try: + tag = TinyTag.get(file_path) + return tag.duration + except Exception as e: + logger.error(f"Error reading media duration for {file_path}: {e}") + return None + +def get_media_duration_time(file_path): + """ + Given a local file path, reads the duration and returns a datetime.time object. + Caps hours at 23 to comply with Django TimeField limits. + """ + duration_seconds = get_media_duration_seconds(file_path) + if duration_seconds is None: + return None + + seconds = int(duration_seconds) + hours = seconds // 3600 + if hours > 23: + hours = 23 + minutes = 59 + secs = 59 + else: + minutes = (seconds % 3600) // 60 + secs = seconds % 60 + + return datetime.time(hour=hours, minute=minutes, second=secs)