1212import zipfile
1313import shlex
1414import tarfile
15+ import ssl
1516from io import BytesIO
1617from functools import partial
1718from utils_safe_extract import safe_tar_extract , safe_zip_extract
@@ -352,15 +353,16 @@ def _finalize_proc(self):
352353
353354 def _on_proc_finished (self , code , status ):
354355 ok = (code == 0 )
355- self . _finalize_proc ()
356+ # Show dialog first, then finalize to avoid brief window where Convert is re-enabled during dialog
356357 if ok :
357358 QtWidgets .QMessageBox .information (self , 'Sucesso' , 'Vídeo convertido com sucesso!' )
358359 else :
359360 QtWidgets .QMessageBox .critical (self , 'Erro' , f'Falha ao converter vídeo (código { code } ).' )
361+ self ._finalize_proc ()
360362
361363 def _on_proc_error (self , err ):
362- self ._finalize_proc ()
363364 QtWidgets .QMessageBox .critical (self , 'Erro' , f'Erro de processo: { err } ' )
365+ self ._finalize_proc ()
364366
365367 def _append_log (self , msg ):
366368 # Efficient append without rewriting whole buffer
@@ -387,13 +389,27 @@ def show_video_info(self):
387389 if not candidate or not os .path .exists (candidate ):
388390 candidate = ffprobe_name # rely on PATH
389391 ffprobe = candidate
392+ # Run ffprobe non-blocking using QProcess
390393 try :
391- command = [ffprobe , '-v' , 'quiet' , '-print_format' , 'json' , '-show_streams' , '-show_format' , inp ]
392- p = subprocess .Popen (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
393- out , err = p .communicate ()
394- if p .returncode != 0 :
395- raise RuntimeError ('ffprobe error' )
396- data = json .loads (out )
394+ self ._info_proc = QtCore .QProcess (self )
395+ self ._info_proc .setProgram (ffprobe )
396+ self ._info_proc .setArguments (['-v' , 'quiet' , '-print_format' , 'json' , '-show_streams' , '-show_format' , inp ])
397+ self ._info_proc .setProcessChannelMode (QtCore .QProcess .ProcessChannelMode .MergedChannels )
398+ self ._info_proc .finished .connect (self ._on_info_finished )
399+ self ._info_proc .errorOccurred .connect (lambda err : QtWidgets .QMessageBox .critical (self , 'Erro' , f'Erro ao executar ffprobe: { err } ' ))
400+ self ._info_proc .start ()
401+ except FileNotFoundError :
402+ QtWidgets .QMessageBox .critical (self , 'Erro' , 'ffprobe não encontrado (verifique PATH ou caminho configurado).' )
403+ except Exception as e :
404+ QtWidgets .QMessageBox .critical (self , 'Erro' , f'Falha ao iniciar ffprobe: { e } ' )
405+
406+ def _on_info_finished (self , code , status ):
407+ try :
408+ out = bytes (self ._info_proc .readAllStandardOutput ()).decode ('utf-8' , errors = 'ignore' ) if hasattr (self , '_info_proc' ) else ''
409+ if code != 0 :
410+ QtWidgets .QMessageBox .critical (self , 'Erro' , f'ffprobe falhou (código { code } ).' )
411+ return
412+ data = json .loads (out or '{}' )
397413 info_text = json .dumps (data , indent = 2 , ensure_ascii = False )
398414 dlg = QtWidgets .QDialog (self )
399415 dlg .setWindowTitle ('Informações detalhadas do vídeo' )
@@ -404,10 +420,12 @@ def show_video_info(self):
404420 v .addWidget (b )
405421 dlg .setLayout (v )
406422 dlg .exec ()
407- except FileNotFoundError :
408- QtWidgets .QMessageBox .critical (self , 'Erro' , 'ffprobe não encontrado (verifique PATH ou caminho configurado).' )
409- except Exception as e :
410- QtWidgets .QMessageBox .critical (self , 'Erro' , f'Falha ao obter info: { e } ' )
423+ finally :
424+ try :
425+ self ._info_proc .deleteLater ()
426+ except Exception :
427+ pass
428+ self ._info_proc = None
411429
412430 def on_no_audio_change (self ):
413431 disabled = self .no_audio_chk .isChecked ()
@@ -422,13 +440,16 @@ def _http_get(self, url: str, timeout: int = 60) -> bytes:
422440 """Download URL returning raw bytes. Tries requests first, then urllib as fallback."""
423441 try :
424442 import requests # type: ignore
425- resp = requests .get (url , timeout = timeout )
443+ resp = requests .get (url , timeout = timeout , verify = True )
426444 resp .raise_for_status ()
427445 return resp .content
428446 except Exception :
429447 # Fallback to urllib
430448 import urllib .request
431- with urllib .request .urlopen (url , timeout = timeout ) as resp : # nosec B310
449+ ctx = ssl .create_default_context ()
450+ ctx .check_hostname = True
451+ ctx .verify_mode = ssl .CERT_REQUIRED
452+ with urllib .request .urlopen (url , timeout = timeout , context = ctx ) as resp : # nosec B310
432453 if resp .status != 200 :
433454 raise RuntimeError (f"HTTP { resp .status } " )
434455 return resp .read ()
0 commit comments