-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmake_credits.py
138 lines (121 loc) · 5.76 KB
/
make_credits.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# -*- coding: utf-8 -*-
import argparse
import os
from PIL import Image, ImageFont, ImageDraw
from pprint import pprint
import subprocess
import sys
from lib.audio_mixer import *
from lib.audio_utils import *
from lib.image_utils import *
from lib.io_utils import *
from lib.math_utils import *
from lib.processing_utils import *
from lib.text_utils import *
from lib.video_utils import *
# input
parser = argparse.ArgumentParser()
# add text properties
addTextArguments(parser)
parser.add_argument('-in', dest="INPUT_FILE", default="titles/credits.md", help="Input markdown file")
parser.add_argument('-sdir', dest="SAMPLE_DATA_DIR", default="tmp/", help="Input markdown file")
parser.add_argument('-mdir', dest="METADATA_DIR", default="tmp/", help="Input markdown file")
parser.add_argument('-width', dest="WIDTH", default=1920, type=int, help="Output video width")
parser.add_argument('-height', dest="HEIGHT", default=1080, type=int, help="Output video height")
parser.add_argument('-fps', dest="FPS", default=24, type=int, help="Output video frames per second")
parser.add_argument('-pad0', dest="PAD_START", default=1000, type=int, help="Padding at start in ms")
parser.add_argument('-pad1', dest="PAD_END", default=2000, type=int, help="Padding at end in ms")
parser.add_argument('-outframe', dest="OUTPUT_FRAME", default="", help="Output frames pattern")
parser.add_argument('-out', dest="OUTPUT_FILE", default="output/credits.mp4", help="Output media file")
parser.add_argument('-debug', dest="DEBUG", action="store_true", help="Debug mode?")
parser.add_argument('-ds', dest="DEBUG_SECONDS", default=120, type=int, help="Debug time in seconds")
parser.add_argument('-overwrite', dest="OVERWRITE", action="store_true", help="Overwrite existing frames?")
parser.add_argument('-speed', dest="SCROLL_SPEED", default=3, type=int, help="How much to scroll per frame in px? assumes 30fps; assumes 1920x1080px")
parser.add_argument('-audio', dest="AUDIO_FILE", default="", help="Audio track to add to credits")
parser.add_argument('-astart', dest="AUDIO_START", default=0, type=int, help="Audio start in ms")
parser.add_argument('-afadein', dest="AUDIO_FADE_IN", default=3000, type=int, help="Fade in audio in ms")
parser.add_argument('-afadeout', dest="AUDIO_FADE_OUT", default=5000, type=int, help="Fade out audio in ms")
parser.add_argument('-apad0', dest="AUDIO_PAD_IN", default=500, type=int, help="Pad in audio in ms")
parser.add_argument('-apad1', dest="AUDIO_PAD_OUT", default=500, type=int, help="Pad out audio in ms")
a = parser.parse_args()
aa = vars(a)
aa["REAL_WIDTH"] = a.WIDTH
aa["REAL_HEIGHT"] = a.HEIGHT
aa["WIDTH"] = roundInt(a.WIDTH * a.RESIZE_RESOLUTION)
aa["HEIGHT"] = roundInt(a.HEIGHT * a.RESIZE_RESOLUTION)
aa["SCROLL_SPEED"] = a.SCROLL_SPEED * (a.WIDTH / 1920.0)
aa["MAX_TEXT_WIDTH"] = roundInt(a.MAX_TEXT_WIDTH * a.WIDTH) if 0 < a.MAX_TEXT_WIDTH <= 1.0 else a.WIDTH
if a.SCROLL_SPEED % 1.0 > 0.0:
print("Warning: non-integer scroll speeds (%s) result in jitter effect" % a.SCROLL_SPEED)
if len(a.OUTPUT_FRAME) < 1:
aa["OUTPUT_FRAME"] = "tmp/%s_frames/frame.%%s.png" % getBasename(a.OUTPUT_FILE)
# parse properties
tprops = getTextProperties(a)
TEXTBLOCK_X_OFFSET = roundInt(a.WIDTH * a.TEXTBLOCK_X_OFFSET)
# Read text
lines = parseMdFile(a.INPUT_FILE, a)
print("Found %s lines" % len(lines))
lines = addTextMeasurements(lines, tprops, maxWidth=a.MAX_TEXT_WIDTH)
tw, th = getBBoxFromLines(lines)
print("Size: %s x %s" % (tw, th))
totalFrames = roundInt((th+a.HEIGHT) / a.SCROLL_SPEED) + msToFrame(a.PAD_START + a.PAD_END, a.FPS)
durationMs = frameToMs(totalFrames, a.FPS)
print("Total time: %s" % formatSeconds(durationMs/1000.0))
# make dirs
makeDirectories([a.OUTPUT_FRAME, a.OUTPUT_FILE])
# remove existing files
if a.OVERWRITE:
removeFiles(a.OUTPUT_FRAME % "*")
# get frame sequence
print("Making video frame sequence...")
startMs = a.PAD_START
endMs = durationMs - a.PAD_END
debugFrame = msToFrame(a.DEBUG_SECONDS*1000, a.FPS)
y = a.HEIGHT
for f in range(totalFrames):
frame = f + 1
ms = frameToMs(frame, a.FPS)
if a.DEBUG and frame != debugFrame:
if ms > startMs:
y -= a.SCROLL_SPEED
continue
filename = a.OUTPUT_FRAME % zeroPad(frame, totalFrames)
if ms <= startMs or ms > endMs:
saveBlankFrame(filename, a.REAL_WIDTH, a.REAL_HEIGHT, bgColor=a.BG_COLOR, overwrite=a.OVERWRITE)
else:
linesToImage(lines, filename, a.WIDTH, a.HEIGHT,
color=a.TEXT_COLOR,
bgColor=a.BG_COLOR,
y=y,
tblockXOffset=TEXTBLOCK_X_OFFSET,
resizeResolution=a.RESIZE_RESOLUTION,
overwrite=a.OVERWRITE)
y -= a.SCROLL_SPEED
printProgress(frame, totalFrames)
audioFile = a.OUTPUT_FILE.replace(".mp4", ".mp3")
audioDur = durationMs
if len(a.AUDIO_FILE) > 0:
if not os.path.isfile(audioFile) or a.OVERWRITE:
srcDur = floorInt(getDurationFromFile(a.AUDIO_FILE, accurate=True) * 1000)
print("Source audio dur: %s" % formatSeconds(srcDur/1000.0))
clipDur = audioDur - a.AUDIO_PAD_IN - a.AUDIO_PAD_OUT
if a.AUDIO_START + clipDur > srcDur:
print("Source audio not long enough (%s > %s)" % (a.AUDIO_START + clipDur, srcDur))
instructions = [{
"ms": a.AUDIO_PAD_IN,
"filename": a.AUDIO_FILE,
"start": a.AUDIO_START,
"dur": clipDur,
"volume": 1.0,
"fadeIn": a.AUDIO_FADE_IN,
"fadeOut": a.AUDIO_FADE_OUT,
"matchDb": -16
}]
if a.DEBUG:
sys.exit()
mixAudio(instructions, audioDur, audioFile)
else:
makeBlankAudio(audioDur, audioFile)
if a.DEBUG:
sys.exit()
compileFrames(a.OUTPUT_FRAME, a.FPS, a.OUTPUT_FILE, getZeroPadding(totalFrames), audioFile=audioFile)