# VMAF Optimiser - Agent Guidelines ## Quick Reference **Purpose:** Video library optimization pipeline using VMAF quality targets with AV1 encoding. **Core Files:** - `optimize_library.py` - Main Python script (342 lines) - `run_optimisation.sh` - Linux/macOS wrapper - `run_optimisation.ps1` - Windows wrapper --- ## Build/Lint/Test Commands ### Development Setup ```bash # Install dependencies (if not already) cargo install ab-av1 # v0.10.3+ brew install ffmpeg # macOS # OR: apt install ffmpeg # Linux/WSL # OR: winget install ffmpeg # Windows ``` ### Linting ```bash # Ruff is the linter (indicated by .ruff_cache/) ruff check optimize_library.py # Format with ruff ruff format optimize_library.py # Check specific issues ruff check optimize_library.py --select E,F,W ``` ### Running the Application ```bash # Linux/macOS ./run_optimisation.sh --directory /media --vmaf 95 --workers 1 # Windows .\run_optimisation.ps1 -directory "D:\Movies" -vmaf 95 -workers 1 # Direct Python execution python3 optimize_library.py /media --vmaf 95 --preset 6 --workers 1 ``` ### Testing **No formal test suite exists currently.** Test manually by: ```bash # Test with single video file python3 optimize_library.py /media/sample.mkv --vmaf 95 --workers 1 # Dry run (validate logic without encoding) python3 optimize_library.py /media --vmaf 95 --thorough # Check dependencies python3 optimize_library.py 2>&1 | grep -E "(ffmpeg|ab-av1)" ``` --- ## Code Style Guidelines ### Python Style (PEP 8 Compliant) **Imports:** ```python # Standard library first, grouped logically import os import sys import subprocess import json import shutil import platform from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path ``` **Naming Conventions:** ```python # Constants: UPPER_SNAKE_CASE DEFAULT_VMAF = 95.0 DEFAULT_PRESET = 6 EXTENSIONS = {".mkv", ".mp4", ".mov", ".avi", ".ts"} # Functions: snake_case def get_video_info(filepath): def build_ab_av1_command(input_path, output_path, args): # Variables: snake_case input_path = Path(filepath) output_path = input_path.with_stem(input_path.stem + "_av1") # Module-level cache: _PREFIX (private) _AB_AV1_HELP_CACHE = {} ``` **Formatting:** - 4-space indentation - Line length: ~88-100 characters (ruff default: 88) - No trailing whitespace - One blank line between functions - Two blank lines before class definitions (if any) **Function Structure:** ```python def function_name(param1, param2, optional_param=None): """Brief description if needed.""" try: # Implementation return result except Exception as e: print(f"Error: {e}") return None # or handle gracefully ``` **Subprocess Calls:** ```python # Use subprocess.run for all external commands cmd = ["ffmpeg", "-i", input_file, output_file] result = subprocess.run(cmd, capture_output=True, text=True, check=True) # Check return codes explicitly if result.returncode != 0: print(f"Command failed: {result.stderr}") ``` ### Error Handling ```python # Always wrap external tool calls in try-except try: info = get_video_info(filepath) if not info: return # Early return on None except subprocess.CalledProcessError as e: print(f"FFmpeg failed: {e}") return # Use specific exception types when possible except FileNotFoundError: print("File not found") except json.JSONDecodeError: print("Invalid JSON") ``` ### Platform Detection ```python # Use platform module for OS detection def is_wsl(): if os.environ.get("WSL_DISTRO_NAME"): return True try: with open("/proc/sys/kernel/osrelease", "r") as f: return "microsoft" in f.read().lower() except FileNotFoundError: return False def platform_label(): system = platform.system() if system == "Linux" and is_wsl(): return "Linux (WSL)" return system ``` ### Argument Parsing ```python def main(): parser = argparse.ArgumentParser(description="Description") parser.add_argument("directory", help="Root directory") parser.add_argument("--vmaf", type=float, default=95.0, help="Target VMAF") args = parser.parse_args() ``` --- ## Shell Script Guidelines (run_optimisation.sh) **Shebang & Error Handling:** ```bash #!/bin/bash set -e # Exit on error ``` **Color Output:** ```bash COLOR_RED='\033[0;31m' COLOR_GREEN='\033[0;32m' COLOR_CYAN='\033[0;36m' COLOR_RESET='\033[0m' log_info() { echo -e "${COLOR_CYAN}$*${COLOR_RESET}" } log_error() { echo -e "${COLOR_RED}ERROR: $*${COLOR_RESET}" >&2 } ``` **Argument Parsing:** ```bash while [[ $# -gt 0 ]]; do case "$1" in --vmaf) VMAF="$2" shift 2 ;; *) DIRECTORY="$1" shift ;; esac done ``` --- ## PowerShell Guidelines (run_optimisation.ps1) **Parameter Declaration:** ```powershell param( [Parameter(Mandatory=$false)] [string]$Directory = ".", [float]$Vmaf = 95.0, [switch]$Thorough ) ``` **Error Handling:** ```powershell $ErrorActionPreference = "Stop" function Write-ColorOutput { param([string]$Message, [string]$Color = "White") Write-Host $Message -ForegroundColor $Color } ``` **Process Management:** ```powershell $process = Start-Process -FilePath $pythonCmd.Path -ArgumentList $arguments ` -NoNewWindow -PassThru $process.WaitForExit() $exitCode = $process.ExitCode ``` --- ## Key Constraints & Best Practices ### When Modifying `optimize_library.py` 1. **Maintain platform compatibility:** Always test on Linux, Windows, and macOS 2. **Preserve subprocess patterns:** Use `subprocess.run` with `check=True` 3. **Handle missing dependencies:** Check `shutil.which()` before running tools 4. **Thread safety:** The script uses `ThreadPoolExecutor` - avoid global state 5. **Path handling:** Always use `Path` objects from `pathlib` ### When Modifying Wrapper Scripts 1. **Keep interfaces consistent:** Both scripts should accept the same parameters 2. **Preserve color output:** Users expect colored status messages 3. **Validate Python path:** Handle `python3` vs `python` vs `py` 4. **Check script existence:** Verify `optimize_library.py` exists before running ### File Organization - Keep functions under 50 lines - Use descriptive names (no abbreviations like `proc_file`, use `process_file`) - Cache external command help text (see `_AB_AV1_HELP_CACHE`) - Use constants for magic numbers and strings ### Hardware Acceleration - Auto-detect via `normalize_hwaccel()` function - Respect `--hwaccel` flag - Check ab-av1 support with `ab_av1_supports()` before using flags - Default: `auto` (d3d11va on Windows, videotoolbox on macOS, vaapi on Linux) --- ## Common Patterns ### Checking Tool Availability ```python def check_dependencies(): missing = [] for tool in ["ffmpeg", "ffprobe", "ab-av1"]: if not shutil.which(tool): missing.append(tool) if missing: print(f"Error: Missing tools: {', '.join(missing)}") sys.exit(1) ``` ### Building Commands Conditionally ```python cmd = ["ab-av1", "auto-encode", "-i", input_path] if args.encoder: if ab_av1_supports("auto-encode", "--encoder"): cmd.extend(["--encoder", args.encoder]) else: print("Warning: Encoder not supported") ``` ### File Path Operations ```python # Use pathlib for cross-platform paths input_path = Path(filepath) output_path = input_path.with_stem(input_path.stem + "_av1") # Safe existence check if output_path.exists(): print(f"Skipping: {input_path.name}") return ``` --- ## Version Control ```bash # Check for changes git status # Format before committing ruff format optimize_library.py ruff check optimize_library.py # Commit with conventional commits git commit -m "feat: add hardware acceleration support" git commit -m "fix: handle missing ffprobe gracefully" git commit -m "docs: update setup instructions" ``` --- ## Important Notes 1. **No type hints:** Current codebase doesn't use Python typing 2. **No formal tests:** Test manually with sample videos 3. **No package.json:** This is a standalone script, not a Python package 4. **Lock files:** `.lock/` directory created at runtime for multi-machine coordination 5. **Logs:** JSONL format in `logs/` directory for structured data --- **Last Updated:** December 31, 2025