import datetime import logging import subprocess 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) def extract_video_frame_bytes(file_path, timestamp_sec=1.0): """ Extracts a single frame from a video file as JPEG bytes. First attempts seeking to timestamp_sec (default 1.0s), then falls back to 0.0s. Returns bytes or None if extraction fails. """ try: import imageio_ffmpeg ffmpeg_exe = imageio_ffmpeg.get_ffmpeg_exe() # Try seeking to timestamp_sec first cmd = [ ffmpeg_exe, '-ss', str(timestamp_sec), '-i', str(file_path), '-vframes', '1', '-f', 'image2', '-c:v', 'mjpeg', 'pipe:1' ] res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15) if res.returncode == 0 and res.stdout: return res.stdout # Fallback to start of video cmd_fallback = [ ffmpeg_exe, '-i', str(file_path), '-vframes', '1', '-f', 'image2', '-c:v', 'mjpeg', 'pipe:1' ] res_fb = subprocess.run(cmd_fallback, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15) if res_fb.returncode == 0 and res_fb.stdout: return res_fb.stdout logger.warning(f"Failed to extract video frame from {file_path}: {res.stderr.decode('utf-8', errors='ignore')}") return None except Exception as e: logger.error(f"Error extracting video frame from {file_path}: {e}") return None