Enhance media API with video file validation and Docker configuration update
- Added a function to check if video files are complete and valid using ffprobe, preventing errors during thumbnail generation. - Updated thumbnail generation logic to skip incomplete or corrupted files, improving robustness. - Modified docker-compose.yml to include a restart policy for the camera management API service, ensuring better container reliability.
This commit is contained in:
@@ -5,6 +5,7 @@ services:
|
|||||||
context: ./camera-management-api
|
context: ./camera-management-api
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
working_dir: /app
|
working_dir: /app
|
||||||
|
restart: unless-stopped # Automatically restart container if it fails or exits
|
||||||
volumes:
|
volumes:
|
||||||
- ./camera-management-api:/app
|
- ./camera-management-api:/app
|
||||||
- /mnt/nfs_share:/mnt/nfs_share
|
- /mnt/nfs_share:/mnt/nfs_share
|
||||||
|
|||||||
@@ -138,13 +138,45 @@ def file_id_from_path(p: pathlib.Path) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def path_from_file_id(fid: str) -> pathlib.Path:
|
def path_from_file_id(fid: str) -> pathlib.Path:
|
||||||
rel = urllib.parse.unquote_plus(fid)
|
# Handle double-encoding: decode until no more changes
|
||||||
|
rel = fid
|
||||||
|
while True:
|
||||||
|
decoded = urllib.parse.unquote_plus(rel)
|
||||||
|
if decoded == rel:
|
||||||
|
break
|
||||||
|
rel = decoded
|
||||||
p = (MEDIA_DIR / rel).resolve()
|
p = (MEDIA_DIR / rel).resolve()
|
||||||
if not p.is_file() or MEDIA_DIR not in p.parents:
|
if not p.is_file() or MEDIA_DIR not in p.parents:
|
||||||
raise HTTPException(status_code=404, detail="Video not found")
|
raise HTTPException(status_code=404, detail="Video not found")
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
def is_video_file_complete(p: pathlib.Path) -> bool:
|
||||||
|
"""
|
||||||
|
Check if video file is complete and valid using ffprobe.
|
||||||
|
Returns True if file is complete and can be processed, False otherwise.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Quick check: use ffprobe to verify file is valid
|
||||||
|
cmd = [
|
||||||
|
"ffprobe",
|
||||||
|
"-v", "error",
|
||||||
|
"-show_entries", "format=duration",
|
||||||
|
"-of", "default=noprint_wrappers=1:nokey=1",
|
||||||
|
str(p)
|
||||||
|
]
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
# If ffprobe succeeds, file is valid
|
||||||
|
return result.returncode == 0
|
||||||
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, Exception):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def generate_thumbnail_background(p: pathlib.Path, width: int = 320, height: int = 180) -> bool:
|
def generate_thumbnail_background(p: pathlib.Path, width: int = 320, height: int = 180) -> bool:
|
||||||
"""
|
"""
|
||||||
Generate thumbnail in background (non-blocking, doesn't raise exceptions).
|
Generate thumbnail in background (non-blocking, doesn't raise exceptions).
|
||||||
@@ -188,13 +220,19 @@ def generate_thumbnail_background(p: pathlib.Path, width: int = 320, height: int
|
|||||||
except OSError:
|
except OSError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Check if video file is complete and valid before attempting thumbnail generation
|
||||||
|
# This prevents errors with incomplete/corrupted files
|
||||||
|
if not is_video_file_complete(p):
|
||||||
|
return False # File is incomplete or corrupted, skip
|
||||||
|
|
||||||
# Try to generate thumbnail - try 1s first, then 0s if that fails
|
# Try to generate thumbnail - try 1s first, then 0s if that fails
|
||||||
for seek_time in [1.0, 0.0]:
|
for seek_time in [1.0, 0.0]:
|
||||||
cmd = [
|
cmd = [
|
||||||
"ffmpeg", "-y",
|
"ffmpeg", "-y",
|
||||||
"-ss", str(seek_time),
|
"-ss", str(seek_time),
|
||||||
"-i", str(p),
|
"-i", str(p),
|
||||||
"-vframes", "1",
|
"-frames:v", "1",
|
||||||
|
"-update", "1", # Required for single image output with image2 muxer
|
||||||
"-vf", f"scale='min({width},iw)':-2,scale={width}:{height}:force_original_aspect_ratio=decrease,pad={width}:{height}:(ow-iw)/2:(oh-ih)/2",
|
"-vf", f"scale='min({width},iw)':-2,scale={width}:{height}:force_original_aspect_ratio=decrease,pad={width}:{height}:(ow-iw)/2:(oh-ih)/2",
|
||||||
str(out)
|
str(out)
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user