-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvideo-maker.py
201 lines (164 loc) · 8.41 KB
/
video-maker.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#import statements
import json, os, string, argparse, sys, praw, shutil, gtts, moviepy.editor
#create an argument parser to parse the arguments
parser = argparse.ArgumentParser(description = 'the main program that turns a reddit URL into a video')
parser.add_argument('--post-url', dest = 'posturl', required = True, help = 'the URL for the reddit post')
parser.add_argument('--comment-limit', dest = 'commentlimit', required = True, help = 'the max amount of comments to be used for the video')
parser.add_argument('--output-path', '-o', dest = 'outputpath', required = True, help = 'the path that the program will save the final video')
#parse the arguments
arguments = parser.parse_args()
#create a reddit bot user
redditBotUser = praw.Reddit('python-reddit-comment-gatherer', user_agent = 'python-reddit-comment-gatherer')
#create a function that reads json files
def readJSONFile(path) -> dict:
return json.loads(str(open(path).read()))
#create variables that hold the acceptable characters for posts and ones to replace them with
acceptableCharacters = [*string.ascii_lowercase, *string.ascii_uppercase, *string.digits, *string.whitespace, *str(string.punctuation.replace('"', ''))]
unacceptableCharacterReplaceWithCharatacter = '?'
#create a function to turn a string into a valid string with no unacceptable characters
def makeAcceptable(string) -> str:
global acceptableCharacters, unacceptableCharacterReplaceWithCharatacter
newString = ''
string = str(string)
for char in string:
if (char.lower() not in acceptableCharacters): #if the character isnt recognized as a legal character then replace it with a question mark
char = '?'
newString += str(char)
return newString.replace('\n', ' ') #replace new line characters with spaces
#store the max number of images that can be saved
maxNumberOfImages = 9999999
#get the data from the submission url
submission = redditBotUser.submission(url = str(arguments.posturl))
postData = {
'title':submission.title,
'author':submission.author,
'score':submission.score,
'comments':len(submission.comments)
}
#get the json data from the json files
JSONData = {
'dimensions':readJSONFile('./utils/image-dimensions.json'),
'fonts':readJSONFile('./utils/fonts.json'),
'colors':readJSONFile('./utils/color-scheme.json'),
'images':readJSONFile('./utils/images.json'),
'ffmpeg':readJSONFile('./utils/ffmpeg-config.json')
}
#create a function that turns the lists of commands into actual command line commands
def parseCommandList(commandList) -> str:
endString = ''
for command in commandList:
endString += str(str(command[0]) + ' "' + str(command[1]) + '" ')
return endString
#create a function that fills in the rest of an integer with spaces
def makeIntegerFileCountFriendly(integer) -> str:
global maxNumberOfImages
integer = str(integer)
tmpMaxNumberOfImages = str(maxNumberOfImages)
return str(('0' * int(len(tmpMaxNumberOfImages) - len(integer)))) + str(integer)
#make an integer to store the file count
fileCount = 0
#make the command for making the title of the video
titleCommand = [
[str(sys.executable), './title-image-maker.py'],
['--title-text', makeAcceptable(postData['title'])],
['--comments-total', postData['comments']],
['--upvotes-total', postData['score']],
['--posted-by', makeAcceptable(postData['author'])],
['--output-path', './tmp/coverimage/' + makeIntegerFileCountFriendly(fileCount) + '.png'],
['--output-image-width', JSONData['dimensions']['width']],
['--output-image-height', JSONData['dimensions']['height']],
['--background-color', JSONData['colors']['background-color']],
['--font-path', JSONData['fonts']['normal-font-path-from-root']],
['--upvote-arrow-icon-path', JSONData['images']['upvote-arrow']],
['--title-text-color', JSONData['colors']['post-title-text-color']],
['--comment-button-path', JSONData['images']['comment-button']],
['--comment-count-color', JSONData['colors']['post-buttons-text-color']],
]
#make a temporary directory for the images
try:
os.mkdir('./tmp')
except:
shutil.rmtree('./tmp')
os.mkdir('./tmp')
#run the command for the title post
os.mkdir('./tmp/coverimage') #folder for the images on the first step of the program
os.system(parseCommandList(titleCommand))
#generate the tts file for the title
tts = gtts.gTTS(makeAcceptable(postData['title']), lang = 'en')
tts.save('./tmp/coverimage/{}.mp3'.format(makeIntegerFileCountFriendly(fileCount)))
#increase the file count
fileCount += 1
#make a directory for the comment images and tts files
os.mkdir('./tmp/images')
#get the top few post comments
commentsLimit = int(arguments.commentlimit)
commentStep = 0
for comment in submission.comments:
commentStep += 1
#make the command for generating the image
imageCommand = [
[str(sys.executable), './comment-image-maker.py'],
['--comment-text', makeAcceptable(comment.body)],
['--upvotes-total', makeAcceptable(comment.score)],
['--posted-by', makeAcceptable(comment.author)],
['--output-path', './tmp/images/' + makeIntegerFileCountFriendly(fileCount) + '.png'],
['--output-image-width', JSONData['dimensions']['width']],
['--output-image-height', JSONData['dimensions']['height']],
['--background-color', JSONData['colors']['background-color']],
['--font-path', JSONData['fonts']['normal-font-path-from-root']],
['--username-text-color', JSONData['colors']['comment-username-color']],
['--comment-body-text-color', JSONData['colors']['comment-body-text-color']],
['--comment-upvotes-text-color', JSONData['colors']['comment-upvotes-text-color']],
]
#run the image command
os.system(parseCommandList(imageCommand))
#generate the tts file
tts = gtts.gTTS(makeAcceptable(comment.body), lang = 'en')
tts.save('./tmp/images/{}.mp3'.format(makeIntegerFileCountFriendly(fileCount)))
#increase the file count
fileCount += 1
#check to make sure the program hasnt gone over the comment limit
if (commentStep >= commentsLimit):
break
#create a directory for the video snippets to go to
os.mkdir('./tmp/videos')
#create a command that stitches together video and audio files
FFMPEGAVStitchCommand = 'ffmpeg.exe -loop 1 -i {} -i {} -c:v libx264 -tune stillimage -c:a aac -b:a 192k -pix_fmt yuv420p -shortest {}' #image path | audio path | output path
#get the title image paths
titleImagePathsTmp = os.listdir('./tmp/coverimage')
titleImagePaths = []
for each in titleImagePathsTmp:
if (each.split('.')[-1].lower() in ['png', 'gif', 'jpg', 'jpeg']):
titleImagePaths.append(each.split('.')[0])
#stitch together the title videos
for videoIDPath in titleImagePaths:
#get the audio and video paths
audioPath = './tmp/coverimage/{}.mp3'.format(str(videoIDPath))
imagePath = './tmp/coverimage/{}.png'.format(str(videoIDPath))
#run the ffmpeg command to stitch the media together
os.system(FFMPEGAVStitchCommand.format(imagePath, audioPath, './tmp/videos/{}.mp4'.format(makeIntegerFileCountFriendly(fileCount))))
#increase the file count
fileCount += 1
#get the comment image paths
commentImagePathsTmp = os.listdir('./tmp/images')
commentImagePaths = []
for each in commentImagePathsTmp:
if (each.split('.')[-1].lower() in ['png', 'gif', 'jpg', 'jpeg']):
commentImagePaths.append(each.split('.')[0])
#stitch together the comment videos
for videoIDPath in commentImagePaths:
#get the audio and video paths
audioPath = './tmp/images/{}.mp3'.format(str(videoIDPath))
imagePath = './tmp/images/{}.png'.format(str(videoIDPath))
#run the ffmpeg command to stitch the media together
os.system(FFMPEGAVStitchCommand.format(imagePath, audioPath, './tmp/videos/{}.mp4'.format(makeIntegerFileCountFriendly(fileCount))))
#increase the file count
fileCount += 1
#stitch together the videos
videoFileList = []
for eachMP4File in os.listdir('./tmp/videos'):
videoFileList.append(moviepy.editor.VideoFileClip('./tmp/videos/' + eachMP4File))
finalVideo = moviepy.editor.concatenate_videoclips(videoFileList)
finalVideo.write_videofile(str(arguments.outputpath))
#show the user that the program sucessfully finished
print('\naskreddit video maker is done\n')