scripts/nix/ffmpeg4discord.py

139 lines
4.3 KiB
Python

#! /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