Skip to content

Instantly share code, notes, and snippets.

@rjmoggach
Last active December 27, 2020 02:52
Show Gist options
  • Save rjmoggach/0bc5d2f7a664a9b497dfd06f8bd234ba to your computer and use it in GitHub Desktop.
Save rjmoggach/0bc5d2f7a664a9b497dfd06f8bd234ba to your computer and use it in GitHub Desktop.
MP4 Maker
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
}
"""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