Code: Select all
import os
import subprocess
import threading
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from tkinterdnd2 import DND_FILES, TkinterDnD
def create_surround_video(video_file, rear_audio_file, output_file, on_done):
cmd = [
"ffmpeg", "-y",
"-i", video_file,
"-i", rear_audio_file,
"-filter_complex",
"[0:a]channelsplit=channel_layout=stereo[left][right];"
"[1:a]channelsplit=channel_layout=stereo[rear_left][rear_right];"
"anullsrc=r=48000:cl=mono[center];"
"anullsrc=r=48000:cl=mono[lfe];"
"[left][right][center][lfe][rear_left][rear_right]"
"join=inputs=6:channel_layout=5.1[aout]",
"-map", "0:v", # Video
"-map", "0:a", # Original audio (track 1)
"-map", "[aout]", # Surround mix (track 2)
"-c:v", "copy",
"-c:a:0", "aac", # Encode original audio
"-c:a:1", "aac", # Encode surround mix
"-metadata:s:a:0", "title=Original Audio",
"-metadata:s:a:1", "title=Estim Surround",
"-disposition:a:0", "default", # Make original default
"-disposition:a:1", "0", # Not default
output_file
]
try:
subprocess.run(cmd, check=True)
on_done(success=True, message=f"✅ Created video with two audio tracks:\n{output_file}")
except subprocess.CalledProcessError as e:
on_done(success=False, message=f"❌ FFmpeg failed:\n{e}")
def browse_video():
path = filedialog.askopenfilename(filetypes=[("Video files", "*.mp4")])
if path:
video_var.set(path)
def browse_audio():
path = filedialog.askopenfilename(filetypes=[("Audio files", ".wav, .mp3")])
if path:
audio_var.set(path)
def on_run():
video = video_var.get()
audio = audio_var.get()
if not os.path.exists(video) or not os.path.exists(audio):
messagebox.showwarning("Missing Files", "Please provide both video and audio files.")
return
output_file = os.path.splitext(video)[0] + "_estim_surround.mp4"
run_button.config(state="disabled")
progress_bar.start(10)
def on_done(success, message):
progress_bar.stop()
run_button.config(state="normal")
if success:
messagebox.showinfo("Success", message)
else:
messagebox.showerror("Error", message)
# Run in background thread to avoid freezing the GUI
threading.Thread(
target=create_surround_video,
args=(video, audio, output_file, on_done),
daemon=True
).start()
def on_drop(event, var):
file_path = event.data.strip("{}")
var.set(file_path)
# GUI setup
app = TkinterDnD.Tk()
app.title("Estim Surround Video Builder")
app.geometry("600x300")
video_var = tk.StringVar()
audio_var = tk.StringVar()
tk.Label(app, text="Drag & drop or browse for files", font=("Arial", 14)).pack(pady=10)
tk.Label(app, text="Video File:").pack()
video_entry = tk.Entry(app, textvariable=video_var, width=80)
video_entry.pack(pady=2)
video_entry.drop_target_register(DND_FILES)
video_entry.dnd_bind('<<Drop>>', lambda e: on_drop(e, video_var))
tk.Button(app, text="Browse...", command=browse_video).pack(pady=2)
tk.Label(app, text="Estim Audio File:").pack()
audio_entry = tk.Entry(app, textvariable=audio_var, width=80)
audio_entry.pack(pady=2)
audio_entry.drop_target_register(DND_FILES)
audio_entry.dnd_bind('<<Drop>>', lambda e: on_drop(e, audio_var))
tk.Button(app, text="Browse...", command=browse_audio).pack(pady=2)
progress_bar = ttk.Progressbar(app, mode='indeterminate', length=400)
progress_bar.pack(pady=10)
run_button = tk.Button(app, text="Create Surround Video", command=on_run, height=2, width=30, bg="#4CAF50", fg="white")
run_button.pack(pady=5)
app.mainloop()