DEV Community

Mate Technologies
Mate Technologies

Posted on

QuickVideo – Batch Change Video Speed with Python and FFmpeg

In this tutorial, we’ll build QuickVideo, a Windows desktop tool to batch adjust video speed with perfectly synced audio. You can drag & drop videos, preview changes, and export them in multiple formats.

You can download the EXE or clone the repository:

Windows EXE:
https://github.com/rogers-cyber/QuickVideo/releases

Source Code:

git clone https://github.com/rogers-cyber/QuickVideo.git

  1. Project Setup

Install the required Python packages:

pip install ttkbootstrap tkinterdnd2
Enter fullscreen mode Exit fullscreen mode

Ensure you have FFmpeg installed and its path configured:

FFMPEG_PATH = r"C:\ffmpeg\bin\ffmpeg.exe"
FFPLAY_PATH = r"C:\ffmpeg\bin\ffplay.exe"
Enter fullscreen mode Exit fullscreen mode
  1. Create the Main Window

We’ll use tkinterdnd2 for drag & drop and ttkbootstrap for a modern dark theme.

from tkinterdnd2 import TkinterDnD
import ttkbootstrap as tb

app = TkinterDnD.Tk()
app.title("QuickVideo – Change Video Speed v1.0.0")
app.geometry("1200x760")
tb.Style("darkly")
Enter fullscreen mode Exit fullscreen mode
  1. Define Application State

Keep track of videos, UI updates, and preview state:

video_list = []
ui_queue = Queue()
stop_flag = False
pause_flag = False
preview_process = None
Enter fullscreen mode Exit fullscreen mode
  1. Add File Selection UI

Create a file card with drag & drop support and buttons to add or clear videos:

files_card = tb.Labelframe(app, text="🎬 Video Files", padding=12)
files_card.pack(fill="x", padx=12, pady=6)

listbox = tk.Listbox(files_card, height=4)
listbox.pack(side="left", fill="x", expand=True)

scroll = tk.Scrollbar(files_card, command=listbox.yview)
scroll.pack(side="right", fill="y")
listbox.config(yscrollcommand=scroll.set)
Enter fullscreen mode Exit fullscreen mode

Add buttons to add or clear videos:

tb.Button(files_card, text="➕ Add Files", bootstyle="success", command=add_videos).pack(side="left")
tb.Button(files_card, text="🧹 Clear", bootstyle="danger-outline", command=clear_videos).pack(side="left", padx=8)
Enter fullscreen mode Exit fullscreen mode
  1. Playback Speed and Output Settings

Add a slider for speed and preset buttons:

speed_var = tb.DoubleVar(value=1.0)
speed_lbl = tb.Label(app, text="1.00x", foreground="#22c55e")
speed_lbl.pack()

def update_speed(val):
    speed_lbl.config(text=f"{float(val):.2f}x")

tb.Scale(app, from_=0.5, to=3.0, variable=speed_var, orient="horizontal", command=update_speed).pack()
Enter fullscreen mode Exit fullscreen mode

Preset buttons:

for v in [0.75, 1.0, 1.25, 1.5, 2.0]:
    tb.Button(app, text=f"{v}x", bootstyle="info-outline", command=lambda x=v: speed_var.set(x)).pack(side="left")
Enter fullscreen mode Exit fullscreen mode
  1. Output Format and Folder

Let users select the output format and folder:

output_format = tb.StringVar(value="mp4")
output_dir = tb.StringVar()

tb.Combobox(app, values=["mp4","mkv","avi","mov","webm"], textvariable=output_format, state="readonly").pack()
tb.Entry(app, textvariable=output_dir).pack()
tb.Button(app, text="Browse", bootstyle="secondary", command=lambda: output_dir.set(filedialog.askdirectory())).pack()
Enter fullscreen mode Exit fullscreen mode
  1. Progress and Logging

Add a progress bar and a log panel:

progress_var = tb.IntVar()
tb.Progressbar(app, variable=progress_var, maximum=100).pack()

log_text = tk.Text(app, height=5)
log_text.pack(fill="both", expand=True)
log_text.config(state="disabled")
8. Core Video Processing
Enter fullscreen mode Exit fullscreen mode

Use FFmpeg to adjust video speed:

def process_videos():
    for video in video_list:
        speed = speed_var.get()
        fmt = output_format.get()
        out_file = os.path.join(output_dir.get() or os.path.dirname(video), f"{os.path.splitext(os.path.basename(video))[0]}_{speed:.2f}x.{fmt}")

        cmd = [
            FFMPEG_PATH, "-y",
            "-i", video,
            "-filter:v", f"setpts=PTS/{speed}",
            "-filter:a", f"atempo={speed}",
            out_file
        ]

        subprocess.run(cmd, creationflags=CREATE_NO_WINDOW)
Enter fullscreen mode Exit fullscreen mode
  1. Live Preview

Preview the video speed with FFplay:

def play_preview():
    video = listbox.get(listbox.curselection()[0])
    speed = speed_var.get()
    cmd = [FFPLAY_PATH, "-autoexit", "-vf", f"setpts=PTS/{speed}", "-af", f"atempo={speed}", video]
    subprocess.Popen(cmd, creationflags=CREATE_NO_WINDOW)
Enter fullscreen mode Exit fullscreen mode
  1. Controls

Add Start, Pause, Stop, and Preview buttons:

tb.Button(app, text="🚀 Start", bootstyle="success", command=lambda: threading.Thread(target=process_videos, daemon=True).start()).pack(side="left")
tb.Button(app, text="⏸ Pause", bootstyle="warning-outline", command=lambda: setattr(sys.modules[__name__], "pause_flag", not pause_flag)).pack(side="left")
tb.Button(app, text="🛑 Stop", bootstyle="danger-outline", command=lambda: setattr(sys.modules[__name__], "stop_flag", True)).pack(side="left")
tb.Button(app, text="▶ Play Preview", bootstyle="info", command=play_preview).pack(side="left")
Enter fullscreen mode Exit fullscreen mode
  1. Drag & Drop Support

Enable users to drop videos directly into the app:

def drop(event):
    files = app.tk.splitlist(event.data)
    for f in files:
        if os.path.isfile(f) and f not in video_list:
            video_list.append(f)
            ui_queue.put(("add", f))

app.drop_target_register(DND_FILES)
app.dnd_bind("<<Drop>>", drop)
Enter fullscreen mode Exit fullscreen mode
  1. Start the App

Finally, run the UI loop:

app.after(100, process_ui)
app.mainloop()
Enter fullscreen mode Exit fullscreen mode

✅ Result: You now have a fully functional QuickVideo desktop tool that can batch adjust video playback speed, preview changes live, and export multiple formats on Windows.

Top comments (0)