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
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
|