Skip to content

Commit 36e21f1

Browse files
committed
Add FFmpeg download button and 'no audio' checkbox to both Tkinter and PyQt6 GUIs
1 parent 2ad0696 commit 36e21f1

2 files changed

Lines changed: 162 additions & 30 deletions

File tree

GUI_pyqt6_WINFF.py

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import subprocess
77
import json
88
from functools import partial
9+
import threading
10+
import urllib.request
11+
import webbrowser
12+
from pathlib import Path
913

1014
from PyQt6 import QtWidgets, QtCore
1115
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, QTextEdit, QComboBox, QFileDialog, QCheckBox, QHBoxLayout, QVBoxLayout
@@ -120,6 +124,17 @@ def _build_ui(self):
120124
h.addWidget(ff_browse)
121125
layout.addLayout(h)
122126

127+
# Download button and no-audio checkbox
128+
h2 = QHBoxLayout()
129+
self.download_btn = QPushButton('Baixar FFmpeg (Downloads)')
130+
self.download_btn.clicked.connect(self.download_ffmpeg_and_maybe_install)
131+
h2.addWidget(self.download_btn)
132+
h2.addStretch()
133+
self.no_audio_chk = QCheckBox('Arquivo sem áudio (remover áudio)')
134+
self.no_audio_chk.stateChanged.connect(self.on_no_audio_change)
135+
h2.addWidget(self.no_audio_chk)
136+
layout.addLayout(h2)
137+
123138
# command display
124139
layout.addWidget(QLabel('Comando FFmpeg:'))
125140
self.command_display = QTextEdit(); self.command_display.setReadOnly(True)
@@ -162,6 +177,45 @@ def select_ffmpeg(self):
162177
if file:
163178
self.ffmpeg_path.setText(file)
164179

180+
def download_ffmpeg_and_maybe_install(self):
181+
def _worker():
182+
try:
183+
downloads = Path.home() / 'Downloads'
184+
downloads.mkdir(parents=True, exist_ok=True)
185+
if os.name == 'nt':
186+
url = 'https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n7.1-latest-win64-gpl-7.1.zip'
187+
out_path = downloads / url.split('/')[-1]
188+
QtWidgets.QMessageBox.information(self, 'Download', f'Baixando FFmpeg para {out_path} ...')
189+
urllib.request.urlretrieve(url, out_path)
190+
QtWidgets.QMessageBox.information(self, 'Download', f'Arquivo salvo em {out_path}')
191+
192+
# Ask user if they want to attempt installation via winget
193+
res = subprocess.run(['winget', '--version'], capture_output=True, text=True)
194+
if res.returncode == 0:
195+
ans = QtWidgets.QMessageBox.question(self, 'Instalar', 'Deseja tentar instalar via winget (Windows)?')
196+
if ans == QtWidgets.QMessageBox.StandardButton.Yes:
197+
proc = subprocess.run(['winget','install','ffmpeg','-e'], capture_output=True, text=True)
198+
if proc.returncode == 0:
199+
QtWidgets.QMessageBox.information(self, 'Instalação', 'FFmpeg instalado via winget com sucesso.')
200+
else:
201+
QtWidgets.QMessageBox.warning(self, 'Instalação', f'Falha na instalação via winget. Saída:\n{proc.stdout}\n{proc.stderr}')
202+
else:
203+
QtWidgets.QMessageBox.warning(self, 'winget', 'winget não está disponível neste sistema.')
204+
else:
205+
webbrowser.open('https://github.com/BtbN/FFmpeg-Builds/releases')
206+
QtWidgets.QMessageBox.information(self, 'Download', 'Página de releases aberta no navegador. Faça o download manualmente.')
207+
except Exception as e:
208+
QtWidgets.QMessageBox.critical(self, 'Erro', f'Falha ao baixar/instalar FFmpeg: {e}')
209+
210+
threading.Thread(target=_worker, daemon=True).start()
211+
212+
def on_no_audio_change(self):
213+
checked = self.no_audio_chk.isChecked()
214+
self.audio_bitrate.setDisabled(checked)
215+
self.audio_sample_rate.setDisabled(checked)
216+
self.audio_channels.setDisabled(checked)
217+
self.update_command_display()
218+
165219
def set_default_options(self):
166220
self.format_combo.setCurrentText('wmv')
167221
self.resolution_combo.setCurrentText('320x240')
@@ -176,6 +230,7 @@ def set_default_options(self):
176230
self.ffmpeg_path.setText('ffmpeg')
177231
self.same_dir_chk.setChecked(False)
178232
self.overwrite_chk.setChecked(True)
233+
self.no_audio_chk.setChecked(False)
179234
self.update_command_display()
180235

181236
def load_config(self):
@@ -198,6 +253,7 @@ def load_config(self):
198253
self.audio_channels.setCurrentText(d.get('audio_channels','1'))
199254
self.same_dir_chk.setChecked(d.get('use_same_directory','False')=='True')
200255
self.overwrite_chk.setChecked(d.get('overwrite_existing','True')=='True')
256+
self.no_audio_chk.setChecked(d.get('use_same_directory','False')=='True')
201257
self.update_command_display()
202258

