#! /usr/bin/env nix-shell #! nix-shell -i python3 -p python3 # Imports import os import math # Function for calculating the appropriate bitrate to use during conversion def get_bitrate(duration, filesize, audio_br): br = math.floor(filesize / duration - audio_br) return br, br * 0.50, br * 1.45 def encode(ffmpeg_string, output_name, fs): os.system(ffmpeg_string) end_size = ( os.path.getsize( "/dev/shm/ffmpeg/out/{output_name}".format(output_name=output_name) ) * 0.00000095367432 ) if end_size < fs: print( ffmpeg_string.replace("\t", "") + "\nThe FFMPEG string above has yielded a file whose size is " + str(end_size) + "MB.\n{output_name} is ready for Discord.\n".format( output_name=output_name ) ) return False else: print( ffmpeg_string.replace("\t", "") + "\nThe FFMPEG string above has yielded a file whose size is " + str(end_size) + "MB.\n{output_name} is NOT ready for Discord, and will be re-run.\nMy bad.".format( output_name=output_name ) ) return True def time_calculations(fname, length): startstring = fname[0:2] + ":" + fname[2:4] + ":" + fname[4:6] endstring = fname[7:9] + ":" + fname[9:11] + ":" + fname[11:13] try: int(fname[0:6]) startseconds = ( int(fname[0:2]) * 60 * 60 + int(fname[2:4]) * 60 + int(fname[4:6]) ) try: int(fname[11:13]) endseconds = ( int(fname[7:9]) * 60 * 60 + int(fname[9:11]) * 60 + int(fname[11:13]) ) duration = endseconds - startseconds timestamped_section = f"-ss {startstring} -to {endstring}" except: duration = length - startseconds timestamped_section = f"-ss {startstring}" except: duration = length timestamped_section = "" return duration, timestamped_section fname = os.listdir("/dev/shm/ffmpeg/in/")[0] os.rename("/dev/shm/ffmpeg/in/" + fname, "/dev/shm/ffmpeg/in/" + fname.replace(" ", "")) fname = fname.replace(" ", "") # ffprobe to calculate the total duration of the clip. length = math.floor( float( os.popen( "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 /dev/shm/ffmpeg/in/{fname}".format( fname=fname ) ).read() ) ) duration, timestamped_section = time_calculations(fname, length) run = True reso = os.getenv("reso") codec = os.getenv("codec") audio_br = os.getenv("audio_br") audio_br = int(str(os.getenv("audio_br"))) fs = float(str(os.getenv("fs"))) target_fs = fs codecs = { "vp9": { "pass1": f"-vf scale={reso} -g 240 -threads 8 -speed 4 -row-mt 1 -tile-columns 2 -vsync cfr -c:v libvpx-vp9 -pass 1 -an", "pass2": f"-vf scale={reso} -g 240 -threads 8 -speed 2 -row-mt 1 -tile-columns 2 -c:v libvpx-vp9 -c:a libopus -pass 2", "output_name": "small_" + fname.replace(".mp4", ".webm"), }, "x264": { "pass1": f"-vf scale={reso} -vsync cfr -c:v libx264 -pass 1 -an", "pass2": f"-vf scale={reso} -c:v libx264 -c:a aac -pass 2 ", "output_name": "small_" + fname, }, "x265": { "pass1": f"-vf scale={reso} -c:v libx265 -vsync cfr -x265-params pass=1 -an", "pass2": f"-vf scale={reso} -c:v libx265 -x265-params pass=2 -c:a aac", "output_name": "small_" + fname, }, } while run: # Conversion to KiB end_fs = fs * 8192 br, minbr, maxbr = get_bitrate( duration=duration, filesize=end_fs, audio_br=audio_br ) ffmpeg_string = f""" ffpb {timestamped_section} -hwaccel cuda -i /dev/shm/ffmpeg/in/{fname} -y \ {codecs[str(codec)]['pass1']} \ -b:v {br}k -minrate {minbr}k -maxrate {maxbr}k \ -f null /dev/null && \ ffpb {timestamped_section} -hwaccel cuda -i /dev/shm/ffmpeg/in/{fname} \ {codecs[str(codec)]['pass2']} \ -b:a {audio_br}k -b:v {br}k -minrate {minbr}k -maxrate {maxbr}k \ /dev/shm/ffmpeg/out/{codecs[str(codec)]['output_name']} -y """ run = encode( ffmpeg_string, output_name=codecs[str(codec)]["output_name"], fs=target_fs ) if run: fs = fs - 0.2