Last active
December 15, 2022 10:08
-
-
Save Cyberes/1bf93966b0804ffdb89b26a96c58111f to your computer and use it in GitHub Desktop.
Random Video Montage
This file contains 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
# I wrote this to combine a bunch of audio files for the random video script above | |
# I didn't use this because ffmpeg messed up some of my .ogg files. | |
rm audio.txt | |
for f in $PWD/audio/*.ogg; do | |
printf '%s\n' "file '$f'" >> audio.txt | |
done | |
shuf audio.txt > temp | |
mv temp audio.txt | |
ffmpeg -f concat -safe 0 -i audio.txt -vn -ar 44100 -b:a 112k -acodec libmp3lame long.mp3 |
This file contains 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 argparse | |
import datetime | |
import logging | |
import math | |
import os | |
import random | |
import subprocess | |
import sys | |
import uuid | |
from pathlib import Path | |
import cv2 | |
from tqdm import tqdm | |
parser = argparse.ArgumentParser() | |
parser.add_argument('input') | |
parser.add_argument('output') | |
parser.add_argument('--duration', default=2, help='individual duration of the clips') | |
args = parser.parse_args() | |
def pad_zeros(string, max_length_str) -> str: | |
max_len = len(str(max_length_str)) | |
if max_len == 1: | |
max_len = 2 | |
return "{:0>{digits}}".format(string, digits=max_len) | |
def video_duration(filename): | |
video = cv2.VideoCapture(filename) | |
frames = video.get(cv2.CAP_PROP_FRAME_COUNT) | |
fps = video.get(cv2.CAP_PROP_FPS) | |
seconds = round(frames / fps) | |
video_time = datetime.timedelta(seconds=seconds) | |
return video_time | |
def convert_time(seconds): | |
seconds = seconds % (24 * 3600) | |
hour = seconds // 3600 | |
seconds %= 3600 | |
minutes = seconds // 60 | |
seconds %= 60 | |
return hour, minutes, seconds | |
output_dir = Path(args.output).expanduser().absolute() | |
output_dir.mkdir(parents=True, exist_ok=True) | |
files = [] | |
for path in Path(args.input).expanduser().absolute().rglob('*.webm'): | |
files.append(path) | |
bar = tqdm(files) | |
for file in files: | |
bar.set_description(f'{file.name}') | |
try: | |
duration = video_duration(str(file)) | |
except Exception as e: | |
bar.write(f'{file.name} failed with {e}') | |
bar.update(1) | |
continue | |
if duration.seconds < args.duration: | |
bar.write(f'{file.name} with duration of {duration.seconds} is less than the cut length of {args.duration}') | |
bar.update(1) | |
continue | |
# Choose a random time to start | |
start_s = random.randint(0, duration.seconds) | |
# Make sure out segmet isn't longer than the duration. | |
while start_s + args.duration > duration.seconds: | |
start_s -= 1 | |
# Find the end time | |
end_s = convert_time(start_s + args.duration)[2] | |
end_m = convert_time(start_s + args.duration)[1] | |
# Pad zeros for ffmpeg | |
start_m = pad_zeros(convert_time(start_s)[1], 2) | |
start_s = pad_zeros(convert_time(start_s)[2], 2) | |
end_m = pad_zeros(end_m, 2) | |
end_s = pad_zeros(end_s, 2) | |
bar.set_description(f'{file.name} -> {start_m}:{start_s} to {end_m}:{end_s}') | |
# Random filename | |
output_file = Path(output_dir, f'{uuid.uuid4()}.webm') | |
# Do the ffmpeg thing | |
cmd = f'ffmpeg -y -i "{str(file)}" -vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:-1:-1:color=black" -c:v libvpx-vp9 -crf 30 -b:v 0 -b:a 128k -c:a libopus -ss 00:{start_m}:{start_s} -to 00:{end_m}:{end_s} "{output_file}"' | |
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) | |
if result.returncode != 0: | |
message = f"""'Error running command: {cmd} | |
Error code: {result.returncode} | |
stdout: {result.stdout.decode(encoding="utf8", errors="ignore") if len(result.stdout) > 0 else '<empty>'} | |
stderr: {result.stderr.decode(encoding="utf8", errors="ignore") if len(result.stderr) > 0 else '<empty>'} | |
""" | |
bar.write(message + '\n') | |
bar.update(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment