Skip to content

Commit e3535d5

Browse files
committed
upload ffmpeg audio and video merger
0 parents  commit e3535d5

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
build/
2+
dist/
3+
main.spec
4+
ffmpeg_path.json

ffmpeg_icon.png

550 Bytes
Loading

main.py

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import os, json, tkinter as tk, ctypes, threading
2+
from tkinter import filedialog, ttk
3+
4+
class MyApp(tk.Tk):
5+
def __init__(self):
6+
super().__init__()
7+
self.ffmpeg_path = tk.StringVar()
8+
self.video_path = tk.StringVar()
9+
self.audio_path = tk.StringVar()
10+
self.output_path = tk.StringVar()
11+
self.file_name = tk.StringVar()
12+
13+
if os.path.exists("ffmpeg_path.json"):
14+
with open("ffmpeg_path.json", "r") as f:
15+
try:
16+
data = json.load(f)
17+
self.ffmpeg_path.set(data.get("ffmpeg_path", ""))
18+
self.video_path.set(data.get("video_path", ""))
19+
self.audio_path.set(data.get("audio_path", ""))
20+
self.output_path.set(data.get("output_path", ""))
21+
self.file_name.set(data.get("file_name", "output.mp4"))
22+
except json.JSONDecodeError:
23+
self.ffmpeg_path.set("")
24+
self.video_path.set("")
25+
self.audio_path.set("")
26+
self.output_path.set("")
27+
self.file_name.set("output.mp4")
28+
29+
# Frame for FFmpeg path
30+
ffmpeg_frame = tk.Frame(self)
31+
ffmpeg_frame.pack(pady=10, anchor="w", padx=10)
32+
tk.Label(ffmpeg_frame, text="FFmpeg Path, leave blank if in PATH").pack(anchor="w")
33+
self.ffmpeg_path_entry = tk.Entry(ffmpeg_frame, textvariable=self.ffmpeg_path, width=50)
34+
self.ffmpeg_path_entry.pack(side=tk.LEFT)
35+
self.ffmpeg_browse_button = tk.Button(ffmpeg_frame, text="Browse", command=self.browse_ffmpeg, borderwidth=0, highlightthickness=0, background="#3C3C3C", cursor="hand2")
36+
self.ffmpeg_browse_button.pack(side=tk.LEFT, padx=10)
37+
38+
# Frame for Video path
39+
video_frame = tk.Frame(self)
40+
video_frame.pack(pady=10, anchor="w", padx=10)
41+
tk.Label(video_frame, text="Video Path").pack(anchor="w")
42+
self.video_path_entry = tk.Entry(video_frame, textvariable=self.video_path, width=50)
43+
self.video_path_entry.pack(side=tk.LEFT)
44+
self.video_browse_button = tk.Button(video_frame, text="Browse", command=self.browse_video, borderwidth=0, highlightthickness=0, background="#3C3C3C", cursor="hand2")
45+
self.video_browse_button.pack(side=tk.LEFT, padx=10)
46+
47+
# Frame for Audio path
48+
audio_frame = tk.Frame(self)
49+
audio_frame.pack(pady=10, anchor="w", padx=10)
50+
tk.Label(audio_frame, text="Audio Path").pack(anchor="w")
51+
self.audio_path_entry = tk.Entry(audio_frame, textvariable=self.audio_path, width=50)
52+
self.audio_path_entry.pack(side=tk.LEFT)
53+
self.audio_browse_button = tk.Button(audio_frame, text="Browse", command=self.browse_audio, borderwidth=0, highlightthickness=0, background="#3C3C3C", cursor="hand2")
54+
self.audio_browse_button.pack(side=tk.LEFT, padx=10)
55+
56+
57+
# Output path
58+
output_frame = tk.Frame(self)
59+
output_frame.pack(pady=10, anchor="w", padx=10)
60+
tk.Label(output_frame, text="Output Path").pack(anchor="w")
61+
self.output_path_entry = tk.Entry(output_frame, textvariable=self.output_path, width=50)
62+
self.output_path_entry.pack(side=tk.LEFT)
63+
self.output_browse_button = tk.Button(output_frame, text="Browse", command=self.browse_output, borderwidth=0, highlightthickness=0, background="#3C3C3C", cursor="hand2")
64+
self.output_browse_button.pack(side=tk.LEFT, padx=10)
65+
66+
output_file_frame = tk.Frame(self)
67+
output_file_frame.pack(pady=10, anchor="w", padx=10)
68+
tk.Label(output_file_frame, text="Output File Name").pack(anchor="w")
69+
self.output_file_name_entry = tk.Entry(output_file_frame, textvariable=self.file_name, width=50)
70+
self.output_file_name_entry.pack(side=tk.LEFT)
71+
self.output_browse_button = tk.Button(output_file_frame, text="Browse", command=self.browse_output_file)
72+
self.output_browse_button.pack(side=tk.LEFT, padx=10)
73+
74+
# Buttons
75+
button_frame = tk.Frame(self)
76+
button_frame.pack(pady=10, anchor="w", padx=10)
77+
self.save_button = tk.Button(button_frame, text="Save Paths", command=self.save_paths, borderwidth=0, highlightthickness=0, background="#3C3C3C", cursor="hand2")
78+
self.save_button.pack(side=tk.LEFT)
79+
self.run_button = tk.Button(button_frame, text="Merge", command=self.thread_run_ffmpeg, borderwidth=0, highlightthickness=0, background="#3C3C3C", cursor="hand2")
80+
self.run_button.pack(side=tk.LEFT, padx=10)
81+
self.clear_paths_button = tk.Button(button_frame, text="Clear Paths", command=self.clear_paths, borderwidth=0, highlightthickness=0, background="#3C3C3C", cursor="hand2")
82+
self.clear_paths_button.pack(side=tk.LEFT, padx=(0,10))
83+
84+
# note
85+
note_frame = tk.Frame(self)
86+
note_frame.pack(pady=10, anchor="w", padx=10)
87+
tk.Label(note_frame, text="Note: If ffmpeg path is blank, it will be searched in PATH.", foreground="#5E5E5E").pack(anchor="w")
88+
tk.Label(note_frame, text="Note: If output path is blank, it will be saved in app directory.", foreground="#5E5E5E").pack(anchor="w")
89+
tk.Label(note_frame, text="Note: If ffmpeg asks you something in the terminal, please do as it says.", foreground="#5E5E5E").pack(anchor="w")
90+
91+
def display_notification(self, title, message):
92+
ctypes.windll.user32.MessageBoxW(0, message, title, 0x40 | 0x40000)
93+
def clear_paths(self):
94+
self.ffmpeg_path.set("")
95+
self.video_path.set("")
96+
self.audio_path.set("")
97+
self.output_path.set("")
98+
self.file_name.set("output.mp4")
99+
with open("ffmpeg_path.json", "w") as f:
100+
json.dump({"ffmpeg_path": "", "video_path": "", "audio_path": "", "output_path": "", "file_name": "output.mp4"}, f)
101+
def browse_ffmpeg(self):
102+
path = filedialog.askdirectory()
103+
if path:
104+
self.ffmpeg_path.set(os.path.join(path, "ffmpeg"))
105+
106+
def browse_video(self):
107+
path = filedialog.askopenfilename(filetypes=[("Video Files", "*.mp4;*.mkv;*.avi;*.mov;*.webm")])
108+
if path:
109+
self.video_path.set(path)
110+
111+
def browse_audio(self):
112+
path = filedialog.askopenfilename(filetypes=[("Audio Files", "*.mp3;*.aac;*.wav;*.flac;*.opus;*.m4a;*.webm")])
113+
if path:
114+
self.audio_path.set(path)
115+
116+
def browse_output(self):
117+
path = filedialog.askdirectory()
118+
if path:
119+
self.output_path.set(path)
120+
121+
def browse_output_file(self):
122+
file_path = filedialog.asksaveasfilename(filetypes=[("Video Files", "*.mp4;*.mkv;*.avi;*.mov;*.webm")])
123+
if file_path:
124+
self.file_name.set(os.path.basename(file_path))
125+
126+
127+
def save_paths(self):
128+
paths = {
129+
"ffmpeg_path": self.ffmpeg_path.get(),
130+
"video_path": self.video_path.get(),
131+
"audio_path": self.audio_path.get(),
132+
"output_path": self.output_path.get(),
133+
"file_name": self.file_name.get()
134+
}
135+
with open("ffmpeg_path.json", "w") as f:
136+
json.dump(paths, f)
137+
138+
def run_ffmpeg(self):
139+
with open("ffmpeg_path.json", "r") as f:
140+
try:
141+
data = json.load(f)
142+
self.ffmpeg_path.set(data.get("ffmpeg_path", ""))
143+
self.video_path.set(data.get("video_path", ""))
144+
self.audio_path.set(data.get("audio_path", ""))
145+
self.output_path.set(data.get("output_path", ""))
146+
self.file_name.set(data.get("file_name", ""))
147+
except json.JSONDecodeError:
148+
self.ffmpeg_path.set("")
149+
self.video_path.set("")
150+
self.audio_path.set("")
151+
self.output_path.set("")
152+
self.file_name.set("")
153+
154+
ffmpeg_path = self.ffmpeg_path.get()
155+
video_path = self.video_path.get()
156+
audio_path = self.audio_path.get()
157+
output_path = self.output_path.get().replace("/", "\\")
158+
file_name = self.file_name.get()
159+
160+
if video_path and audio_path:
161+
if not ffmpeg_path:
162+
ffmpeg_path = "ffmpeg"
163+
if not output_path:
164+
output_path = os.getcwd()
165+
#print(f'{ffmpeg_path} -i "{video_path}" -i "{audio_path}" -c:v copy -c:a aac "{output_path}\\{file_name}"')
166+
os.system(f'{ffmpeg_path} -i "{video_path}" -i "{audio_path}" -c:v copy -c:a aac "{output_path}\\{file_name}"')
167+
self.display_notification("FFmpeg Audio and Video Merger", f"Video and audio merged successfully\n {output_path}\\{file_name}")
168+
else:
169+
print("Please make sure all paths are set.")
170+
171+
def thread_run_ffmpeg(self):
172+
threading.Thread(target=self.run_ffmpeg).start()
173+
174+
if __name__ == "__main__":
175+
app = MyApp()
176+
app.title("FFmpeg Audio and Video Merger")
177+
app.minsize(400, 400)
178+
app.resizable(False, False)
179+
app.tk_setPalette(background='#2b2b2b', foreground='white')
180+
try:
181+
app.iconphoto(False, tk.PhotoImage(file='ffmpeg_icon.png'))
182+
except tk.TclError:
183+
print("Icon not found, using default icon")
184+
185+
app.mainloop()

0 commit comments

Comments
 (0)