Skip to content

Instantly share code, notes, and snippets.

@Cyberes
Last active December 15, 2022 10:08
Show Gist options
  • Save Cyberes/1bf93966b0804ffdb89b26a96c58111f to your computer and use it in GitHub Desktop.
Save Cyberes/1bf93966b0804ffdb89b26a96c58111f to your computer and use it in GitHub Desktop.
Random Video Montage
# 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
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