You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

82 lines
2.6 KiB

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