203259
def save_config(self):
@@ -246,20 +302,25 @@ def build_command(self):
246302
cmd = f'"{ffmpeg}" -y -i "{inp}"' if inp else ''
247303
if video_bitrate:
248304
cmd += f' -b:v {video_bitrate}'
249-
if audio_bitrate:
250-
cmd += f' -b:a {audio_bitrate}'
305+
if not self.no_audio_chk.isChecked():
306+
if audio_bitrate:
307+
cmd += f' -b:a {audio_bitrate}'
251308
if resolution != 'original':
252309
cmd += f' -s {resolution}'
253310
if frame_rate:
254311
cmd += f' -r {frame_rate}'
255-
if audio_sample_rate:
256-
cmd += f' -ar {audio_sample_rate}'
257-
if audio_channels:
258-
cmd += f' -ac {audio_channels}'
312+
if not self.no_audio_chk.isChecked():
313+
if audio_sample_rate:
314+
cmd += f' -ar {audio_sample_rate}'
315+
if audio_channels:
316+
cmd += f' -ac {audio_channels}'
259317
if video_codec != 'auto':
260318
cmd += f' -vcodec {video_codec}'
261-
if audio_codec != 'auto':
262-
cmd += f' -acodec {audio_codec}'
319+
if not self.no_audio_chk.isChecked():
320+
if audio_codec != 'auto':
321+
cmd += f' -acodec {audio_codec}'
322+
if self.no_audio_chk.isChecked():
323+
cmd += ' -an'
263324
if output_file:
264325
cmd += f' "{output_file}"'
265326
return cmd

GUI_tkinter_WINFF.py

Lines changed: 93 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import configparser
1010
import json
1111
import platform
12+
import threading
13+
import urllib.request
14+
import webbrowser
15+
from pathlib import Path
1216

1317
try:
1418
import ffmpeg as ffmpeg_lib # optional: ffmpeg-python
@@ -138,11 +142,48 @@ def select_ffmpeg_executable():
138142
config.write(configfile)
139143
update_command_display()
140144

141-
def load_config_from_file():
142-
cfg = filedialog.askopenfilename(title="Carregar Configuração", filetypes=[("Configurações", "*.ini")])
143-
if cfg:
144-
load_config(cfg)
145-
messagebox.showinfo("Carregar Configuração", "Configuração carregada com sucesso!")
145+
146+
def download_ffmpeg_and_maybe_install():
147+
"""Download a FFmpeg build to the user's Downloads folder.
148+
On Windows offer to try winget install if available and the user agrees.
149+
Runs in a background thread to avoid blocking the UI."""
150+
def _worker():
151+
try:
152+
downloads = Path.home() / 'Downloads'
153+
downloads.mkdir(parents=True, exist_ok=True)
154+
if os.name == 'nt':
155+
# Example Windows build (user-provided example)
156+
url = 'https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n7.1-latest-win64-gpl-7.1.zip'
157+
out_path = downloads / url.split('/')[-1]
158+
messagebox.showinfo('Download', f'Baixando FFmpeg para {out_path} ...')
159+
urllib.request.urlretrieve(url, out_path)
160+
messagebox.showinfo('Download', f'Arquivo salvo em {out_path}')
161+
162+
# Ask user if they want to attempt installation via winget
163+
if messagebox.askyesno('Instalar', 'Deseja tentar instalar via winget (Windows)?'):
164+
try:
165+
# Check winget availability
166+
res = subprocess.run(['winget', '--version'], capture_output=True, text=True)
167+
if res.returncode == 0:
168+
# Try a generic winget install for ffmpeg
169+
install_cmd = ['winget', 'install', 'ffmpeg', '-e']
170+
proc = subprocess.run(install_cmd, capture_output=True, text=True)
171+
if proc.returncode == 0:
172+
messagebox.showinfo('Instalação', 'FFmpeg instalado via winget com sucesso.')
173+
else:
174+
messagebox.showwarning('Instalação', f'Falha na instalação via winget. Saída:\n{proc.stdout}\n{proc.stderr}')
175+
else:
176+
messagebox.showwarning('winget', 'winget não está disponível neste sistema.')
177+
except FileNotFoundError:
178+
messagebox.showwarning('winget', 'winget não encontrado.')
179+
else:
180+
# Non-Windows: open releases page in browser so user can choose
181+
webbrowser.open('https://github.com/BtbN/FFmpeg-Builds/releases')
182+
messagebox.showinfo('Download', 'Página de releases aberta no navegador. Faça o download manualmente.')
183+
except Exception as e:
184+
messagebox.showerror('Erro', f'Falha ao baixar/instalar FFmpeg: {e}')
185+
186+
threading.Thread(target=_worker, daemon=True).start()
146187

147188
# Conversão e comando ffmpeg
148189

@@ -174,23 +215,29 @@ def convert_video():
174215
messagebox.showerror("Erro", f"O arquivo '{output_file}' já existe e não pode ser sobrescrito.")
175216
return
176217

218+
# Build command. If no audio is selected, add -an and skip audio options
177219
command = f'"{ffmpeg_path}" -y -i "{input_file}"'
178220
if video_bitrate:
179221
command += f" -b:v {video_bitrate}"
180-
if audio_bitrate:
181-
command += f" -b:a {audio_bitrate}"
222+
if not no_audio_var.get():
223+
if audio_bitrate:
224+
command += f" -b:a {audio_bitrate}"
182225
if resolution != "original":
183226
command += f" -s {resolution}"
184227
if frame_rate:
185228
command += f" -r {frame_rate}"
186-
if audio_sample_rate:
187-
command += f" -ar {audio_sample_rate}"
188-
if audio_channels:
189-
command += f" -ac {audio_channels}"
229+
if not no_audio_var.get():
230+
if audio_sample_rate:
231+
command += f" -ar {audio_sample_rate}"
232+
if audio_channels:
233+
command += f" -ac {audio_channels}"
190234
if video_codec != "auto":
191235
command += f" -vcodec {video_codec}"
192-
if audio_codec != "auto":
193-
command += f" -acodec {audio_codec}"
236+
if not no_audio_var.get():
237+
if audio_codec != "auto":
238+
command += f" -acodec {audio_codec}"
239+
if no_audio_var.get():
240+
command += ' -an'
194241

195242
command += f" \"{output_file}\""
196243

@@ -327,20 +374,26 @@ def update_command_display(event=None):
327374
command = f'"{ffmpeg_path}" -y -i "{input_file}"' if input_file else ''
328375
if video_bitrate:
329376
command += f" -b:v {video_bitrate}"
330-
if audio_bitrate:
331-
command += f" -b:a {audio_bitrate}"
377+
if not no_audio_var.get():
378+
if audio_bitrate:
379+
command += f" -b:a {audio_bitrate}"
332380
if resolution != "original":
333381
command += f" -s {resolution}"
334382
if frame_rate:
335383
command += f" -r {frame_rate}"
336-
if audio_sample_rate:
337-
command += f" -ar {audio_sample_rate}"
338-
if audio_channels:
339-
command += f" -ac {audio_channels}"
384+
if not no_audio_var.get():
385+
if audio_sample_rate:
386+
command += f" -ar {audio_sample_rate}"
387+
if audio_channels:
388+
command += f" -ac {audio_channels}"
340389
if video_codec != "auto":
341390
command += f" -vcodec {video_codec}"
342-
if audio_codec != "auto":
343-
command += f" -acodec {audio_codec}"
391+
if not no_audio_var.get():
392+
if audio_codec != "auto":
393+
command += f" -acodec {audio_codec}"
394+
if no_audio_var.get():
395+
command += ' -an'
396+
344397
if output_file:
345398
command += f" \"{output_file}\""
346399

@@ -456,7 +509,25 @@ def show_about():
456509
ffmpeg_path_entry = tk.Entry(root, width=70)
457510
ffmpeg_path_entry.grid(row=13, column=1, padx=10, pady=5)
458511
ffmpeg_path_entry.bind("<KeyRelease>", lambda event: update_command_display())
459-
tk.Button(root, text="Procurar", command=select_ffmpeg_executable).grid(row=13, column=2, padx=10, pady=5)
512+
procurar_ffmpeg_btn = tk.Button(root, text="Procurar", command=select_ffmpeg_executable)
513+
procurar_ffmpeg_btn.grid(row=13, column=2, padx=10, pady=5)
514+
515+
# Button to download FFmpeg
516+
download_ffmpeg_btn = tk.Button(root, text="Baixar FFmpeg (Downloads)", command=download_ffmpeg_and_maybe_install)
517+
download_ffmpeg_btn.grid(row=13, column=0, padx=10, pady=5, sticky='w')
518+
519+
# Checkbox: arquivo sem áudio (desabilita campos de áudio e adiciona -an)
520+
no_audio_var = tk.BooleanVar()
521+
def _on_no_audio_toggle():
522+
state = tk.DISABLED if no_audio_var.get() else tk.NORMAL
523+
audio_bitrate_entry.config(state=state)
524+
audio_sample_rate_entry.config(state=state)
525+
audio_channels_menu.config(state=state)
526+
audio_codec_menu.config(state=state)
527+
update_command_display()
528+
529+
no_audio_check = tk.Checkbutton(root, text="Arquivo sem áudio (remover áudio)", variable=no_audio_var, command=_on_no_audio_toggle)
530+
no_audio_check.grid(row=14, column=2, padx=10, pady=5)
460531

461532
# Command display
462533
tk.Label(root, text="Comando FFmpeg:").grid(row=14, column=0, padx=10, pady=5, sticky="w")

0 commit comments

Comments
 (0)