Folks, Ive worked hard to build this tested script, and Ill help the best I can if you are trying to adapt it... but no promises implied or expressed.
Short story: DOS Batch is all I ever used, and when it came to something more advanced than what (limited) DOS could provide, I had to bang out an executable in QB or VB.NET to do a special task, and even then it as a mix match of code and script... I got tired of all this and discovered Python can... do everything I want to do and no longer, EVER, use DOS again.
Below is the current script, lets call it "Version 1.0" which I use for personal application and is tailored to me. Feel free to play with it as you wish, but I cant baby step you if you know nothing about FFMPEG, command line arguments *or* Python.
I do hope this helps, if anyone else wants to chime in and help anyone that has questions, feel free to..
This project will *not* be going to github or other sites, I do not have time for all that.
Use of my script is at your own risk. Developed and running on Windows 10 and Python 3.11 (no other modules are add-ins used)
Enjoy-
Andrew
Code:""" Encode BD Archive to Media Player MP4 with Loudnorm Version 1.0 """ # import required module from pathlib import Path import os import subprocess from os import system import json #setup some colors CEND = '\33[0m' CRED = '\33[31m' CYELLOW = '\33[33m' CBOLD = '\33[1m' CITALIC = '\33[3m' #setup some variables crlf = "\r\n" infile = "" outfile = "" os.system('cls') print("Encode BD Archive to Media Player MP4 with Loudnorm") print(crlf) #initial setup pathhere = str((os.path.dirname(os.path.realpath(__file__)))) sourcedirectory = "H:\BD-HEVC_ARCHIVE" destdirectory = "W:\\NEW_VIDEOS\\BD-HEVC-HB" pattern = "*.mkv" #cleanup if os.path.exists(pathhere + "\encodethis.mkv"): os.remove(pathhere + "\encodethis.mkv") if os.path.exists(pathhere + "\loudnorm.txt"): os.remove(pathhere + "\loudnorm.txt") # Python does not like trailing \ in string, so make sure the # directories are defined without one, but is needed so it is added sourcedirectory = sourcedirectory + chr(92) destdirectory = destdirectory + chr(92) print("The source directory is: ", sourcedirectory) print("The destination directory is: ", destdirectory) print("The file pattern is:", pattern) print(crlf) print(CITALIC + CBOLD + "The following source files were discovered:" + CEND) files = Path(sourcedirectory).glob(pattern) for file in files: print(file) print(crlf) files = Path(sourcedirectory).glob(pattern) for file in files: # print(file) # print(file.name) # print(file.suffix) # justfile = file.name.replace(file.suffix, "") # print(justfile) infile = (str(file)) # outfile = destdirectory + str(file.name.replace(file.suffix, "")) + "-BD.MKV" outfile = pathhere + "\encodethis.mkv" runstring = '!!PATHHERE!!\handbrakecli --input "!!INFILE!!" --output "!!OUTFILE!!" --encoder nvenc_h265 --quality 31 --crop 0:0:0:0 --non-anamorphic --aencoder eac3 --ab 448k --mixdown 5point1 ' # build out run string runstring = runstring.replace("!!INFILE!!", str(infile)) runstring = runstring.replace("!!OUTFILE!!", str(outfile)) runstring = runstring.replace("!!PATHHERE!!", pathhere) #print(runstring) #print(crlf) print(crlf) print(CRED + "Step 1/3) Media Player Encoding " + CYELLOW + str(infile) + " to " + str(outfile) + CEND) system("title " + "Step 1/3) Media Player Encoding [" + str(infile) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) if os.path.exists(pathhere + "\encodethis.mkv"): os.remove(pathhere + "\encodethis.mkv") subprocess.call(runstring,shell=True) #The base file is encoded with the desired video, lets run FFMPEG on it and determine the LOUDNORM settings if os.path.exists(pathhere + "\loudnorm.txt"): os.remove(pathhere + "\loudnorm.txt") runstring = '!!PATHHERE!!\\ffmpeg -hide_banner -i "!!PATHHERE!!\encodethis.mkv" -nostats -af "loudnorm=print_format=json" -f null nul 2> "!!PATHHERE!!\loudnorm.txt"' runstring = runstring.replace("!!PATHHERE!!", pathhere) print(crlf) print(CRED + "Step 2/3) Reading LOUDNORM... " + CYELLOW + str(infile) + " to " + str(outfile) + CEND) system("title " + "Step 2/3) Reading LOUDNORM [" + str(infile) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) subprocess.call(runstring,shell=True) #read JSON loudnorm.txt #open text file in read mode text_file = open(pathhere + "\\loudnorm.txt", "r") #read whole file to a string data = text_file.read() #close file text_file.close() #remove all but JSON text before, sep, after = data.partition('{') data = "{" + after #parse print(crlf) print("Parsing Data: " + data) print(crlf) y = json.loads(data) input_i = y["input_i"] input_tp = y["input_tp"] input_lra = y["input_lra"] input_thresh = y["input_thresh"] #print(input_i) #print(input_tp) #print(input_lra) #print(input_thresh) outfile = destdirectory + str(file.name.replace(file.suffix, "")) + "-HEVC.MP4" runstring = '!!PATHHERE!!\\ffmpeg -hide_banner -fflags +genpts -i "!!PATHHERE!!\\encodethis.mkv" -filter:a "loudnorm=linear=true:i=-23.0:lra=7.0:tp=-2.0:offset=0.39:measured_I=!!input_i!!:measured_tp=!!input_tp!!:measured_LRA=!!input_lra!!:measured_thresh=!!input_thresh!!" -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v copy "!!OUTFILE!!"' runstring = runstring.replace("!!input_i!!", input_i) runstring = runstring.replace("!!input_tp!!", input_tp) runstring = runstring.replace("!!input_lra!!", input_lra) runstring = runstring.replace("!!input_thresh!!", input_thresh) runstring = runstring.replace("!!OUTFILE!!", str(outfile)) runstring = runstring.replace("!!PATHHERE!!", pathhere) print(crlf) print(CRED + "Step 3/3) Applying LOUDNORM... " + CYELLOW + str(infile) + " to " + str(outfile) + CEND) system("title " + "Step 3/3) Applying LOUDNORM [" + str(infile) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) subprocess.call(runstring,shell=True) #cleanup if os.path.exists(pathhere + "\encodethis.mkv"): os.remove(pathhere + "\encodethis.mkv") if os.path.exists(pathhere + "\loudnorm.txt"): os.remove(pathhere + "\loudnorm.txt") #pause input("Press Enter to continue...")
+ Reply to Thread
Results 1 to 10 of 10
-
Last edited by RogerTango; 27th Dec 2022 at 17:38.
-
Version 1.1
Added:
filename added to title metadata, some programs & DLNA software use it
date and time added to comment metadata, to track when a file was encoded
Code:""" Encode BD Archive to Media Player MP4 with Loudnorm Version 1.1 """ # import required module from pathlib import Path import os import subprocess from os import system import json from datetime import datetime #setup some colors CEND = '\33[0m' CRED = '\33[31m' CYELLOW = '\33[33m' CBOLD = '\33[1m' CITALIC = '\33[3m' #setup some variables crlf = "\r\n" infile = "" outfile = "" os.system('cls') print("Encode BD Archive to Media Player MP4 with Loudnorm") print(crlf) #initial setup pathhere = str((os.path.dirname(os.path.realpath(__file__)))) #sourcedirectory = "Y:\\Test-BD_MKV_TRIMMED" sourcedirectory = "H:\\BD-HEVC_ARCHIVE" destdirectory = "W:\\NEW_VIDEOS\\BD-HEVC" pattern = "*.mkv" #cleanup if os.path.exists(pathhere + "\encodethis.mkv"): os.remove(pathhere + "\encodethis.mkv") if os.path.exists(pathhere + "\loudnorm.txt"): os.remove(pathhere + "\loudnorm.txt") # Python does not like trailing \ in string, so make sure the # directories are defined without one, but is needed so it is added sourcedirectory = sourcedirectory + chr(92) + chr(92) destdirectory = destdirectory + chr(92) + chr(92) print("The source directory is: ", sourcedirectory) print("The destination directory is: ", destdirectory) print("The file pattern is:", pattern) print(crlf) print(CITALIC + CBOLD + "The following source files were discovered:" + CEND) files = Path(sourcedirectory).glob(pattern) for file in files: print(file) print(crlf) files = Path(sourcedirectory).glob(pattern) for file in files: # print(file) # print(file.name) # print(file.suffix) justfile = file.name.replace(file.suffix, "") # print(justfile) infile = (str(file)) # outfile = destdirectory + str(file.name.replace(file.suffix, "")) + "-BD.MKV" outfile = pathhere + "\encodethis.mkv" runstring = '!!PATHHERE!!\handbrakecli --input "!!INFILE!!" --output "!!OUTFILE!!" --encoder nvenc_h265 --quality 30 --crop 0:0:0:0 --non-anamorphic --aencoder eac3 --ab 448k --mixdown 5point1 ' # build out run string runstring = runstring.replace("!!INFILE!!", str(infile)) runstring = runstring.replace("!!OUTFILE!!", str(outfile)) runstring = runstring.replace("!!PATHHERE!!", pathhere) #print(runstring) #print(crlf) print(crlf) print(CRED + "Step 1/3) Media Player Encoding " + CYELLOW + str(infile) + " to " + str(outfile) + CEND) system("title " + "Step 1/3) Media Player Encoding [" + str(infile) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) if os.path.exists(pathhere + "\encodethis.mkv"): os.remove(pathhere + "\encodethis.mkv") subprocess.call(runstring,shell=True) #The base file is encoded with the desired video, lets run FFMPEG on it and determine the LOUDNORM settings if os.path.exists(pathhere + "\loudnorm.txt"): os.remove(pathhere + "\loudnorm.txt") runstring = '!!PATHHERE!!\\ffmpeg -hide_banner -i "!!PATHHERE!!\encodethis.mkv" -nostats -af "loudnorm=print_format=json" -f null nul 2> "!!PATHHERE!!\loudnorm.txt"' runstring = runstring.replace("!!PATHHERE!!", pathhere) print(crlf) print(CRED + "Step 2/3) Reading LOUDNORM... " + CYELLOW + str(infile) + " to " + str(outfile) + CEND) system("title " + "Step 2/3) Reading LOUDNORM [" + str(infile) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) subprocess.call(runstring,shell=True) #read JSON loudnorm.txt #open text file in read mode text_file = open(pathhere + "\\loudnorm.txt", "r") #read whole file to a string data = text_file.read() #close file text_file.close() #remove all but JSON text before, sep, after = data.partition('{') data = "{" + after #parse print(crlf) print("Parsing Data: " + data) print(crlf) y = json.loads(data) input_i = y["input_i"] input_tp = y["input_tp"] input_lra = y["input_lra"] input_thresh = y["input_thresh"] #print(input_i) #print(input_tp) #print(input_lra) #print(input_thresh) #Get current date and time time_now = datetime.now() current_time = time_now.strftime("%H:%M:%S") #print("The current time is", current_time) current_date = time_now.strftime("%m-%d-%Y") #print("The current date is", current_date) current_date_time = current_date + " " + current_time outfile = destdirectory + str(file.name.replace(file.suffix, "")) + "-HEVC.MP4" runstring = '!!PATHHERE!!\\ffmpeg -hide_banner -fflags +genpts -i "!!PATHHERE!!\\encodethis.mkv" -metadata title="!!JUSTFILE!!-HEVC" -metadata comment="!!CURRENT_DATE_TIME!!" -filter:a "loudnorm=linear=true:i=-23.0:lra=7.0:tp=-2.0:offset=0.39:measured_I=!!input_i!!:measured_tp=!!input_tp!!:measured_LRA=!!input_lra!!:measured_thresh=!!input_thresh!!" -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v copy "!!OUTFILE!!"' runstring = runstring.replace("!!input_i!!", input_i) runstring = runstring.replace("!!input_tp!!", input_tp) runstring = runstring.replace("!!input_lra!!", input_lra) runstring = runstring.replace("!!input_thresh!!", input_thresh) runstring = runstring.replace("!!OUTFILE!!", str(outfile)) runstring = runstring.replace("!!PATHHERE!!", pathhere) runstring = runstring.replace("!!JUSTFILE!!", justfile) runstring = runstring.replace("!!CURRENT_DATE_TIME!!", current_date_time) print(crlf) print(CRED + "Step 3/3) Applying LOUDNORM... " + CYELLOW + str(infile) + " to " + str(outfile) + CEND) system("title " + "Step 3/3) Applying LOUDNORM [" + str(infile) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) subprocess.call(runstring,shell=True) #cleanup if os.path.exists(pathhere + "\encodethis.mkv"): os.remove(pathhere + "\encodethis.mkv") if os.path.exists(pathhere + "\loudnorm.txt"): os.remove(pathhere + "\loudnorm.txt") #pause input("Press Enter to continue...")
-
I agree, definitely automatizing works better,
I simplified and cleared your version for better reading:
-paths are manipulated as Path objects
-ffmpeg does not need to create "loudnorm.txt", text is just directly passed in Python from ffmpeg
-last cmd was hanging for me, but f used subprocess.Popen instead subprocess.call, it was working, but I left it there as you wrote it using subprocess.call
Code:""" Encode BD Archive to Media Player MP4 with Loudnorm Version 1.1 """ # import required module from pathlib import Path import os import subprocess import json from datetime import datetime print("Encode BD Archive to Media Player MP4 with Loudnorm") #initial setup pathhere = Path(__file__).parent sourcedirectory = Path("H:\\BD-HEVC_ARCHIVE") destdirectory = Path("W:\\NEW_VIDEOS\\BD-HEVC") pattern = "*.mkv" outfile_handbrake = pathhere / "encodethis.mkv" outfile_final = lambda file: destdirectory / f"{file.stem}-HEVC.MP4" handbrake = pathhere / "handbrakecli" ffmpeg = pathhere / "ffmpeg" files = list(sourcedirectory.glob(pattern)) for file in files: print(file) for file in files: outfile_handbrake.unlink(missing_ok=True) #delete file if exists ##HANDBRAKE CMD runstring = f'"{handbrake}" --input "{file}" --output "{outfile_handbrake}"'\ f' --encoder nvenc_h265 --quality 30 --crop 0:0:0:0 --non-anamorphic --aencoder eac3 --ab 448k --mixdown 5point1 ' subprocess.call(runstring,shell=True) ##FFMPEG CMD runstring = f'"{ffmpeg}" -hide_banner -i "{outfile_handbrake}"'\ f' -nostats -af "loudnorm=print_format=json" -f null null' p = subprocess.Popen(runstring, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() #ffmpeg writes into stderr #print(stderr.decode()) #remove all but JSON text before, sep, after = stderr.decode().partition('{') data = "{" + after y = json.loads(data) time_now = datetime.now() current_time = time_now.strftime("%H:%M:%S") current_date = time_now.strftime("%m-%d-%Y") current_date_time = current_date + " " + current_time ##FFMPEG CMD runstring = f'"{ffmpeg}" -hide_banner -fflags +genpts -i "{outfile_handbrake}"'\ f' -metadata title="{file.stem}-HEVC" -metadata comment="{current_date_time}"'\ f' -filter:a "loudnorm=linear=true:i=-23.0:lra=7.0:tp=-2.0:offset=0.39:'\ f'measured_I={y["input_i"]}:measured_tp={y["input_tp"]}:measured_LRA={y["input_lra"]}:measured_thresh={y["input_thresh"]}"'\ f' -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v copy "{outfile_final(file)}"' subprocess.call(runstring,shell=True) #cleanup outfile_handbrake.unlink(missing_ok=True)
Code:""" Encode BD Archive to Media Player MP4 with Loudnorm Version 1.1 """ # import required module from pathlib import Path import os import subprocess from os import system import json from datetime import datetime #setup some colors CEND = '\33[0m' CRED = '\33[31m' CYELLOW = '\33[33m' CBOLD = '\33[1m' CITALIC = '\33[3m' #setup some variables crlf = "\r\n" os.system('cls') print("Encode BD Archive to Media Player MP4 with Loudnorm") print(crlf) #initial setup pathhere = Path(__file__).parent sourcedirectory = Path("H:\\BD-HEVC_ARCHIVE") destdirectory = Path("W:\\NEW_VIDEOS\\BD-HEVC") pattern = "*.mkv" outfile_handbrake = pathhere / "encodethis.mkv" outfile_final = lambda file: destdirectory / f"{file.stem}-HEVC.MP4" handbrake = pathhere / "handbrakecli" ffmpeg = pathhere / "ffmpeg" print("The source directory is: ", sourcedirectory) print("The destination directory is: ", destdirectory) print("The file pattern is:", pattern) print(crlf) print(CITALIC + CBOLD + "The following source files were discovered:" + CEND) files = list(sourcedirectory.glob(pattern)) for file in files: print(file) print(crlf) for file in files: outfile_handbrake.unlink(missing_ok=True) runstring = f'"{handbrake}" --input "{file}" --output "{outfile_handbrake}"'\ f' --encoder nvenc_h265 --quality 30 --crop 0:0:0:0 --non-anamorphic --aencoder eac3 --ab 448k --mixdown 5point1 ' print(crlf) print(CRED + "Step 1/3) Media Player Encoding " + CYELLOW + str(file) + " to " + str(outfile) + CEND) system("title " + "Step 1/3) Media Player Encoding [" + str(file) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) subprocess.call(runstring,shell=True) #The base file is encoded with the desired video, lets run FFMPEG on it and determine the LOUDNORM settings runstring = f'"{ffmpeg}" -hide_banner -i "{outfile_handbrake}"'\ f' -nostats -af "loudnorm=print_format=json" -f null null' print(crlf) print(CRED + "Step 2/3) Reading LOUDNORM... " + CYELLOW + str(file) + " to " + str(outfile) + CEND) system("title " + "Step 2/3) Reading LOUDNORM [" + str(file) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) p = subprocess.Popen(runstring, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() #ffmpeg writes into stderr #print(stderr.decode()) #remove all but JSON text before, sep, after = stderr.decode().partition('{') data = "{" + after #parse print(crlf) print("Parsing Data: " + data) print(crlf) y = json.loads(data) input_i = y["input_i"] input_tp = y["input_tp"] input_lra = y["input_lra"] input_thresh = y["input_thresh"] #print(input_i) #print(input_tp) #print(input_lra) #print(input_thresh) #Get current date and time time_now = datetime.now() current_time = time_now.strftime("%H:%M:%S") #print("The current time is", current_time) current_date = time_now.strftime("%m-%d-%Y") #print("The current date is", current_date) current_date_time = current_date + " " + current_time runstring = f'"{ffmpeg}" -hide_banner -fflags +genpts -i "{outfile_handbrake}"'\ f' -metadata title="{file.stem}-HEVC" -metadata comment="{current_date_time}"'\ f' -filter:a "loudnorm=linear=true:i=-23.0:lra=7.0:tp=-2.0:offset=0.39:'\ f'measured_I={input_i}:measured_tp={input_tp}:measured_LRA={input_lra}:measured_thresh={input_thresh}"'\ f' -c:a aac -ar 48000 -b:a 192k -ac 2 -c:v copy "{outfile_final(file)}"' print(crlf) print(CRED + "Step 3/3) Applying LOUDNORM... " + CYELLOW + str(file) + " to " + str(outfile) + CEND) system("title " + "Step 3/3) Applying LOUDNORM [" + str(file) + "] to [" + str(outfile) + "]") print(CRED + "Using: " + CEND + CITALIC + runstring + CEND) print(crlf) subprocess.call(runstring,shell=True) #cleanup outfile_handbrake.unlink(missing_ok=True) #pause input("Press Enter to continue...")
Last edited by _Al_; 28th Dec 2022 at 10:50.
-
Wow, Al! I am not the only Phython person here!
Nice work, I didn't know how to capture stdout and parse it, you taught me a few things! I may have some questions for you later..
Thanks sir!
Andrew -
Al,
Ive been reading your code and there is a lot I still have to learn, one thing is Python is way more powerful than I first thought!!
I have a question, I did read up on lamda but I cannot grasp the concept. Could you put in layman's terms what you did here?
Code:outfile_final = lambda file: destdirectory / f"{file.stem}-HEVC.MP4"
Andrew -
lambda is anonymous function, it is a function basically
Code:outfile_final = lambda file: destdirectory / f"{file.stem}-HEVC.MP4" path = outfile_final(file)
Code:def outfile_final(file): return destdirectory / f"{file.stem}-HEVC.MP4" path = outfile_final(file)
-
Also that f" ....." is called f-string, introduced in some python3 version, very powerful also, if you google for it.
If using it , like f"....{variable} ....", it just executes whatever is inside of those complex brackets and looks what is a __str__ representation for that object class, that is what I think, it substitutes that object to a string and pastes that string in that whole string.
It is much faster than: something + " " + other_something
Better: f"{something} {other_something}"
So if you have a pathlib Path object included in those f-strings, it gets turned into a string, no problem
Code:sourcedirectory = Path("H:\\BD-HEVC_ARCHIVE") string = f'"{sourcedirectory}"'
Using pathlib clears the code immensely.Last edited by _Al_; 28th Dec 2022 at 18:41.
-
Similar Threads
-
How to get the widevine pssh from init mp4 JUST using python script?
By ancientbanana in forum Video Streaming DownloadingReplies: 14Last Post: 20th Nov 2024, 11:57 -
2-pass AVC H.264 encoding = 5% bitrate improvement over 1-pass?
By pxstein in forum Video ConversionReplies: 7Last Post: 25th Jan 2022, 10:34 -
Best program for MKV to MP4 or just encoding the audio?
By kkiller23 in forum Newbie / General discussionsReplies: 7Last Post: 28th Oct 2019, 16:08 -
Create 2-pass videos using ffmpeg & python
By olpdog in forum EditingReplies: 0Last Post: 29th Jun 2019, 21:16 -
What is difference between 1 pass encoding & 2 pass encoding
By Computerman1 in forum Video ConversionReplies: 18Last Post: 13th Jan 2019, 19:59