112 lines
4.3 KiB
Python
112 lines
4.3 KiB
Python
import subprocess
|
|
import os
|
|
from typing import List, Tuple
|
|
|
|
def split_video(input_file: str, output_dir: str, timestamps: List[Tuple[float, float]], frame_perfect: bool = False) -> None:
|
|
"""
|
|
Splits a video file into multiple segments based on specified time ranges.
|
|
|
|
Args:
|
|
input_file (str): The path to the video file to be split.
|
|
output_dir (str): The directory where the split segments will be saved.
|
|
timestamps (List[Tuple[float, float]]): A list of tuples representing (start_time, end_time).
|
|
frame_perfect (bool): If True, skips fast-copy and forces high-quality re-encoding
|
|
to ensure cuts happen exactly on the requested frame.
|
|
"""
|
|
if not os.path.exists(input_file):
|
|
print(f"Error: Input file not found at {input_file}")
|
|
return
|
|
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
print(f"Starting video split process for {input_file}...")
|
|
|
|
for i, (start, end) in enumerate(timestamps):
|
|
segment_name = f"segment_{i+1:02d}_{start:.2f}s_to_{end:.2f}s.mp4"
|
|
output_file = os.path.join(output_dir, segment_name)
|
|
|
|
print(f"\n-> Processing segment {i+1}/{len(timestamps)}: {segment_name}")
|
|
|
|
# Method 1: Fast Copy (Lossless, no re-encoding)
|
|
# Moving -ss BEFORE -i fixes the frozen/corrupted initial frames.
|
|
fast_command = [
|
|
"ffmpeg", "-y",
|
|
"-ss", str(start),
|
|
"-i", input_file,
|
|
"-to", str(end),
|
|
"-c", "copy",
|
|
output_file
|
|
]
|
|
|
|
# Method 2: High-Quality Re-encode (Frame-perfect fallback)
|
|
# Used if fast copy fails or if frame-accuracy is explicitly requested.
|
|
reencode_command = [
|
|
"ffmpeg", "-y",
|
|
"-ss", str(start),
|
|
"-i", input_file,
|
|
"-to", str(end),
|
|
"-c:v", "libx264",
|
|
"-crf", "18", # High visual quality
|
|
"-preset", "slow", # Better compression efficiency
|
|
"-c:a", "aac",
|
|
"-b:a", "256k", # High-fidelity audio
|
|
output_file
|
|
]
|
|
|
|
try:
|
|
if frame_perfect:
|
|
print("Running in frame-perfect mode (re-encoding)...")
|
|
subprocess.run(reencode_command, check=True, capture_output=True, text=True)
|
|
else:
|
|
try:
|
|
print("Attempting lightning-fast stream copy...")
|
|
subprocess.run(fast_command, check=True, capture_output=True, text=True)
|
|
except subprocess.CalledProcessError:
|
|
print("⚠️ Fast copy failed. Falling back to high-quality re-encode...")
|
|
subprocess.run(reencode_command, check=True, capture_output=True, text=True)
|
|
|
|
print(f"Successfully created segment: {output_file}")
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"❌ Error splitting video segment {i+1}:")
|
|
print(f"Command failed with return code {e.returncode}")
|
|
print(f"STDERR: {e.stderr}")
|
|
except FileNotFoundError:
|
|
print("-" * 50)
|
|
print("CRITICAL ERROR: FFmpeg executable not found.")
|
|
print("Please ensure 'ffmpeg' is installed and accessible in your system's PATH.")
|
|
print("-" * 50)
|
|
return
|
|
|
|
def main():
|
|
"""
|
|
Example usage:
|
|
Assumes 'input.mp4' exists and a 'split_videos' directory is ready.
|
|
"""
|
|
# --- CONFIGURATION ---
|
|
INPUT_VIDEO_PATH = "path/to/your/input_video.mp4" # <<< CHANGE THIS
|
|
OUTPUT_SEGMENTS_DIR = "output_splits" # <<< CHANGE THIS
|
|
|
|
# Define time segments: [(start_time, end_time), ...]
|
|
# Times should be floats representing seconds.
|
|
# Example 1: 0s to 10s, then 10s to 20s
|
|
TIMESTAMPS: List[Tuple[float, float]] = [
|
|
(0.0, 10.0),
|
|
(15.0, 25.0),
|
|
(30.0, 35.0)
|
|
]
|
|
# ---------------------
|
|
|
|
if not os.path.exists(INPUT_VIDEO_PATH):
|
|
print("!!! IMPORTANT !!!")
|
|
print(f"Please update the 'INPUT_VIDEO_PATH' variable in the main() function with the correct path to your video.")
|
|
print("!!! Script execution aborted. !!!")
|
|
return
|
|
|
|
split_video(
|
|
input_file=INPUT_VIDEO_PATH,
|
|
output_dir=OUTPUT_SEGMENTS_DIR,
|
|
timestamps=TIMESTAMPS
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
main() |