Last active
December 27, 2020 02:52
-
-
Save rjmoggach/0bc5d2f7a664a9b497dfd06f8bd234ba to your computer and use it in GitHub Desktop.
MP4 Maker
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
Write-Host "Watch this!" | |
$src_folders = @( | |
"folder_one-yo", | |
"folder_two-yo", | |
"folder_three-yo", | |
"HRMND_wavedata", | |
"HRMND_windtunnel", | |
"HRMND_zonalambience" | |
) | |
foreach ($src in $src_folders) { | |
New-Item -ItemType Directory -Force -Path ..\public\design\$src | Out-Null | |
python .\mp4maker.py -s $src -d ..\public\design\$src -f render -r -e -j --debug | |
} |
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
"""MP4 maker | |
v0.0.1 | |
Written by Robert Moggach, 2020 | |
This script looks for image sequences in specified source directory | |
and makes a basic MP4 in the target directory. | |
dependencies: | |
- Python 2.6+ or 3.6+ | |
- fileseq https://github.com/justinfx/fileseq | |
- ffmpeg https://github.com/kkroening/ffmpeg-python | |
example: | |
$ python mp4maker.py --help | |
""" | |
import os | |
import sys | |
import string | |
import shutil | |
import tempfile | |
import platform | |
import contextlib | |
import subprocess | |
import fileseq | |
import ffmpeg | |
import textwrap | |
CWD = os.getcwd() | |
DEBUG = True | |
def get_padding_string(fs=None): | |
if fs: | |
strf = '%0{}d'.format(fs.getPaddingNum(fs.padding())) | |
return strf | |
else: | |
return None | |
def find_needles(needles=[], haystacks=[], debug=False): | |
if not needles: | |
if haystacks: | |
return haystacks | |
else: | |
return False | |
if not haystacks: | |
return False | |
results = [] | |
needle = needles.pop(0) | |
for haystack in haystacks: | |
for root, subfolders, files in os.walk(haystack): | |
for folder in subfolders: | |
if folder == needle: | |
found_needle = os.path.join(root, folder) | |
results.append(found_needle) | |
haystacks = find_needles(needles=needles, haystacks=results) | |
return haystacks | |
def get_sequences(paths=[], sequences=[], recurse=False, debug=False): | |
if paths: | |
results = [] | |
path = paths.pop(0) | |
# print(str.rjust("PATH: ", 8), path) | |
results = fileseq.findSequencesOnDisk(path) | |
if len(results) > 0: | |
if debug: | |
print( | |
str.rjust(str(len(results)), 4), | |
"possible sequences in: %s" % path, | |
) | |
for result in results: | |
try: | |
if len(result.frameSet()) > 1: | |
# print( | |
# str.rjust("SEQ:", 12), | |
# result.format(template="{dirname}{basename}*{extension}"), | |
# ) | |
sequences.append(result) | |
except: | |
pass | |
if recurse: | |
subdirs = [f.path for f in os.scandir(path) if f.is_dir()] | |
paths.extend(subdirs) | |
# subseqs = get_sequences(paths=subdirs, recurse=True) | |
get_sequences(paths=paths, sequences=sequences, recurse=recurse) | |
return sequences | |
def encode_mp4(sequence=None, dest=None, encode=False, overwrite=False, jpeg=False,rate=24): | |
if dest == CWD: | |
dest = os.path.join(dest, "_mp4maker_output") | |
if not os.path.exists(dest): | |
os.makedirs(dest) | |
if sequence: | |
# this doesn't work by default on windows | |
# input = ffmpeg.input(path, pattern_type="glob", framerate=24) | |
padding_str=get_padding_string(sequence) | |
input_path = sequence.format(template="{dirname}{basename}%s{extension}" % padding_str) | |
basename = sequence.format(template="{basename}") | |
start_fr = sequence.format(template="{start}") | |
end_fr = sequence.format(template="{end}") | |
output_filename = "%smp4" % basename | |
output_path = os.path.join(dest, output_filename) | |
output_jpeg = "%sjpg" % basename | |
output_jpeg_path = os.path.join(dest, output_jpeg) | |
if encode: | |
clobber = os.path.isfile(output_path) and overwrite | |
exists = os.path.isfile(output_path) | |
print("CLOBBER", clobber) | |
print("EXISTS", exists) | |
if (os.path.isfile(output_path) and overwrite) or ( | |
not os.path.isfile(output_path) | |
): | |
print("OKAY") | |
input = ffmpeg.input(input_path, start_number=start_fr, framerate=rate) | |
output = input.output( | |
output_path, | |
vcodec="libx264", | |
vf="scale=1920:-1", | |
crf=23, | |
preset="medium", | |
movflags="faststart", | |
) | |
output.run() | |
if jpeg: | |
inputFr = ffmpeg.input(output_path, ss=1) | |
outputFr = inputFr.output(output_jpeg_path, vframes=1) | |
outputFr.run() | |
print(str.rjust("Encoded:", 12), output_filename) | |
else: | |
print(str.rjust("Skipped:", 12), output_filename) | |
else: | |
print(str.rjust("Sequence:", 12), input_path) | |
print(str.rjust("Base Name:", 12), basename) | |
print( | |
str.rjust("Start:", 12), | |
start_fr, | |
"End:", | |
end_fr, | |
) | |
return True | |
return False | |
def main(): | |
import argparse | |
# get options | |
parser = argparse.ArgumentParser(usage=__doc__) | |
# if source directory specified set source | |
kw = ["src", "dest", "encode", "filter", "overwrite", "recurse", "debug", "jpeg"] | |
parser.add_argument( | |
"-s", | |
"--src", | |
dest="src", | |
help="source directory to search through to find sequences", | |
default=CWD, | |
) | |
# if target directory specified set target | |
parser.add_argument( | |
"-d", | |
"--dest", | |
dest="dest", | |
help="destination directory to encode our sequence mp4 movies to", | |
default=CWD, | |
) | |
parser.add_argument( | |
"-t", | |
"--rate", | |
dest="rate", | |
help="frame rate", | |
default=24, | |
) | |
# if listing only specified set arg | |
parser.add_argument( | |
"-e", | |
"--encode", | |
dest="encode", | |
action="store_true", | |
help="encode videos from found sequences (default is listing only)", | |
default=False, | |
) | |
# filter by specified parent directory name, can be space delimited | |
parser.add_argument( | |
"-f", | |
"--filter", | |
dest="filter", | |
action="append", | |
nargs="*", | |
help="parent directory name to look for\n eg. 'shot010 render'\n ordered so will search for shot010 then render folder", | |
default=None, | |
) | |
# ignore old sequences | |
parser.add_argument( | |
"-o", | |
"--overwrite", | |
dest="overwrite", | |
action="store_true", | |
help="process new sequences only and don't overwrite", | |
default=False, | |
) | |
parser.add_argument( | |
"-g", | |
"--debug", | |
dest="debug", | |
action="store_true", | |
help="enable debug output", | |
default=False, | |
) | |
parser.add_argument( | |
"-j", | |
"--jpeg", | |
dest="jpeg", | |
action="store_true", | |
help="enable jpeg thumb", | |
default=False, | |
) | |
parser.add_argument( | |
"-v", | |
"--verbose", | |
dest="verbose", | |
action="store_true", | |
help="enable verbose output", | |
default=False, | |
) | |
parser.add_argument( | |
"-r", | |
"--recurse", | |
dest="recurse", | |
action="store_true", | |
help="recurse into source directory subdirectories", | |
default=False, | |
) | |
kwargs, args = parser.parse_known_args() | |
# if recursive specified recurse | |
# get list of directories | |
# get sequences from target directories | |
# get list of render folders | |
# get sequences in render folders | |
# create mp4 for each image sequence | |
paths = [] | |
DEBUG = kwargs.debug | |
if not os.path.isdir(kwargs.src): | |
sys.exit("Source is not a directory: %s" % kwargs.src) | |
if not os.path.isdir(kwargs.dest): | |
sys.exit("Destination is not a directory: %s" % kwargs.dest) | |
if kwargs.filter: | |
kwargs.filter = [item for sublist in kwargs.filter for item in sublist] | |
search_str_list = kwargs.filter | |
paths = find_needles(needles=kwargs.filter, haystacks=[kwargs.src], debug=DEBUG) | |
else: | |
paths.append(kwargs.src) | |
if DEBUG: | |
for path in paths: | |
print(" %s" % path) | |
for arg in kw: | |
print(str.rjust("%s: " % arg, 12), eval("kwargs.%s" % arg)) | |
seqs = get_sequences(paths=paths, recurse=kwargs.recurse, debug=DEBUG) | |
ERRORS = [] | |
for seq in seqs: | |
# print seq.format(template="{dirname}{basename}*{extension}") | |
try: | |
encode_mp4( | |
sequence=seq, | |
dest=kwargs.dest, | |
encode=kwargs.encode, | |
overwrite=kwargs.overwrite, | |
jpeg=kwargs.jpeg, | |
rate=rate | |
) | |
except: | |
padding_str=get_padding_string(seq) | |
seq_path = seq.format(template="{dirname}{basename}%s{extension}" % padding_str) | |
ERRORS.append(seq_path) | |
if len(ERRORS) > 0: | |
print( | |
str.rjust("ERRORS:", 12), | |
"We encountered the following problem sequences...", | |
) | |
for err in ERRORS: | |
print(textwrap.indent(err, " ")) | |
sys.exit(0) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment