Python: add script for batch triming videos
This commit is contained in:
parent
397ecaef55
commit
c479100ac0
1 changed files with 150 additions and 0 deletions
150
PythonScripts/trim-videos.py
Normal file
150
PythonScripts/trim-videos.py
Normal file
|
@ -0,0 +1,150 @@
|
|||
import os
|
||||
import subprocess
|
||||
import argparse
|
||||
|
||||
|
||||
def get_video_duration(input_file):
|
||||
"""
|
||||
Get the duration of a video file using ffprobe.
|
||||
|
||||
ffprobe is a command-line tool used to analyze multimedia streams. In this function:
|
||||
- `-v error`: Limits output to only errors.
|
||||
- `-select_streams v:0`: Selects the first video stream.
|
||||
- `-show_entries format=duration`: Outputs only the video duration.
|
||||
- `-of default=noprint_wrappers=1:nokey=1`: Formats the output to display only the duration value.
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[
|
||||
"ffprobe",
|
||||
"-v",
|
||||
"error",
|
||||
"-select_streams",
|
||||
"v:0",
|
||||
"-show_entries",
|
||||
"format=duration",
|
||||
"-of",
|
||||
"default=noprint_wrappers=1:nokey=1",
|
||||
input_file,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return float(result.stdout.strip())
|
||||
except (subprocess.CalledProcessError, ValueError) as e:
|
||||
raise RuntimeError(f"Failed to get duration for {input_file}: {e}")
|
||||
|
||||
|
||||
def process_folder(input_dir, output_dir, trim_start, trim_end, supported_formats):
|
||||
"""Process each folder in the input directory and trim video files."""
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
for folder in os.listdir(input_dir):
|
||||
folder_path = os.path.join(input_dir, folder)
|
||||
if not os.path.isdir(folder_path):
|
||||
continue
|
||||
|
||||
for file in os.listdir(folder_path):
|
||||
if file.endswith(tuple(supported_formats)):
|
||||
input_file = os.path.join(folder_path, file)
|
||||
|
||||
# Extract episode number or default to folder name
|
||||
# Expected folder naming convention: 'SeriesName #EpisodeNumber - Description'
|
||||
# If '#' is missing, the folder name will be used as the episode identifier
|
||||
if "#" in folder:
|
||||
try:
|
||||
episode = folder.split("#")[1].strip().split(" - ")[0]
|
||||
except IndexError:
|
||||
episode = folder.replace(" ", "_")
|
||||
else:
|
||||
episode = folder.replace(" ", "_")
|
||||
|
||||
output_ext = os.path.splitext(file)[1]
|
||||
output_file = os.path.join(output_dir, f"ep{episode}{output_ext}")
|
||||
|
||||
try:
|
||||
duration = get_video_duration(input_file)
|
||||
except RuntimeError as e:
|
||||
print(e)
|
||||
continue
|
||||
|
||||
if trim_start >= duration:
|
||||
print(
|
||||
f"Skipping {input_file}: trim-start ({trim_start}s) is greater than or equal to video duration ({duration}s)."
|
||||
)
|
||||
continue
|
||||
if duration - trim_end <= trim_start:
|
||||
print(
|
||||
f"Skipping {input_file}: Invalid trim range (start: {trim_start}s, end: {trim_end}s, duration: {duration}s)."
|
||||
)
|
||||
continue
|
||||
|
||||
end_time = duration - trim_end
|
||||
|
||||
# FFmpeg command to trim the video
|
||||
ffmpeg_command = [
|
||||
"ffmpeg", # Command to run FFmpeg
|
||||
"-i",
|
||||
input_file, # Input file path
|
||||
"-ss",
|
||||
f"{trim_start:.2f}", # Start trimming from this timestamp (in seconds)
|
||||
"-to",
|
||||
f"{end_time:.2f}", # End trimming at this timestamp (in seconds)
|
||||
"-c",
|
||||
"copy", # Copy codec to avoid re-encoding
|
||||
output_file, # Output file path
|
||||
]
|
||||
|
||||
try:
|
||||
subprocess.run(ffmpeg_command, check=True)
|
||||
print(f"Trimmed and saved: {output_file}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Failed to process {input_file}: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to parse arguments and run the script."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Trim video files in folders and save them with episode numbers."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--input",
|
||||
type=str,
|
||||
default=".",
|
||||
help="Input directory containing folders with video files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=str,
|
||||
default="output",
|
||||
help="Output directory for trimmed videos",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--trim-start",
|
||||
type=float,
|
||||
default=46.0,
|
||||
help="Number of seconds to trim from the start",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--trim-end",
|
||||
type=float,
|
||||
default=16.0,
|
||||
help="Number of seconds to trim from the end",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--formats",
|
||||
type=str,
|
||||
default=".mp4,.webm,.mkv",
|
||||
help="Comma-separated list of supported video formats eg, '.mp4,.webm,.mkv' ",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
supported_formats = [fmt.strip() for fmt in args.formats.split(",")]
|
||||
process_folder(
|
||||
args.input, args.output, args.trim_start, args.trim_end, supported_formats
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue