Skip to content

Instantly share code, notes, and snippets.

@thegamecracks
Created January 15, 2026 13:52
Show Gist options
  • Select an option

  • Save thegamecracks/990c88c40e7393f7800b310f8d167599 to your computer and use it in GitHub Desktop.

Select an option

Save thegamecracks/990c88c40e7393f7800b310f8d167599 to your computer and use it in GitHub Desktop.
Calculate video bitrate to achieve a given filesize and duration
"""Calculate the video bitrate for video compression software
to achieve a given filesize and duration.
Intended for use with HandBrake: https://handbrake.fr/
Result bitrate should be copied to your constant/average bitrate.
Note that input filesize is in megabytes, not mebibytes.
"""
import re
import sys
from tkinter import Misc, StringVar, Tk
from tkinter.ttk import Entry, Frame, Label, LabelFrame, Spinbox
def main() -> None:
def refresh_output() -> None:
try:
filesize = float(video_filesize.var.get()) * 8000
duration = parse_video_duration(video_length.var.get())
audio = float(audio_bitrate.var.get())
except ValueError:
return output_bitrate.var.set("")
output_bitrate_num = (filesize - audio * duration) / duration
if output_bitrate_num <= 0:
return output_bitrate.var.set("Video too long")
output_bitrate.var.set(str(int(output_bitrate_num)))
enable_windows_dpi_awareness()
app = Tk()
app.title("HandBrake Video Bitrate Calculator")
app.geometry("854x200")
content = Frame(app, padding=10)
content.pack(expand=True, fill="both")
content.grid_columnconfigure(0, weight=3)
content.grid_columnconfigure(1, weight=1)
content.grid_rowconfigure(0, weight=1)
inputs = Frame(content)
inputs.grid(row=0, column=0, sticky="nesw")
inputs.grid_columnconfigure(0, weight=1)
inputs.grid_rowconfigure(0, weight=3)
inputs.grid_rowconfigure(1, weight=1)
outputs = Frame(content)
outputs.grid(row=0, column=1, sticky="nesw")
outputs.grid_anchor("center")
video = LabelFrame(inputs, text="Video", padding=10)
video.grid(row=0, column=0, sticky="nesw")
video.grid_anchor("center")
video.grid_columnconfigure(0, weight=1)
video_filesize = LabelSpinbox(video, label="Desired filesize (MB):", value="50")
video_filesize.grid(sticky="ew")
video_length = LabelEntry(video, label="Length (mm:ss):", value="3:00")
video_length.grid(sticky="ew")
audio = LabelFrame(inputs, text="Audio", padding=10)
audio.grid(row=1, column=0, sticky="nesw")
audio.grid_anchor("center")
audio.grid_columnconfigure(0, weight=1)
audio_bitrate = LabelSpinbox(audio, label="Bitrate (kbps):", value="96")
audio_bitrate.grid(sticky="ew")
output_bitrate = LabelEntry(outputs, label="Result bitrate (kbps):")
output_bitrate.grid(sticky="ew")
video_filesize.var.trace_add("write", lambda *_: refresh_output())
video_length.var.trace_add("write", lambda *_: refresh_output())
audio_bitrate.var.trace_add("write", lambda *_: refresh_output())
refresh_output()
app.mainloop()
def parse_video_duration(s: str) -> int:
s = s.strip()
m = re.match(r"(?:(\d+):)?(?:(\d+):)?(\d+)", s)
if m is None:
raise ValueError(f"Invalid duration: {s}")
hours = int(m[1] or 0)
minutes = int(m[2] or 0)
seconds = int(m[3] or 0)
if m[1] is not None and m[2] is None:
hours, minutes = 0, hours
return hours * 3600 + minutes * 60 + seconds
def enable_windows_dpi_awareness() -> None:
if sys.platform == "win32":
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(2)
class LabelEntry(Frame):
def __init__(self, parent: Misc, *, label: str, value: str = "") -> None:
super().__init__(parent)
self.parent = parent
self.grid_columnconfigure(1, weight=1)
self.label = Label(self, text=label)
self.label.grid(row=0, column=0, sticky="w")
self.var = StringVar(self, value=value)
self.entry = Entry(self, textvariable=self.var)
self.entry.grid(row=0, column=1, sticky="ew", padx=(5, 0))
class LabelSpinbox(Frame):
def __init__(self, parent: Misc, *, label: str, value: str = "", **kwargs) -> None:
super().__init__(parent)
self.parent = parent
self.grid_columnconfigure(1, weight=1)
self.label = Label(self, text=label)
self.label.grid(row=0, column=0, sticky="w")
self.var = StringVar(self, value=value)
self.entry = Spinbox(self, from_=0, to=9999, textvariable=self.var)
self.entry.grid(row=0, column=1, sticky="ew", padx=(5, 0))
if __name__ == "__main__":
main()
@thegamecracks
Copy link
Author

Windowed application containing fields for desired filesize, length, audio bitrate, and resulting video bitrate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment