1- from sys import argv ,exit # parse args
2- from os import path # split file extension
1+ from argparse import ArgumentParser
2+ from sys import argv ,exit
3+ from os import path , system
34from configparser import ConfigParser
4- from subprocess import run # Run vs
5- from random import choice # Randomize the smoothie's flavor))
5+ from subprocess import run
6+ from random import choice # Randomize smoothie's flavor
67
78# Bool aliases
89yes = ['True' ,'true' ,'yes' ,'y' ,'1' ]
910no = ['False' ,'false' ,'no' ,'n' ,'0' ,'null' ,'' ,None ]
1011
11- if len (argv ) == 1 :
12- print ('''
13- using Smoothie from the command line:
12+ parser = ArgumentParser ()
13+ parser .add_argument ("-peek" , "-p" , help = "render a specific frame (outputs an image)" , action = "store" , nargs = 1 , metavar = '752' , type = int )
14+ parser .add_argument ("-trim" , "-t" , help = "Trim out the frames you don't want to use" , action = "store" , nargs = 1 , metavar = '0:23,1:34' , type = str )
15+ parser .add_argument ("-dir" , help = "opens the directory where Smoothie resides" , action = "store_true" )
16+ parser .add_argument ("-recipe" , "-rc" , help = "opens default recipe.ini" , action = "store_true" )
17+ parser .add_argument ("--config" , "-c" , help = "specify override config file" , action = "store" , nargs = 1 , metavar = 'PATH' , type = str )
18+ parser .add_argument ("--encoding" ,"-enc" , help = "specify override ffmpeg encoding arguments" , action = "store" , type = str )
19+ parser .add_argument ("-verbose" , "-v" , help = "increase output verbosity" , action = "store_true" )
20+ parser .add_argument ("-curdir" , "-cd" , help = "save all output to current directory" , action = "store_true" , )
21+ parser .add_argument ("-input" , "-i" , help = "specify input video path(s)" , action = "store" , nargs = "+" , metavar = 'PATH' , type = str )
22+ parser .add_argument ("-vpy" , help = "specify a VapourSynth script" , action = "store" , nargs = 1 , metavar = 'PATH' , type = str )
23+ args = parser .parse_args ()
24+
25+ if args .dir :
26+ run (f'explorer { path .dirname (argv [0 ])} ' )
27+ exit (0 )
28+ if args .recipe :
29+ recipe = path .abspath (path .join (path .dirname (__file__ ), "settings/recipe.ini" ))
30+ if path .exists (recipe ) == False :
31+ print ("config path does not exist (are you messing with files?), exitting" )
32+ run ('powershell -NoLogo' )
33+ run (f'explorer { recipe } ' )
34+ exit (0 )
1435
15- sm "D:\Video\input1.mp4" "D:\Video\input2.mp4" ...
16- Simply give in the path of the videos you wish to queue to smoothie
1736
18- sm config.extension "D:\Video\input1.mp4" "D:\Video\input2.mp4" ...
19- You can also make the first argument be your custom config file's name, it'll look for it in the settings folder
20- ''' )
21- exit (0 )
37+ conf = ConfigParser ()
2238
23- def ensure (file , desc ):
24- if path .exists (file ) == False :
25- print (f"{ desc } file not found: { file } " )
26- exit (1 )
39+ if args .config :
40+ config_filepath = path .abspath (args .config [0 ])
41+ conf .read (config_filepath )
42+ else :
43+ config_filepath = path .abspath (path .join (path .dirname (__file__ ), "settings/recipe.ini" ))
44+ conf .read (config_filepath )
2745
28- if argv == [argv [0 ],'folder' ]:
29- run (f'explorer { path .dirname (argv [0 ])} ' )
30- exit (0 )
46+ if path .exists (config_filepath ) in [False ,None ]:
47+ print ("config path does not exist, exitting" )
48+ run ('powershell -NoLogo' )
49+ elif args .verbose :
50+ print (f"VERBOSE: using config file: { config_filepath } " )
3151
32- if path .splitext (argv [1 ])[1 ] in ['.ini' ,'.txt' ]:
52+ if args .input in [no , None ]:
53+ parser .parse_args ('-h' .split ()) # If the user does not pass any args, just redirect to -h (Help)
3354
34- if path .dirname (argv [1 ]) == '' : # If no directory, look for it in the settings folder
35- recipe = path .join (path .dirname (argv [0 ]), f"settings\\ { argv [1 ]} " )
36- config_filepath = recipe
37- conf = ConfigParser ()
38- conf .read (recipe )
39- else : # If there is a directory, it's a full path so just load it in
40- conf = ConfigParser ()
41- conf .read (argv [1 ])
42- config_filepath = argv [1 ]
55+ round = 0 # Reset the round counter
4356
44- queue = argv [2 :]
45- else :
46- recipe = path .join (path .dirname (__file__ ), "settings\\ recipe.ini" )
47- config_filepath = recipe
48- conf = ConfigParser ()
49- conf .read (recipe )
50- queue = argv [1 :]
57+ for video in args .input : # Loops through every single video
58+
59+ # Title
60+
61+ round += 1
62+
63+ title = "Smoothie - " + path .basename (video )
64+
65+ if len (args .input ) > 1 :
66+ title = f'[{ round } /{ len (args .input )} ] ' + title
5167
52- for video in queue :
68+ system (f"title { title } " )
69+
70+ # Suffix
5371
5472 if str (conf ['misc' ]['flavors' ]) in [yes ,'fruits' ]:
5573 flavors = [
56- 'Strawberry ' ,'Blueberry ' ,'Raspberry ' ,'Blackberry ' ,'Cherry ' ,'Cranberry ' ,'Coconut ' ,'Pineapple ' ,'Kiwi'
57- 'Peach' ,'Apricot' ,'Dragonfuit ' ,'Grapefruit' , ' Melon' ,'Papaya' ,'Watermelon' , ' Banana' ,'Apple' ,'Pear' ,'Orange'
74+ 'Berry ' ,'Cherry ' ,'Cranberry ' ,'Coconut ' ,'Kiwi ' ,'Avocado ' ,'Durian ' ,'Lemon ' ,'Lime' , 'Fig' , 'Mirabelle' ,
75+ 'Peach' ,'Apricot' ,'Grape ' ,'Melon' ,'Papaya' ,'Banana' ,'Apple' ,'Pear' ,'Orange' , 'Mango' , 'Plum' , 'Pitaya '
5876 ]
5977 else :
6078 flavors = ['Smoothie' ]
6179
62- filename , ext = path .splitext (video )
80+ # Extension
81+
82+ if args .peek :
83+ ext = '.png'
84+ elif conf ['misc' ]['container' ] in no :
85+ ext = path .splitext (video )[1 ]
86+ else :
87+ ext = conf ['misc' ]['container' ]
88+
89+ filename = path .basename (path .splitext (video )[0 ])
6390
64- if conf [ 'misc' ][ 'folder' ] in no :
91+ # Directory
6592
93+ if args .curdir :
94+ outdir = path .abspath (path .curdir )
95+ elif conf ['misc' ]['folder' ] in no :
6696 outdir = path .dirname (video )
6797 else :
6898 outdir = conf ['misc' ]['folder' ]
6999
70- out = path .join (outdir , filename + f' - { choice (flavors )} ' + ext )
100+ out = path .join (outdir , filename + f' - { choice (flavors )} { ext } ' )
71101
72102 count = 2
73103 while path .exists (out ):
74- out = path .join (outdir , filename + f' - { choice (flavors )} ' + f' ({ count } )' + ext )
104+ out = path .join (outdir , f' { filename } - { choice (flavors )} ({ count } ){ ext } ' )
75105 count += 1
76106
107+ # VapourSynth
108+
77109 vspipe = path .join (path .dirname ((path .dirname (__file__ ))),'VapourSynth' ,'VSPipe.exe' )
78- vpy = 'blender.vpy'
110+
111+ if args .vpy :
112+
113+ if path .dirname (args .vpy [0 ]) in no :
114+
115+ vpy = path .join ( path .dirname (__file__ ), (args .vpy [0 ]) )
116+ else :
117+ vpy = path .abspath (args .vpy [0 ])
118+ else :
119+ vpy = path .abspath (path .join (path .dirname (__file__ ),'blender.vpy' ))
79120
80- command = [ # Split in two for readability
81-
82- f"cmd /c { vspipe } -y \" { path .join (path .dirname (__file__ ), vpy )} \" --arg input_video=\" { video } \" --arg config_filepath=\" { config_filepath } \" " ,
83- f"- | { conf ['encoding' ]['process' ]} -hide_banner -loglevel warning -stats -i - { conf ['encoding' ]['args' ]} \" { out } \" "
84- # -i \"{video}\" -map 0:v -map 1:a?
121+ command = [ # This is the master command, it gets appended some extra output args later down
122+ f'{ vspipe } -y "{ vpy } " --arg input_video="{ video } " --arg config_filepath="{ config_filepath } " ' ,
123+ f'- | { conf ["encoding" ]["process" ]} -hide_banner -loglevel warning -stats -i - ' ,
85124 ]
86- if (conf ['misc' ]['verbose' ]) in yes :
87- print (command )
88- print (f"VSPipe: { vspipe } " )
89- print (f"VIDEO: { video } " )
90125
91- run (' ' .join (command ),shell = True )
126+ if args .peek :
127+ frame = int (args .peek [0 ]) # Extracting the frame passed from the singular array
128+ command [0 ] += f'--start { frame } --end { frame } '
129+ command [1 ] += f' "{ out } "' # No need to specify audio map, simple image output
130+ elif args .trim :
131+ command [0 ] += f'--arg trim="{ args .trim } "'
132+ command [1 ] += f'{ conf ["encoding" ]["args" ]} "{ out } "'
133+ else :
134+ # Adds as input the video to get it's audio tracks and gets encoding arguments from the config file
135+ command [1 ] += f'-i "{ path .abspath (video )} " -map 0:v -map 1:a? { conf ["encoding" ]["args" ]} "{ out } "'
136+
137+ if args .verbose :
138+ command [0 ] += ' --arg verbose=True'
139+ for cmd in command : print (cmd )
140+ print (f"Queuing video: { video } " )
141+
142+ run (' ' .join (command ),shell = True )
143+
144+ system (f"title [{ round } /{ len (args .input )} ] Smoothie - Finished! (EOF)" )
0 commit comments