In this tutorial, we’ll create a desktop video converter using Python, Tkinter, and FFmpeg. This app can compress videos while keeping quality, track progress, and even estimate output file size in real-time. Perfect for beginners who want to learn Python GUI programming and video processing!
We’ll break this into small sections with clear explanations.
- Setup: Install Required Libraries
We need a few Python libraries:
pip install ttkbootstrap
ttkbootstrap – gives a modern, themed look to Tkinter apps.
tkinter – built-in Python library for GUI.
subprocess – used to run FFmpeg commands.
Also, download FFmpeg from ffmpeg.org
and note its path (e.g., C:\ffmpeg\bin\ffmpeg.exe).
- Basic Imports and Config
Start by importing libraries and setting the path to FFmpeg:
import sys
import os
import subprocess
import threading
from pathlib import Path
import re
import tkinter as tk
from tkinter import filedialog, messagebox
import ttkbootstrap as tb
# Path to FFmpeg executable
FFMPEG_PATH = r"C:\ffmpeg\bin\ffmpeg.exe"
We’ll also define some helper functions for cross-platform compatibility.
- FFmpeg Utility Functions
We need functions to hide FFmpeg windows on Windows, check if FFmpeg exists, and read video duration:
def hidden_ffmpeg_startupinfo():
if sys.platform.startswith("win"):
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
si.wShowWindow = subprocess.SW_HIDE
return si
return None
def check_ffmpeg():
if not os.path.exists(FFMPEG_PATH):
messagebox.showerror("FFmpeg Missing", f"FFmpeg not found:\n{FFMPEG_PATH}")
return False
return True
def get_video_duration(input_file):
try:
result = subprocess.run(
[FFMPEG_PATH, "-i", input_file],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
universal_newlines=True,
startupinfo=hidden_ffmpeg_startupinfo()
)
m = re.search(r"Duration: (\d+):(\d+):(\d+\.?\d*)", result.stderr)
if m:
h, m_, s = int(m.group(1)), int(m.group(2)), float(m.group(3))
return h * 3600 + m_ * 60 + s
except:
pass
return None
✅ Explanation:
hidden_ffmpeg_startupinfo() hides the console window when running FFmpeg.
check_ffmpeg() ensures FFmpeg exists.
get_video_duration() parses the video duration using FFmpeg’s output.
- Video Compression Helpers
We can calculate CRF for quality compression and estimated file size:
def estimate_crf(reduction):
return int(23 + (reduction / 100) * 12)
def update_estimated_size(video_path, reduction):
if not video_path:
return "Estimated size: —"
size = os.path.getsize(video_path)
est = size * (1 - reduction / 100)
return f"Estimated size: {est/1024/1024:.2f} MB"
✅ Explanation:
estimate_crf() calculates a CRF value based on your reduction percentage.
update_estimated_size() previews the compressed file size without running FFmpeg.
- Creating the Tkinter GUI
We’ll start building the GUI window:
app = tk.Tk()
style_obj = tb.Style(theme="superhero")
app.title("VID Converter + Smart Compression")
app.geometry("800x560")
ttkbootstrap.Style gives a modern theme.
Set title and size of the app window.
5a. Input and Output Selection
Add input/output file selection:
video_path = tk.StringVar()
output_path = tk.StringVar()
def select_video():
path = filedialog.askopenfilename(filetypes=[("Video Files", "*.mp4 *.avi *.mov *.mkv")])
if path:
video_path.set(path)
def select_output():
path = filedialog.asksaveasfilename(defaultextension=".mp4", filetypes=[("MP4", "*.mp4"), ("MKV", "*.mkv")])
if path:
output_path.set(path)
# GUI
input_frame = tb.Labelframe(app, text="Video Input", padding=10)
input_frame.pack(fill="x", padx=10, pady=5)
tb.Entry(input_frame, textvariable=video_path).pack(side="left", fill="x", expand=True, padx=5)
tb.Button(input_frame, text="Browse", command=select_video).pack(side="left")
output_frame = tb.Labelframe(app, text="Output File", padding=10)
output_frame.pack(fill="x", padx=10, pady=5)
tb.Entry(output_frame, textvariable=output_path).pack(side="left", fill="x", expand=True, padx=5)
tb.Button(output_frame, text="Browse", command=select_output).pack(side="left")
✅ Explanation:
video_path and output_path store file paths.
Buttons open file dialogs to select input and output files.
5b. File Size Reduction Slider
Add a slider for reduction percentage:
reduce_var = tk.IntVar(value=20)
estimated_size_var = tk.StringVar(value="Estimated size: —")
def update_slider(val):
estimated_size_var.set(update_estimated_size(video_path.get(), int(val)))
size_frame = tb.Labelframe(app, text="Reduce File Size", padding=10)
size_frame.pack(fill="x", padx=10, pady=5)
tb.Scale(size_frame, from_=5, to=80, orient="horizontal",
variable=reduce_var, command=update_slider).pack(fill="x", padx=10)
tb.Label(size_frame, textvariable=estimated_size_var).pack(anchor="w", pady=3)
✅ Explanation:
Slider ranges from 5–80% reduction.
Updates estimated size in real-time.
- Start Video Conversion
We can run FFmpeg in a background thread to avoid freezing the UI:
def run_ffmpeg():
if not check_ffmpeg():
return
if not video_path.get() or not output_path.get():
messagebox.showerror("Error", "Select input and output files.")
return
duration = get_video_duration(video_path.get())
reduction = reduce_var.get()
cmd = [
FFMPEG_PATH, "-i", video_path.get(),
"-c:v", "libx264",
"-preset", "fast",
"-crf", str(estimate_crf(reduction)),
"-c:a", "aac",
"-b:a", "128k",
output_path.get()
]
subprocess.run(cmd, startupinfo=hidden_ffmpeg_startupinfo())
messagebox.showinfo("Done", "Conversion finished!")
def start_conversion():
threading.Thread(target=run_ffmpeg, daemon=True).start()
✅ Explanation:
Runs FFmpeg without blocking the GUI.
Uses crf for quality-based compression.
6a. Add Start Button
start_btn = tb.Button(app, text="Start ▶", bootstyle="success", command=start_conversion)
start_btn.pack(pady=10)
Clicking this starts conversion in the background.
- Final Touches
You can add a progress bar, stop button, and “About” info to make it more user-friendly. Then run the main loop:
app.mainloop()
- Result
Now you have a fully functional offline video converter!
Supports MP4, AVI, MOV, MKV
Smart compression (Target Size / Quality)
Live estimated size
Simple modern GUI
You can clone the full project here:
https://github.com/rogers-cyber/VIDConverter
✅ Tips for Beginners
Experiment with the CRF value to balance size vs quality.
Use threading to avoid freezing GUI during long conversions.
Explore ttkbootstrap themes to customize UI appearance.

Top comments (0)