Spaces:
Sleeping
Sleeping
| """Video processing utilities for the DRS application. | |
| This module provides helper functions to save uploaded videos to the | |
| filesystem and to trim the last N seconds from a video. Using | |
| OpenCV's ``VideoCapture`` and ``VideoWriter`` avoids external | |
| dependencies like ffmpeg or moviepy, which may not be installed in | |
| all execution environments. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import shutil | |
| from pathlib import Path | |
| from typing import Union | |
| import cv2 | |
| def save_uploaded_video(name: str, file_obj: Union[bytes, str, Path]) -> str: | |
| """Persist an uploaded video to a predictable location on disk. | |
| When a user records or uploads a video in the Gradio interface, it | |
| arrives as a temporary file object. To analyse the video later we | |
| copy it into the working directory using its original filename. | |
| Parameters | |
| ---------- | |
| name: str | |
| The original filename from the upload widget. | |
| file_obj: Union[bytes, str, Path] | |
| The file-like object representing the uploaded video. Gradio | |
| passes the file as a ``gradio.Files`` object whose `.name` | |
| property holds the temporary path. This function accepts | |
| either the temporary path or an open file handle. | |
| Returns | |
| ------- | |
| str | |
| The absolute path where the video has been saved. | |
| """ | |
| # Determine a safe output directory. Use the current working | |
| # directory so that Gradio can later access the file by path. | |
| output_dir = Path(os.getcwd()) / "user_videos" | |
| output_dir.mkdir(exist_ok=True) | |
| # Compose an output filename; avoid overwriting by prefixing with an | |
| # incrementing integer if necessary. | |
| base_name = Path(name).stem | |
| ext = Path(name).suffix or ".mp4" | |
| counter = 0 | |
| dest = output_dir / f"{base_name}{ext}" | |
| while dest.exists(): | |
| counter += 1 | |
| dest = output_dir / f"{base_name}_{counter}{ext}" | |
| # If file_obj is a path, simply copy it; otherwise, read and write | |
| if isinstance(file_obj, (str, Path)): | |
| shutil.copy(str(file_obj), dest) | |
| else: | |
| # Gradio passes a file-like object with a `.read()` method | |
| with open(dest, "wb") as f_out: | |
| f_out.write(file_obj.read()) | |
| return str(dest) | |
| def trim_last_seconds(input_path: str, output_path: str, seconds: int) -> None: | |
| """Save the last ``seconds`` of a video to ``output_path``. | |
| This function reads the entire video file, calculates the starting | |
| frame corresponding to ``seconds`` before the end, and writes the | |
| remaining frames to a new video using OpenCV. If the video is | |
| shorter than the requested duration, the whole video is copied. | |
| Parameters | |
| ---------- | |
| input_path: str | |
| Path to the source video file. | |
| output_path: str | |
| Path where the trimmed video will be saved. | |
| seconds: int | |
| The duration from the end of the video to retain. | |
| """ | |
| cap = cv2.VideoCapture(input_path) | |
| if not cap.isOpened(): | |
| raise RuntimeError(f"Unable to open video: {input_path}") | |
| fps = cap.get(cv2.CAP_PROP_FPS) | |
| total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) | |
| if fps <= 0: | |
| fps = 30.0 # default fallback | |
| frames_to_keep = int(seconds * fps) | |
| start_frame = max(total_frames - frames_to_keep, 0) | |
| # Prepare writer with the same properties as the input | |
| width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) | |
| height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) | |
| fourcc = cv2.VideoWriter_fourcc(*"mp4v") | |
| out = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) | |
| # Skip frames until start_frame | |
| current = 0 | |
| while current < start_frame: | |
| ret, _ = cap.read() | |
| if not ret: | |
| break | |
| current += 1 | |
| # Write remaining frames | |
| while True: | |
| ret, frame = cap.read() | |
| if not ret: | |
| break | |
| out.write(frame) | |
| cap.release() | |
| out.release() |