@ -27,14 +27,13 @@ import time
class ProgressFfmpeg ( threading . Thread ) :
def __init__ ( self , vid_duration_seconds , progress_update_callback ) :
threading . Thread . __init__ ( self , name = ' ProgressFfmpeg ' )
threading . Thread . __init__ ( self , name = " ProgressFfmpeg " )
self . stop_event = threading . Event ( )
self . output_file = tempfile . NamedTemporaryFile ( mode = ' w+ ' , delete = False )
self . output_file = tempfile . NamedTemporaryFile ( mode = " w+ " , delete = False )
self . vid_duration_seconds = vid_duration_seconds
self . progress_update_callback = progress_update_callback
def run ( self ) :
while not self . stop_event . is_set ( ) :
latest_progress = self . get_latest_ms_progress ( )
if latest_progress is not None :
@ -47,8 +46,8 @@ class ProgressFfmpeg(threading.Thread):
if lines :
for line in lines :
if ' out_time_ms ' in line :
out_time_ms = line . split ( ' = ' ) [ 1 ]
if " out_time_ms " in line :
out_time_ms = line . split ( " = " ) [ 1 ]
return int ( out_time_ms ) / 1000000.0
return None
@ -82,18 +81,30 @@ def name_normalize(name: str) -> str:
def prepare_background ( reddit_id : str , W : int , H : int ) - > str :
output_path = f " assets/temp/ { reddit_id } /background_noaudio.mp4 "
output = ffmpeg . input ( f " assets/temp/ { reddit_id } /background.mp4 " ) . filter ( ' crop ' , f " ih*( { W } / { H } ) " , " ih " ) . output (
output_path , an = None ,
* * { " c:v " : " h264 " , " b:v " : " 20M " , " b:a " : " 192k " , " threads " : multiprocessing . cpu_count ( ) } ) . overwrite_output ( )
output = (
ffmpeg . input ( f " assets/temp/ { reddit_id } /background.mp4 " )
. filter ( " crop " , f " ih*( { W } / { H } ) " , " ih " )
. output (
output_path ,
an = None ,
* * {
" c:v " : " h264 " ,
" b:v " : " 20M " ,
" b:a " : " 192k " ,
" threads " : multiprocessing . cpu_count ( ) ,
} ,
)
. overwrite_output ( )
)
output . run ( quiet = True )
return output_path
def make_final_video (
number_of_clips : int ,
length : int ,
reddit_obj : dict ,
background_config : Tuple [ str , str , str , Any ] ,
number_of_clips : int ,
length : int ,
reddit_obj : dict ,
background_config : Tuple [ str , str , str , Any ] ,
) :
""" Gathers audio clips, gathers all screenshots, stitches them together and saves the final video to assets/temp
Args :
@ -116,7 +127,9 @@ def make_final_video(
if settings . config [ " settings " ] [ " storymode " ] :
if settings . config [ " settings " ] [ " storymodemethod " ] == 0 :
audio_clips = [ ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/title.mp3 " ) ]
audio_clips . insert ( 1 , ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/postaudio.mp3 " ) )
audio_clips . insert (
1 , ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/postaudio.mp3 " )
)
elif settings . config [ " settings " ] [ " storymodemethod " ] == 1 :
audio_clips = [
ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/postaudio- { i } .mp3 " )
@ -124,20 +137,37 @@ def make_final_video(
range ( number_of_clips + 1 ) , " Collecting the audio files... "
)
]
audio_clips . insert ( 0 , ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/title.mp3 " ) )
audio_clips . insert (
0 , ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/title.mp3 " )
)
else :
audio_clips = [ ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/ { i } .mp3 " ) for i in range ( number_of_clips ) ]
audio_clips = [
ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/ { i } .mp3 " )
for i in range ( number_of_clips )
]
audio_clips . insert ( 0 , ffmpeg . input ( f " assets/temp/ { reddit_id } /mp3/title.mp3 " ) )
audio_clips_durations = [ float ( ffmpeg . probe ( f " assets/temp/ { reddit_id } /mp3/ { i } .mp3 " ) [ ' format ' ] [ ' duration ' ] ) for i
in
range ( number_of_clips ) ]
audio_clips_durations . insert ( 0 , float (
ffmpeg . probe ( f " assets/temp/ { reddit_id } /mp3/title.mp3 " ) [ ' format ' ] [ ' duration ' ] ) )
audio_clips_durations = [
float (
ffmpeg . probe ( f " assets/temp/ { reddit_id } /mp3/ { i } .mp3 " ) [ " format " ] [
" duration "
]
)
for i in range ( number_of_clips )
]
audio_clips_durations . insert (
0 ,
float (
ffmpeg . probe ( f " assets/temp/ { reddit_id } /mp3/title.mp3 " ) [ " format " ] [
" duration "
]
) ,
)
audio_concat = ffmpeg . concat ( * audio_clips , a = 1 , v = 0 )
ffmpeg . output ( audio_concat , f " assets/temp/ { reddit_id } /audio.mp3 " , * * { " b:a " : " 192k " } ) . overwrite_output ( ) . run (
quiet = True )
ffmpeg . output (
audio_concat , f " assets/temp/ { reddit_id } /audio.mp3 " , * * { " b:a " : " 192k " }
) . overwrite_output ( ) . run ( quiet = True )
console . log ( f " [bold green] Video Will Be: { length } Seconds Long " )
# Create a screenshot_width variable to scale the screenshots to the correct size, the calculation is int((W * 90) // 100)
@ -149,48 +179,72 @@ def make_final_video(
image_clips . insert (
0 ,
ffmpeg . input ( f " assets/temp/ { reddit_id } /png/title.png " ) [ ' v ' ]
. filter ( ' scale ' , screenshot_width , - 1 )
ffmpeg . input ( f " assets/temp/ { reddit_id } /png/title.png " ) [ " v " ] . filter (
" scale " , screenshot_width , - 1
) ,
)
current_time = 0
if settings . config [ " settings " ] [ " storymode " ] :
audio_clips_durations = [
float ( ffmpeg . probe ( f " assets/temp/ { reddit_id } /mp3/postaudio- { i } .mp3 " ) [ ' format ' ] [ ' duration ' ] ) for i in
range ( number_of_clips ) ]
audio_clips_durations . insert ( 0 , float (
ffmpeg . probe ( f " assets/temp/ { reddit_id } /mp3/title.mp3 " ) [ ' format ' ] [ ' duration ' ] ) )
float (
ffmpeg . probe ( f " assets/temp/ { reddit_id } /mp3/postaudio- { i } .mp3 " ) [
" format "
] [ " duration " ]
)
for i in range ( number_of_clips )
]
audio_clips_durations . insert (
0 ,
float (
ffmpeg . probe ( f " assets/temp/ { reddit_id } /mp3/title.mp3 " ) [ " format " ] [
" duration "
]
) ,
)
if settings . config [ " settings " ] [ " storymodemethod " ] == 0 :
image_clips . insert (
1 ,
ffmpeg . input ( f " assets/temp/ { reddit_id } /png/story_content.png " )
. filter ( ' scale ' , screenshot_width , - 1 )
ffmpeg . input ( f " assets/temp/ { reddit_id } /png/story_content.png " ) . filter (
" scale " , screenshot_width , - 1
) ,
)
background_clip = background_clip . overlay (
image_clips [ 1 ] ,
enable = f " between(t, { current_time } , { current_time + audio_clips_durations [ 1 ] } ) " ,
x = " (main_w-overlay_w)/2 " ,
y = " (main_h-overlay_h)/2 " ,
)
background_clip = background_clip . overlay ( image_clips [ 1 ] ,
enable = f ' between(t, { current_time } , { current_time + audio_clips_durations [ 1 ] } ) ' ,
x = ' (main_w-overlay_w)/2 ' , y = ' (main_h-overlay_h)/2 ' )
current_time + = audio_clips_durations [ 1 ]
elif settings . config [ " settings " ] [ " storymodemethod " ] == 1 :
for i in track (
range ( 0 , number_of_clips + 1 ) , " Collecting the image files... "
range ( 0 , number_of_clips + 1 ) , " Collecting the image files... "
) :
image_clips . append (
ffmpeg . input ( f " assets/temp/ { reddit_id } /png/img { i } .png " ) [ ' v ' ]
. filter ( ' scale ' , 1080 , - 1 )
ffmpeg . input ( f " assets/temp/ { reddit_id } /png/img { i } .png " ) [ " v " ] . filter (
" scale " , 1080 , - 1
)
)
background_clip = background_clip . overlay (
image_clips [ i ] ,
enable = f " between(t, { current_time } , { current_time + audio_clips_durations [ i ] } ) " ,
x = " (main_w-overlay_w)/2 " ,
y = " (main_h-overlay_h)/2 " ,
)
background_clip = background_clip . overlay ( image_clips [ i ] ,
enable = f ' between(t, { current_time } , { current_time + audio_clips_durations [ i ] } ) ' ,
x = ' (main_w-overlay_w)/2 ' , y = ' (main_h-overlay_h)/2 ' )
current_time + = audio_clips_durations [ i ]
else :
for i in range ( 0 , number_of_clips + 1 ) :
image_clips . append (
ffmpeg . input ( f " assets/temp/ { reddit_id } /png/comment_ { i } .png " ) [ ' v ' ]
. filter ( ' scale ' , screenshot_width , - 1 )
ffmpeg . input ( f " assets/temp/ { reddit_id } /png/comment_ { i } .png " ) [
" v "
] . filter ( " scale " , screenshot_width , - 1 )
)
background_clip = background_clip . overlay (
image_clips [ i ] ,
enable = f " between(t, { current_time } , { current_time + audio_clips_durations [ i ] } ) " ,
x = " (main_w-overlay_w)/2 " ,
y = " (main_h-overlay_h)/2 " ,
)
background_clip = background_clip . overlay ( image_clips [ i ] ,
enable = f ' between(t, { current_time } , { current_time + audio_clips_durations [ i ] } ) ' ,
x = ' (main_w-overlay_w)/2 ' , y = ' (main_h-overlay_h)/2 ' )
current_time + = audio_clips_durations [ i ]
title = re . sub ( r " [^ \ w \ s-] " , " " , reddit_obj [ " thread_title " ] )
@ -209,8 +263,7 @@ def make_final_video(
if settingsbackground [ " background_thumbnail " ] :
if not exists ( f " ./results/ { subreddit } /thumbnails " ) :
print_substep (
" The results/thumbnails folder didn ' t exist so I made it " )
print_substep ( " The results/thumbnails folder didn ' t exist so I made it " )
os . makedirs ( f " ./results/ { subreddit } /thumbnails " )
# get the first file with the .png extension from assets/backgrounds and use it as a background for the thumbnail
first_image = next (
@ -230,19 +283,33 @@ def make_final_video(
font_color = settingsbackground [ " background_thumbnail_font_color " ]
thumbnail = Image . open ( f " assets/backgrounds/ { first_image } " )
width , height = thumbnail . size
thumbnailSave = create_thumbnail ( thumbnail , font_family , font_size , font_color , width , height , title_thumb )
thumbnailSave = create_thumbnail (
thumbnail ,
font_family ,
font_size ,
font_color ,
width ,
height ,
title_thumb ,
)
thumbnailSave . save ( f " ./assets/temp/ { reddit_id } /thumbnail.png " )
print_substep ( f " Thumbnail - Building Thumbnail in assets/temp/ { reddit_id } /thumbnail.png " )
print_substep (
f " Thumbnail - Building Thumbnail in assets/temp/ { reddit_id } /thumbnail.png "
)
text = f " Background by { background_config [ 2 ] } "
background_clip = ffmpeg . drawtext ( background_clip ,
text = text ,
x = f ' (w-text_w) ' , y = f ' (h-text_h) ' ,
fontsize = 12 ,
fontcolor = " White " ,
fontfile = os . path . join ( " fonts " , " Roboto-Regular.ttf " ) )
background_clip = ffmpeg . drawtext (
background_clip ,
text = text ,
x = f " (w-text_w) " ,
y = f " (h-text_h) " ,
fontsize = 12 ,
fontcolor = " White " ,
fontfile = os . path . join ( " fonts " , " Roboto-Regular.ttf " ) ,
)
print_step ( " Rendering the video 🎥 " )
from tqdm import tqdm
pbar = tqdm ( total = 100 , desc = " Progress: " , bar_format = " {l_bar} {bar} " , unit = " % " )
def on_update_example ( progress ) :
@ -251,22 +318,31 @@ def make_final_video(
pbar . update ( status - old_percentage )
with ProgressFfmpeg ( length , on_update_example ) as progress :
ffmpeg . output ( background_clip , audio , f " results/ { subreddit } / { filename } .mp4 " , f = ' mp4 ' ,
* * { " c:v " : " h264 " , " b:v " : " 20M " , " b:a " : " 192k " ,
" threads " : multiprocessing . cpu_count ( ) } ) . overwrite_output ( ) . global_args ( ' -progress ' ,
progress . output_file . name ) . run (
quiet = True , overwrite_output = True , capture_stdout = False , capture_stderr = False )
ffmpeg . output (
background_clip ,
audio ,
f " results/ { subreddit } / { filename } .mp4 " ,
f = " mp4 " ,
* * {
" c:v " : " h264 " ,
" b:v " : " 20M " ,
" b:a " : " 192k " ,
" threads " : multiprocessing . cpu_count ( ) ,
} ,
) . overwrite_output ( ) . global_args ( " -progress " , progress . output_file . name ) . run (
quiet = True ,
overwrite_output = True ,
capture_stdout = False ,
capture_stderr = False ,
)
old_percentage = pbar . n
pbar . update ( 100 - old_percentage )
pbar . close ( )
if settingsbackground [ " background_thumbnail " ] :
if not exists ( f " ./results/ { subreddit } /thumbnails " ) :
print_substep (
" The results/thumbnails folder didn ' t exist so I made it " )
print_substep ( " The results/thumbnails folder didn ' t exist so I made it " )
os . makedirs ( f " ./results/ { subreddit } /thumbnails " )
# get the first file with the .png extension from assets/backgrounds and use it as a background for the thumbnail
first_image = next (
@ -286,13 +362,22 @@ def make_final_video(
font_color = settingsbackground [ " background_thumbnail_font_color " ]
thumbnail = Image . open ( f " assets/backgrounds/ { first_image } " )
width , height = thumbnail . size
thumbnailSave = create_thumbnail ( thumbnail , font_family , font_size , font_color , width , height , title_thumb )
thumbnailSave = create_thumbnail (
thumbnail , font_family , font_size , font_color , width , height , title_thumb
)
thumbnailSave . save ( f " ./assets/temp/ { reddit_id } /thumbnail.png " )
print_substep ( f " Thumbnail - Building Thumbnail in assets/temp/ { reddit_id } /thumbnail.png " )
print_substep (
f " Thumbnail - Building Thumbnail in assets/temp/ { reddit_id } /thumbnail.png "
)
# get the thumbnail image from assets/temp/id/thumbnail.png and save it in results/subreddit/thumbnails
if settingsbackground [ " background_thumbnail " ] and exists ( f " assets/temp/ { reddit_id } /thumbnail.png " ) :
shutil . move ( f " assets/temp/ { reddit_id } /thumbnail.png " , f " ./results/ { subreddit } /thumbnails/ { filename } .png " )
if settingsbackground [ " background_thumbnail " ] and exists (
f " assets/temp/ { reddit_id } /thumbnail.png "
) :
shutil . move (
f " assets/temp/ { reddit_id } /thumbnail.png " ,
f " ./results/ { subreddit } /thumbnails/ { filename } .png " ,
)
save_data ( subreddit , filename + " .mp4 " , title , idx , background_config [ 2 ] )
print_step ( " Removing temporary files 🗑 " )