Skip to content

Instantly share code, notes, and snippets.

@Ryan-Haines
Created October 27, 2025 04:00
Show Gist options
  • Select an option

  • Save Ryan-Haines/81829db1a92f3fe0bec932a43b9f7be6 to your computer and use it in GitHub Desktop.

Select an option

Save Ryan-Haines/81829db1a92f3fe0bec932a43b9f7be6 to your computer and use it in GitHub Desktop.
Script to flip all videos in a folder by 180 degrees, using ffmpeg with vulkan APIs
#!/usr/bin/env python3
import subprocess, sys, shutil
from pathlib import Path
# --- Config -------------------------------------------------------------
# Will try all methods until one works: vulkan -> cuda -> opencl
# Encoder: h264_nvenc is fast and widely compatible on NVIDIA.
# You can switch to "hevc_nvenc" or "av1_nvenc" if you prefer.
VIDEO_ENCODER = "h264_nvenc"
# Quality/Speed trade-offs (NVENC). Match original quality.
NVENC_ARGS = [
"-preset", "p5", # p1 slowest/best -> p7 fastest
"-cq", "23", # 23 is more reasonable (lower = higher quality)
"-b:v", "0" # Let CQ control quality
]
# File types to process
EXTS = {".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v", ".mts", ".m2ts", ".ts", ".wmv"}
# -----------------------------------------------------------------------
def have(cmd: str) -> bool:
return shutil.which(cmd) is not None
if not have("ffmpeg"):
sys.exit("ffmpeg not found on PATH")
if not have("ffprobe"):
print("Warning: ffprobe not found; proceeding without it.", file=sys.stderr)
def out_name(p: Path) -> Path:
return p.with_name(p.stem + "_flipped" + p.suffix)
def get_video_bitrate(inp: Path) -> str:
"""Get the original video bitrate to match it"""
try:
cmd = [
"ffprobe", "-v", "error", "-select_streams", "v:0",
"-show_entries", "stream=bit_rate", "-of", "default=noprint_wrappers=1:nokey=1",
str(inp)
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
if result.returncode == 0 and result.stdout.strip():
bitrate = int(result.stdout.strip())
return str(bitrate)
except:
pass
return None
def get_all_methods():
"""Return all methods to try in order of preference"""
return [
{
"name": "Vulkan GPU",
"vf": "hwupload,hflip_vulkan,vflip_vulkan,hwdownload,format=yuv420p",
"hwargs": ["-init_hw_device", "vulkan"],
"vcodec": VIDEO_ENCODER
},
{
"name": "CUDA GPU",
"vf": "hwupload_cuda,hflip_cuda,vflip_cuda,hwdownload,format=yuv420p",
"hwargs": ["-hwaccel", "cuda"],
"vcodec": VIDEO_ENCODER
},
{
"name": "OpenCL GPU",
"vf": "format=nv12,hwupload,hflip_opencl,vflip_opencl,hwdownload,format=yuv420p",
"hwargs": ["-init_hw_device", "opencl"],
"vcodec": VIDEO_ENCODER
},
]
def flip_file(inp: Path):
outp = out_name(inp)
if outp.exists():
print(f"SKIP (exists): {outp.name}")
return True
# Get original bitrate to match it
original_bitrate = get_video_bitrate(inp)
methods = get_all_methods()
for method in methods:
print(f"Trying: {method['name']}")
# Build encoding args - use original bitrate if available
if original_bitrate and "nvenc" in method["vcodec"]:
encoding_args = ["-b:v", original_bitrate, "-maxrate", original_bitrate, "-bufsize", str(int(original_bitrate) * 2)]
else:
encoding_args = NVENC_ARGS
cmd = [
"ffmpeg",
"-y",
"-hide_banner",
"-loglevel", "error",
*method["hwargs"],
"-i", str(inp),
"-map", "0",
"-vf", method["vf"],
"-c:v", method["vcodec"],
*encoding_args,
"-c:a", "copy",
"-c:s", "copy",
"-movflags", "+faststart",
str(outp)
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print(f"✓ Success with {method['name']}")
return True
else:
print(f"✗ {method['name']} failed")
if outp.exists():
outp.unlink()
print(f"❌ All methods failed for {inp.name}")
return False
def main():
print(f"Auto-trying all methods | Encoder: {VIDEO_ENCODER}")
vids = [p for p in Path(".").iterdir() if p.is_file() and p.suffix.lower() in EXTS]
if not vids:
print("No videos found in current folder.")
return
print(f"Found {len(vids)} video(s) to process\n")
failures = 0
for i, p in enumerate(vids, 1):
print(f"[{i}/{len(vids)}] {p.name}")
if not flip_file(p):
failures += 1
print()
print("=" * 50)
print(f"Processed: {len(vids)} | Success: {len(vids) - failures} | Failed: {failures}")
if failures:
sys.exit(f"{failures} file(s) failed.")
print("✓ All done!")
if __name__ == "__main__":
main()
@Ryan-Haines
Copy link
Copy Markdown
Author

Took some videos with a gopro and only realized several days later that they were all upside down! This script flips them with a minimal quality loss.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment