Spaces:
Paused
Paused
Download app/video_processor.py from ankitverma304/video: direct link, hf CLI and curl.
- Browser
- Download file 3.79 kB
-
https://huggingface.co/spaces/ankitverma304/video/resolve/main/app/video_processor.py
- Command line
-
hf download hf://spaces/ankitverma304/video/app/video_processor.py
-
curl -L -o video_processor.py https://huggingface.co/spaces/ankitverma304/video/resolve/main/app/video_processor.py
3.79 kB
| """Video cropping and clip concatenation using MoviePy.""" | |
| import logging | |
| import traceback | |
| from datetime import datetime | |
| from pathlib import Path | |
| # Support both moviepy 1.x (moviepy.editor) and 2.x (moviepy) | |
| try: | |
| from moviepy import VideoFileClip, concatenate_videoclips # 2.x | |
| except ImportError: | |
| from moviepy.editor import VideoFileClip, concatenate_videoclips # 1.x | |
| from app.utils import format_duration | |
| from config.settings import settings | |
| logger = logging.getLogger("media_intelligence.video_processor") | |
| def _subclip(clip, start: float, end: float): | |
| """Call the correct subclip method for moviepy 1.x and 2.x.""" | |
| if hasattr(clip, "subclipped"): | |
| return clip.subclipped(start, end) # moviepy 2.x | |
| return clip.subclip(start, end) # moviepy 1.x | |
| def crop_video(video_path: str, segments: list) -> tuple: | |
| """ | |
| Extract and concatenate the requested time segments from *video_path*. | |
| Args: | |
| video_path: Path to the source video file. | |
| segments: List of dicts with 'start' and 'end' keys (seconds). | |
| Returns: | |
| (output_path, duration_str) | |
| Raises: | |
| ValueError: No valid segments provided. | |
| RuntimeError: MoviePy processing failure. | |
| """ | |
| if not segments: | |
| raise ValueError( | |
| "No segments provided for cropping. " | |
| "Please select at least one segment and try again." | |
| ) | |
| video_path = Path(video_path) | |
| logger.info(f"Crop start: {video_path.name} — {len(segments)} segment(s)") | |
| output_dir = Path(settings.OUTPUT_DIR) | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| output_path = output_dir / f"short_video_{timestamp}.mp4" | |
| source = None | |
| clips = [] | |
| try: | |
| source = VideoFileClip(str(video_path)) | |
| total_duration = source.duration | |
| logger.info(f"Source duration: {format_duration(total_duration)}") | |
| for seg in segments: | |
| start = float(seg["start"]) | |
| end = float(seg["end"]) | |
| start = max(0.0, min(start, total_duration)) | |
| end = max(0.0, min(end, total_duration)) | |
| if start >= end: | |
| logger.warning(f"Skipping invalid segment: {start:.1f}s – {end:.1f}s") | |
| continue | |
| logger.info(f"Extracting: {start:.1f}s → {end:.1f}s") | |
| clips.append(_subclip(source, start, end)) | |
| if not clips: | |
| raise ValueError( | |
| "All selected segments are outside the video duration. " | |
| "Please verify the video and re-analyse." | |
| ) | |
| logger.info(f"Joining {len(clips)} clip(s)…") | |
| final = concatenate_videoclips(clips) | |
| duration_str = format_duration(final.duration) | |
| logger.info(f"Writing: {output_path}") | |
| final.write_videofile( | |
| str(output_path), | |
| codec="libx264", | |
| audio_codec="aac", | |
| verbose=False, | |
| logger=None, | |
| ) | |
| final.close() | |
| logger.info(f"Crop complete: {output_path.name} ({duration_str})") | |
| return str(output_path), duration_str | |
| except (ValueError, RuntimeError): | |
| raise | |
| except Exception as exc: | |
| msg = ( | |
| f"Video processing failed. " | |
| f"Possible reason: corrupt file or unsupported codec. " | |
| f"Detail: {exc}" | |
| ) | |
| logger.error(f"{msg}\n{traceback.format_exc()}") | |
| raise RuntimeError(msg) from exc | |
| finally: | |
| for clip in clips: | |
| try: | |
| clip.close() | |
| except Exception: | |
| pass | |
| if source is not None: | |
| try: | |
| source.close() | |
| except Exception: | |
| pass | |