Created
January 6, 2026 20:22
-
-
Save soravux/9392a6facc12778813c9074d1fe581a9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import subprocess | |
| import os | |
| from concurrent.futures import ProcessPoolExecutor, as_completed | |
| from pathlib import Path | |
| #FFPROBE = r"ffmpeg\ffprobe.exe" | |
| #FFMPEG = r"ffmpeg\ffmpeg.exe" | |
| FFPROBE = r"ffprobe" | |
| FFMPEG = r"ffmpeg" | |
| VIDEOS_DIR = "/nvme/2025_fred/panflow/" | |
| NUM_WORKERS = 5 | |
| def check_and_convert(video_path: Path) -> str: | |
| """Check if video has spherical metadata type 3 and convert if needed.""" | |
| try: | |
| # Run ffprobe to check for spherical metadata | |
| result = subprocess.run( | |
| [FFPROBE, str(video_path)], | |
| capture_output=True, | |
| text=True, | |
| encoding="utf-8", | |
| errors="replace" | |
| ) | |
| # Check stderr for the spherical metadata message (ffprobe outputs info to stderr) | |
| #output = result.stderr | |
| output = result.stderr | |
| if "Unknown spherical metadata type 3" not in output: | |
| return f"[SKIP] {video_path.name}: No spherical metadata type 3 found" | |
| # Build output filename | |
| stem = video_path.stem | |
| ext = video_path.suffix | |
| output_path = video_path.parent / f"{stem}_equirect{ext}" | |
| # Skip if output already exists | |
| if output_path.exists(): | |
| return f"[SKIP] {video_path.name}: Output already exists" | |
| # Run ffmpeg conversion | |
| cmd = [ | |
| FFMPEG, | |
| "-i", str(video_path), | |
| "-vf", "v360=eac:equirect:yaw=180:pitch=0:roll=0", | |
| "-an", | |
| str(output_path) | |
| ] | |
| convert_result = subprocess.run( | |
| cmd, | |
| capture_output=False, | |
| text=True, | |
| encoding="utf-8", | |
| errors="replace" | |
| ) | |
| if convert_result.returncode == 0: | |
| return f"[OK] {video_path.name} -> {output_path.name}" | |
| else: | |
| return f"[ERROR] {video_path.name}: ffmpeg failed" | |
| except Exception as e: | |
| return f"[ERROR] {video_path.name}: {str(e)}" | |
| def main(): | |
| videos_path = Path(VIDEOS_DIR) | |
| # Get all video files | |
| video_extensions = {".mp4", ".webm", ".mkv", ".avi", ".mov"} | |
| video_files = [ | |
| f for f in videos_path.iterdir() | |
| if f.is_file() and f.suffix.lower() in video_extensions and "_equirect" not in f.stem | |
| ] | |
| # Sort files by filename in ascending order | |
| video_files.sort(key=lambda x: x.name) | |
| print(f"Found {len(video_files)} video files to process") | |
| print(f"Using {NUM_WORKERS} workers") | |
| print("-" * 60) | |
| with ProcessPoolExecutor(max_workers=NUM_WORKERS) as executor: | |
| futures = {executor.submit(check_and_convert, video): video for video in video_files} | |
| for future in as_completed(futures): | |
| result = future.result() | |
| print(result) | |
| print("-" * 60) | |
| print("Done!") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment