diff --git a/.gitignore b/.gitignore index 697c0f8..55446bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ *.exe *.DS_store *.spec +*.rc +*.ico +.gitignore +.gitattributes build dist __pycache__ \ No newline at end of file diff --git a/LICENSE b/LICENSE index f08fa3b..6b12e9c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Guillermo-Hidalgo-Gadea +Copyright (c) 2022 Guillermo-Hidalgo-Gadea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6f98836..640e046 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # VideoPyToolbox [logo](https://guillermohidalgogadea.com/) -Play, compress, trim and concatenate videos in python using FFmpeg - +Play, compress, trim and concatenate videos using ffmpeg ## What it is This Toolbox is a python wrapper for a bunch of useful ffmpeg commands to play, compress and edit videos using ffmpeg. @@ -11,15 +10,12 @@ When it comes to video compression, FFmpeg can be challenging to use over comman logo ## How to use it -The Toolbox is built as an interactive terminal prompt to guide you step by step through the process of concatenating, compressing, spliting and playing videos. You can either use the `VideoPyToolbox.py` script in your IDE, or run it from the terminal. You can find a compiled version of VideoPyToolbox [here](https://gitlab.ruhr-uni-bochum.de/ikn/syncflir/-/blob/master/PostProcessing/VideoPyToolbox.exe). +The Toolbox is built as an interactive terminal prompt to guide you step by step through the process of concatenating, compressing, spliting and playing videos. You can either use the `VideoPyToolbox.py` script in your IDE, or run it from the terminal. You can find a compiled version of VideoPyToolbox [here](https://gitlab.ruhr-uni-bochum.de/ikn/syncflir/-/blob/master/PostProcessing/VideoPyToolbox.exe). Compiled in Windows with pyinstaller v5.1 using `pyinstaller --add-binary ffmpeg.exe;. --add-binary ffplay.exe;. --onefile --icon=logo.ico VideoPyToolbox.py`. ## Features The video player allows to stream up to 9 files at the same time, for example to check for synchronizity. Video compression is set to `hevc/h.265`, but could be expanded in future releases. GPU mode uses hardware acceleration for encoding with `hevc_nvenc`. The trim/split function to extract video snippets between timestamps needs to re-encode the video with `h.265 cfr 0` or `hevc_nvenc cq 0` respectively, to achieve "frame-accurate" splits. Lossless splits are not accurate enough if the chosen timestamp does not happen to contain a keyframe, which can result in timeshifts of up to several seconds (depending on framerate). ## Next release -* enhanced batch processing for split from multiple directories -* choice between losless and re-encoded trim/split -* choose from wider variety of compression codecs * play audio from video with waveform and timestamp -* create mosaic video +* scrap all files in directory and subdirectory * tbd diff --git a/VideoPyToolbox.py b/VideoPyToolbox.py index c81ba70..f929ea3 100644 --- a/VideoPyToolbox.py +++ b/VideoPyToolbox.py @@ -5,9 +5,7 @@ to play a video recording, compress it using h265 codec, or to concatenate and trim clips from timestamps. Use the custom pipeline to compress and concat at once. -'conda install ffmpeg' if not already installed - -MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com +MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com Sourcecode: https://github.com/Guillermo-Hidalgo-Gadea/VideoPyToolbox ==================================================================================================== @@ -22,11 +20,11 @@ # import functions from toolbox.function_ffplay import ffplay -from toolbox.function_compress import compress_h265, compress_hevc_nvenc +from toolbox.function_compress import compress_h265, compress_h264, compress_hevc_nvenc from toolbox.function_concatenate import concat_videos -from toolbox.function_custom import the_usual_h265, the_usual_hevc_nvenc -from toolbox.function_rename import batch_rename +from toolbox.function_custom import the_usual_h265 from toolbox.function_split import trim_split_h265, trim_split_hevc_nvenc +from toolbox.function_mosaic import mosaic_video from toolbox.logo import ascii_logo, width, height # Helper functions @@ -58,6 +56,7 @@ def check_gpu(): # Terminal print colors RESET = "\033[0;0m" MATRIX = "\033[0;32m" +YELLOW = "\033[0;33m" CYAN = "\033[0;36m" RED = "\033[0;31m" @@ -73,11 +72,11 @@ def check_gpu(): # header print("#"*width + "\n") - print("MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com\n".center(width)) + print("MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com\n".center(width)) print("#"*width) #logo in ascii here - sys.stdout.write(MATRIX) + sys.stdout.write(YELLOW) print(ascii_logo.center(width)) sys.stdout.write(RESET) instructions = "VideoPy is a FFMPEG wrapper to play videos, to compress and change codecs, as well as to append and split raw videos." @@ -86,11 +85,11 @@ def check_gpu(): # function help format help = ''' 'p' Play audio or video file - 'c' Compress video to h.265/h.264 + 'c' Compress video with h265/h264 'a' Append or Concatenate multiple files 's' Split or Trim files by timestamp - 'r' Rename files in batches 'u' Custom pipeline to compress and concatenate + 'm' Create multi-view mosaic video 'gpu' Check for hardware acceleration 'q' Quit ''' @@ -104,7 +103,7 @@ def check_gpu(): # header print("#"*width + "\n") - print("MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com\n".center(width)) + print("MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com\n".center(width)) print("#"*width) #logo in ascii here @@ -117,11 +116,11 @@ def check_gpu(): # function help format help = ''' 'p' Play audio or video file - 'c' Compress video to h.265/h.264 + 'c' Compress video with h265/h264 'a' Append or Concatenate multiple files 's' Split or Trim files by timestamp - 'r' Rename files in batches 'u' Custom pipeline to compress and concatenate + 'm' Create multi-view mosaic video 'gpu' Check for hardware acceleration 'q' Quit ''' @@ -150,10 +149,17 @@ def check_gpu(): # start compression print("\nStart compression with ffmpeg... \n") if "y" not in gpu_use: - compress_h265() - input("\nCompressing ended!\n") - # reset while loop - choice = 'main' + encoder = input("\n(CPU mode) Choose video encoder [h264/h265]: ") + if "4" in encoder: + compress_h264() + input("\nCompressing ended!\n") + # reset while loop + choice = 'main' + elif "5" in encoder: + compress_h265() + input("\nCompressing ended!\n") + # reset while loop + choice = 'main' else: compress_hevc_nvenc() input("\nCompressing ended!\n") @@ -173,15 +179,25 @@ def check_gpu(): elif choice.startswith("s"): print("\nStart trimming videos in ffmpeg... \n") if "y" not in gpu_use: - trim_split_h265() - input("\nTrimming ended!\n") - # reset while loop - choice = 'main' + try: + trim_split_h265() + input("\nTrimming ended!\n") + # reset while loop + choice = 'main' + except: + input("\nTrimming ended!\n") + # reset while loop + choice = 'main' else: - trim_split_hevc_nvenc() - input("\nTrimming ended!\n") - # reset while loop - choice = 'acc_main' + try: + trim_split_hevc_nvenc() + input("\nTrimming ended!\n") + # reset while loop + choice = 'acc_main' + except: + input("\nTrimming ended!\n") + # reset while loop + choice = 'acc_main' elif choice.startswith("u"): print("\nAh, the usual. Comming right up... \n") @@ -191,20 +207,23 @@ def check_gpu(): # reset while loop choice = 'main' else: - the_usual_hevc_nvenc() + the_usual_h265() input("\nPipeline ended!\n") # reset while loop choice = 'acc_main' - elif choice.startswith("r"): - print("\nStart renaming files... \n") - batch_rename() - input("\nRenaming ended!\n") - # reset while loop - if "y" in gpu_use: - choice = 'acc_main' - else: + elif choice.startswith("m"): + print("\nStart creating mosaic video... \n") + if "y" not in gpu_use: + mosaic_video() + input("\nMosaic video created!\n") + # reset while loop choice = 'main' + else: + mosaic_video() + input("\nMosaic video created!\n") + # reset while loop + choice = 'acc_main' elif choice.startswith("q"): break diff --git a/VideoPyToolbox.spec b/VideoPyToolbox.spec index 8b274be..5369fdb 100644 --- a/VideoPyToolbox.spec +++ b/VideoPyToolbox.spec @@ -4,41 +4,42 @@ block_cipher = None -a = Analysis(['VideoPyToolbox.py'], - pathex=[], - binaries=[], - datas=[], - hiddenimports=[], - hookspath=[], - hooksconfig={}, - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, - noarchive=False) -pyz = PYZ(a.pure, a.zipped_data, - cipher=block_cipher) +a = Analysis( + ['VideoPyToolbox.py'], + pathex=[], + binaries=[('ffmpeg.exe', '.'), ('ffplay.exe', '.')], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) -exe = EXE(pyz, - a.scripts, - a.binaries, - a.zipfiles, - a.datas, - [], - name='VideoPyToolbox', - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=True, - upx_exclude=[], - runtime_tmpdir=None, - console=False, - disable_windowed_traceback=False, - target_arch=None, - codesign_identity=None, - entitlements_file=None , icon='logo.ico') -app = BUNDLE(exe, - name='VideoPyToolbox.app', - icon='logo.ico', - bundle_identifier=None) +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='VideoPyToolbox', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon='logo.ico', version='version.rc', +) diff --git a/logo.ico b/logo.ico index 3fe5ef2..51eeb50 100644 Binary files a/logo.ico and b/logo.ico differ diff --git a/screen.PNG b/screen.PNG index 67932ae..6ffbb7b 100644 Binary files a/screen.PNG and b/screen.PNG differ diff --git a/toolbox/function_compress.py b/toolbox/function_compress.py index 40aa454..8fd6dc4 100644 --- a/toolbox/function_compress.py +++ b/toolbox/function_compress.py @@ -2,16 +2,14 @@ ==================================================================================================== Helper function for the VideoPyToolbox -MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com +MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com Sourcecode: https://github.com/Guillermo-Hidalgo-Gadea/VideoPyToolbox ==================================================================================================== ''' import os # paths and terminal commands -import pandas as pd # data frames for metadata -from tkinter import filedialog # interactive interface to selet files -from datetime import datetime # get timestamp for filenames +from tkinter import filedialog # interactive interface to selet files def compress_h265(): ''' @@ -31,43 +29,53 @@ def compress_h265(): else: outputdir = secondtry - # set output directory - path = outputdir + '/compressed/' - try: - os.mkdir(path) - except: - pass - - outputs = [path + 'h265_' + os.path.basename(filename) for filename in videofile] - # compress videos with libx265 encoder - - # crf from 0 to 51, 18 recommended lossless average print("HEVC/H265 compression...") - crf = input("Choose constant rate factor between 0-50: ") or 18 + # crf from 0 to 51, 12 recommended for lossless average + crf = input("Choose constant rate factor between 0-50: ") or 12 - starttime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") + # set output filenames + path = outputdir + outputs = [path + 'h265_crf{crf}_' + os.path.basename(filename) for filename in videofile] for i, video in enumerate(videofile): - output = outputs[i] + output = outputs[i].split('.')[0] # strip video container - ffmpeg_command = f"ffmpeg -y -i {video} -an -vcodec libx265 -crf {crf} {output}" + ffmpeg_command = f"ffmpeg -y -i {video} -an -dn -vcodec libx265 -crf {crf} {output}.mp4" os.system(ffmpeg_command) - endtime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") +def compress_h264(): + ''' + Video Compression with H264: This function reads video filenames from a list and compresses with H264 codec. Audio is removed. + ''' + # select batch to compress multiple + videofile = list(filedialog.askopenfilenames(title='Choose Video Files you want to compress')) + if not videofile: + return + + outputdir = filedialog.askdirectory(title='Choose Output Directory for Compression') + if not outputdir: + print('Please select a valid output directory') + secondtry = filedialog.askdirectory(title='Choose Output Directory for Concatenation') + if not secondtry: + return + else: + outputdir = secondtry + + # compress videos with libx265 encoder + print("AVC/H264 compression...") + # crf from 0 to 51, 12 recommended for lossless average + crf = input("Choose constant rate factor between 0-50: ") or 12 + + # set output filenames + path = outputdir + outputs = [path + 'h264_crf{crf}_' + os.path.basename(filename) for filename in videofile] - # save metadata - metadata = path + '/' + 'h265_compression_' + starttime + '.txt' - filelist = pd.DataFrame({'input': videofile, 'output': outputs}, index=None) - filelist.to_csv(metadata, sep ='\t', index=False) - # add header - title = f"# VideoPyToolbox compression with libx265 crf={crf} \n # started: {starttime} ended: {endtime} \n" - with open(metadata, "r+") as textfile: - original = textfile.read() - textfile.seek(0) - textfile.write(title + original) - textfile.close() + for i, video in enumerate(videofile): + output = outputs[i].split('.')[0] # strip video container + ffmpeg_command = f"ffmpeg -y -i {video} -an -dn -vcodec libx264 -crf {crf} {output}.mp4" + os.system(ffmpeg_command) def compress_hevc_nvenc(): ''' @@ -87,37 +95,16 @@ def compress_hevc_nvenc(): else: outputdir = secondtry - # set output directory - path = outputdir + '/compressed/' - try: - os.mkdir(path) - except: - pass - - outputs = [path + 'hevc_nvenc_' + os.path.basename(filename) for filename in videofile] - # compress videos with hevc_nvenc encoder print("HEVC_NVENC compression...") - cq = input("Choose constant quantization parameter between 0-50: ") or 18 + cq = input("Choose constant quantization parameter between 0-50: ") or 12 - starttime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") + # set output directory + path = outputdir + outputs = [path + 'hevc_nvenc_cq{cq}_' + os.path.basename(filename) for filename in videofile] for i, video in enumerate(videofile): - output = outputs[i] + output = outputs[i].split('.')[0] # strip video container - ffmpeg_command = f"ffmpeg -y -i {video} -an -vcodec hevc_nvenc -preset p7 -rc vbr -cq {cq} {output}" + ffmpeg_command = f"ffmpeg -y -i {video} -an -vcodec hevc_nvenc -preset p7 -rc vbr -cq {cq} {output}.mp4" os.system(ffmpeg_command) - - endtime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") - - # save metadata - metadata = path + '/' + 'hevc_nvenc_compression_' + starttime + '.txt' - filelist = pd.DataFrame({'input': videofile, 'output': outputs}, index=None) - filelist.to_csv(metadata, sep ='\t', index=False) - # add header - title = f"# VideoPyToolbox compression with hevc_nvenc preset=p7, rc=vbr, cq={cq} \n # started: {starttime} ended: {endtime}" - with open(metadata, "r+") as textfile: - original = textfile.read() - textfile.seek(0) - textfile.write(title + original) - textfile.close() \ No newline at end of file diff --git a/toolbox/function_concatenate.py b/toolbox/function_concatenate.py index a25ce0f..ac38f05 100644 --- a/toolbox/function_concatenate.py +++ b/toolbox/function_concatenate.py @@ -2,7 +2,7 @@ ==================================================================================================== Helper function for the VideoPyToolbox -MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com +MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com Sourcecode: https://github.com/Guillermo-Hidalgo-Gadea/VideoPyToolbox ==================================================================================================== @@ -63,12 +63,7 @@ def concat_videos(): # set output directory #output = os.path.join(videopath , "concatenated", outputfile) - output = os.path.join(outputdir , "concatenated", outputfile) - - try: - os.mkdir(os.path.dirname(output)) - except: - pass + output = os.path.join(outputdir, outputfile) # write mylist.txt for ffmpeg path, _ = os.path.split(filenames[0]) @@ -94,4 +89,4 @@ def concat_videos(): np.savetxt(metadata, concats, fmt = "%s") #break while loop - break + break \ No newline at end of file diff --git a/toolbox/function_custom.py b/toolbox/function_custom.py index bdb8521..d397d19 100644 --- a/toolbox/function_custom.py +++ b/toolbox/function_custom.py @@ -2,7 +2,7 @@ ==================================================================================================== Helper function for the VideoPyToolbox -MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com +MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com Sourcecode: https://github.com/Guillermo-Hidalgo-Gadea/VideoPyToolbox ==================================================================================================== @@ -12,7 +12,6 @@ import numpy as np # use arrays in concat from tkinter import filedialog # interactive interface to selet files from natsort import natsorted, ns # natural sorting of strings with numbers -from datetime import datetime # get timestamp for filenames def the_usual_h265(): @@ -37,7 +36,7 @@ def the_usual_h265(): _, name1 = os.path.split(filenames[0]) _, name2 = os.path.split(filenames[-1]) output = input(f"Output filename for {name1} to {name2}: ") - output = [output + '_h265.mp4'] * len(filenames) + output = ['h265_crf12_' + output + '.mp4'] * len(filenames) # append or create concatenation dataset try: @@ -50,18 +49,16 @@ def the_usual_h265(): if next =='n': # ask output directory - outputdir =filedialog.askdirectory(title='Choose Output Directory for Concatenation') + outputdir =filedialog.askdirectory(title='Choose Output Directory:') if not outputdir: print('Please select a valid output directory') - secondtry = filedialog.askdirectory(title='Choose Output Directory for Concatenation') + secondtry = filedialog.askdirectory(title='Choose Output Directory:') if not secondtry: return # set compression print("HEVC/H265 compression...") - crf = input("Choose constant rate factor between 0-50: ") or 18 - - starttime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") + crf = 12 # recommended lossless # find unique output names in concats for outputfile in np.unique(concats[:,1]): @@ -69,12 +66,7 @@ def the_usual_h265(): filenames = concats[concats[:,1]==outputfile][:,0] # set output directory - output = os.path.join(outputdir , "concatenated", outputfile) - - try: - os.mkdir(os.path.dirname(output)) - except: - pass + output = os.path.join(outputdir, outputfile) # write mylist.txt for ffmpeg path, _ = os.path.split(filenames[0]) @@ -89,112 +81,6 @@ def the_usual_h265(): ffmpeg_command = f"ffmpeg -y -f concat -safe 0 -i {myfile} -an -vcodec libx265 -crf {crf} {output}" os.system(ffmpeg_command) os.remove(myfile) - - endtime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") - - # save metadata - metadata = outputdir + '/' + 'concat_compression_' + starttime + '.txt' - np.savetxt(metadata, concats, fmt = "%s") - - # add header - title = f"# VideoPyToolbox concat_compression with libx265 crf={crf} \n # started: {starttime} ended: {endtime}\n " - with open(metadata, "r+") as textfile: - original = textfile.read() - textfile.seek(0) - textfile.write(title + original) - textfile.close() - - #break while loop - break - -def the_usual_hevc_nvenc(): - ''' - The usual is a custom pipeline to concat and compress video snippets at once. See concat_videos and compress_hevc_nvenc for doing the processing in separate steps. - ''' - - next = 'y' - - while True: - - if next == 'y': - # select videos to append - concatlist= filedialog.askopenfilenames(title='Choose Video Files you want to concatenate') - if not concatlist: - return - - # maintain natural order of strings with numbers - filenames = list(natsorted(concatlist, alg=ns.IGNORECASE)) - - # give common output filename - _, name1 = os.path.split(filenames[0]) - _, name2 = os.path.split(filenames[-1]) - output = input(f"Output filename for {name1} to {name2}: ") - output = [output + '_h265.mp4'] * len(filenames) - - # append or create concatenation dataset - try: - concats = np.vstack((concats, np.column_stack((filenames, output)))) - except: - concats = np.column_stack((filenames, output)) - - # repeat for other sessions... - next = input(f"Concatenate more sessions [y/N]: ") - - if next =='n': - # ask output directory - outputdir =filedialog.askdirectory(title='Choose Output Directory for Concatenation') - if not outputdir: - print('Please select a valid output directory') - secondtry = filedialog.askdirectory(title='Choose Output Directory for Concatenation') - if not secondtry: - return - - # set compression - print("HEVC_NVENC compression...") - cq = input("Choose constant quantization parameter between 0-50: ") or 18 - - starttime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") - - # find unique output names in concats - for outputfile in np.unique(concats[:,1]): - # get all filenames that correspond to output - filenames = concats[concats[:,1]==outputfile][:,0] - - # set output directory - output = os.path.join(outputdir , "concatenated", outputfile) - - try: - os.mkdir(os.path.dirname(output)) - except: - pass - - # write mylist.txt for ffmpeg - path, _ = os.path.split(filenames[0]) - myfile = path + "/mylist.txt" - with open(myfile, "w+") as textfile: - for element in filenames: - _, name = os.path.split(element) - textfile.write("file " +"'"+ name + "'\n") - textfile.close() - - # concatenate file with re-encoding - ffmpeg_command = f"ffmpeg -y -f concat -safe 0 -i {myfile} -an -vcodec hevc_nvenc -preset p7 -rc vbr -cq {cq} {output}" - os.system(ffmpeg_command) - os.remove(myfile) - - endtime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") - - # save metadata - metadata = outputdir + '/' + 'concat_compression_' + starttime + '.txt' - np.savetxt(metadata, concats, fmt = "%s") - - # add header - title = f"# VideoPyToolbox concat_compression with hevc_nvenc preset=p7, rc=vbr, cq={cq}\n # started: {starttime} ended: {endtime}\n" - with open(metadata, "r+") as textfile: - original = textfile.read() - textfile.seek(0) - textfile.write(title + original) - textfile.close() - + #break while loop break \ No newline at end of file diff --git a/toolbox/function_ffplay.py b/toolbox/function_ffplay.py index 4d6196f..96e6225 100644 --- a/toolbox/function_ffplay.py +++ b/toolbox/function_ffplay.py @@ -2,7 +2,7 @@ ==================================================================================================== Helper function for the VideoPyToolbox -MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com +MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com Sourcecode: https://github.com/Guillermo-Hidalgo-Gadea/VideoPyToolbox ==================================================================================================== @@ -16,7 +16,6 @@ def ffplay(): Video player with ffplay and mosaic display ''' # Choose Video File from Dialog Box - videofiles = list(filedialog.askopenfilenames(title='Choose the Video Files you want to play')) if not videofiles: return diff --git a/toolbox/function_mosaic.py b/toolbox/function_mosaic.py new file mode 100644 index 0000000..71eaa22 --- /dev/null +++ b/toolbox/function_mosaic.py @@ -0,0 +1,60 @@ +''' +==================================================================================================== +Helper function for the VideoPyToolbox + +MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com + +Sourcecode: https://github.com/Guillermo-Hidalgo-Gadea/VideoPyToolbox +==================================================================================================== +''' + +import os +from tkinter import filedialog + +def mosaic_video(): + ''' + Create Mosaic Videos + ''' + # Choose Video File from Dialog Box + videofiles = list(filedialog.askopenfilenames(title='Choose the Video Files to combine')) + if not videofiles: + return + + outputdir = filedialog.askdirectory(title='Choose Output Directory for Compression') + if not outputdir: + print('Please select a valid output directory') + secondtry = filedialog.askdirectory(title='Choose Output Directory for Concatenation') + if not secondtry: + return + else: + outputdir = secondtry + + output = os.path.join(outputdir, 'mosaic_' + os.path.basename(videofiles[0]).split('.')[0]) + + if len(videofiles) == 2: + ffmpeg_command = f'ffmpeg -i {videofiles[0]} -i {videofiles[1]} -filter_complex "[0:v][1:v]hstack=inputs=2[v];[v]scale=w=1440:h=-1[scaled]" -map "[scaled]" {output}.mp4' + os.system(ffmpeg_command) + + elif len(videofiles) == 3: + ffmpeg_command = f'ffmpeg -i {videofiles[0]} -i {videofiles[1]} -i {videofiles[2]} -filter_complex "[0:v][1:v][2:v]hstack=inputs=3[v];[v]scale=w=1440:h=-1[scaled]" -map "[scaled]" {output}.mp4' + os.system(ffmpeg_command) + + elif len(videofiles) == 4: + ffmpeg_command = f'ffmpeg -i {videofiles[0]} -i {videofiles[1]} -i {videofiles[2]} -i {videofiles[3]} -filter_complex ' \ + f'"[0:v][1:v]hstack=inputs=2[top];[2:v][3:v]hstack=inputs=2[bottom];[top][bottom]vstack=inputs=2[v];[v]scale=w=1440:h=-1[scaled]" -map "[scaled]" {output}.mp4' + os.system(ffmpeg_command) + + elif len(videofiles) == 6: + ffmpeg_command = f'ffmpeg '\ + f'-i {videofiles[0]} -i {videofiles[1]} -i {videofiles[2]} -i {videofiles[3]} -i {videofiles[4]} -i {videofiles[5]} '\ + f'-filter_complex "[0:v][1:v][2:v]hstack=inputs=3[top];[3:v][4:v][5:v]hstack=inputs=3[bottom];[top][bottom]vstack=inputs=2[v];[v]scale=w=1440:h=-1[scaled]" -map "[scaled]" {output}.mp4' + os.system(ffmpeg_command) + + elif len(videofiles) == 8: + ffmpeg_command = f'ffmpeg '\ + f'-i {videofiles[0]} -i {videofiles[1]} -i {videofiles[2]} -i {videofiles[3]} -i {videofiles[4]} -i {videofiles[5]} -i {videofiles[6]} -i {videofiles[7]} '\ + f'-filter_complex "[0:v][1:v][2:v][3:v]hstack=inputs=4[top];[4:v][5:v][6:v][7:v]hstack=inputs=4[bottom];[top][bottom]vstack=inputs=2[v];[v]scale=w=1440:h=-1[scaled]" -map "[scaled]" {output}.mp4' + os.system(ffmpeg_command) + + else: + print("Invalid number of videos! (valid: 2, 3, 4, 6 or 8") diff --git a/toolbox/function_rename.py b/toolbox/function_rename.py deleted file mode 100644 index 1f91e04..0000000 --- a/toolbox/function_rename.py +++ /dev/null @@ -1,49 +0,0 @@ -''' -==================================================================================================== -Helper function for the VideoPyToolbox - -MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com - -Sourcecode: https://github.com/Guillermo-Hidalgo-Gadea/VideoPyToolbox -==================================================================================================== -''' - -import os # paths and terminal commands -from tkinter import filedialog # interactive interface to selet files - -def batch_rename(): - ''' - This function reads a list of files and appends/prepends string filename - ''' - - choice = 'y' - - while True: - if choice == 'y': - # choose batch of files to rename - renamelist = filedialog.askopenfilenames(title='Choose Video Batch to rename') - if not renamelist: - return - - batch = list(renamelist) - dir_list = [os.path.dirname(file)for file in batch] - name_list = [os.path.basename(file).split('.')[0] for file in batch] - suffix_list = [os.path.basename(file).split('.')[1] for file in batch] - - # ask for - prepend = input(f"Prepend string for videos ..._{name_list[0]}: ") - append = input(f"Append string for videos {name_list[0]}_... : ") - - #edit new file names - newnames = [prepend+'_'+name+'_'+append+'.'+suffix for name, suffix in zip(name_list, suffix_list)] - newbatch = [dir+'/'+file for dir,file in zip(dir_list,newnames)] - - # rename files - for i in range(len(batch)): - os.rename(batch[i], newbatch[i]) - - # reset loop - choice = input(f"Rename another Video Batch [y/N]: ") - - else: - break \ No newline at end of file diff --git a/toolbox/function_split.py b/toolbox/function_split.py index 8053355..b4ac4d0 100644 --- a/toolbox/function_split.py +++ b/toolbox/function_split.py @@ -2,7 +2,7 @@ ==================================================================================================== Helper function for the VideoPyToolbox -MIT License Copyright (c) 2021 GuillermoHidalgoGadea.com +MIT License Copyright (c) 2022 GuillermoHidalgoGadea.com Sourcecode: https://github.com/Guillermo-Hidalgo-Gadea/VideoPyToolbox ==================================================================================================== @@ -32,7 +32,7 @@ def trim_split_h265(): splitlist = [os.path.basename(filename) for filename in splitlist] # placeholders - output = ['trim_' + filename for filename in splitlist] + output = ['trim_' + filename.split('.')[0] + '.mp4' for filename in splitlist] start = ['00:00:00.000'] * len(splitlist) end = ['00:00:00.000'] * len(splitlist) @@ -71,8 +71,9 @@ def trim_split_h265(): open_file(filename) second_check = input('Is the format correct? [y/n] ') if "n" in second_check: - return + splitlist = [] else: + crf = input("Choose constant rate factor between 0-50: ") or 0 starttime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") # loop over cases in file for case in range(len(splitlist)): @@ -84,12 +85,12 @@ def trim_split_h265(): end = row[3] # split with re-encoding - ffmpeg_command = f"ffmpeg -y -i {original} -ss {start} -to {end} -vcodec libx265 -crf 0 {output}" + ffmpeg_command = f"ffmpeg -y -i {original} -ss {start} -to {end} -vcodec libx265 -crf {crf} {output}" os.system(ffmpeg_command) endtime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") # add instructions - header = f"# started: {starttime} ended: {endtime}, re-encoded libx265 crf=0\n" + header = f"# started: {starttime} ended: {endtime}, re-encoded libx265 crf={crf}\n" with open(filename, "r+") as textfile: original = textfile.read() textfile.seek(0) @@ -146,7 +147,7 @@ def trim_split_hevc_nvenc(): open_file(filename) second_check = input('Is the format correct? [y/n] ') if "n" in second_check: - return + splitlist = [] else: starttime = datetime.now().strftime("%Y_%m_%d-%I-%M-%S") # loop over cases in file