import os import subprocess import re from PyQt6.QtCore import QThread, pyqtSignal class VideoDownloader(QThread): progress_signal = pyqtSignal(str) finished_signal = pyqtSignal(bool, str) def __init__(self, url, download_path, bin_dir="bin"): super().__init__() self.url = url self.download_path = download_path self.bin_dir = os.path.abspath(bin_dir) def run(self): ytdlp_exe = os.path.join(self.bin_dir, "yt-dlp.exe") ffmpeg_exe = os.path.join(self.bin_dir, "ffmpeg.exe") if not os.path.exists(ytdlp_exe): self.finished_signal.emit(False, f"No se encontró yt-dlp.exe en: {ytdlp_exe}") return if not os.path.exists(ffmpeg_exe): self.finished_signal.emit(False, f"No se encontró ffmpeg.exe en: {ffmpeg_exe}") return # Creamos una copia del entorno de sistema y añadimos la carpeta 'bin' al PATH # Esto asegura que tanto yt-dlp como subprocess encuentren ffmpeg y ffprobe sin fallar env = os.environ.copy() env["PATH"] = self.bin_dir + os.pathsep + env.get("PATH", "") # Comando idéntico a tu terminal command = [ ytdlp_exe, "-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio", "--merge-output-format", "mp4", "--ffmpeg-location", self.bin_dir, "-P", self.download_path, self.url ] try: startupinfo = None if os.name == 'nt': startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, startupinfo=startupinfo, env=env, # Le pasamos el entorno enriquecido con el PATH a bin encoding='utf-8', errors='replace' ) for line in process.stdout: line_str = line.strip() if line_str: # Imprime en la consola de Python para poder ver cualquier advertencia de yt-dlp print(line_str) if "[download]" in line_str and "%" in line_str: match = re.search(r'(\d+\.\d+%)', line_str) if match: self.progress_signal.emit(f"Descargando: {match.group(1)}") elif "[Merger]" in line_str or "Merging" in line_str: self.progress_signal.emit("Uniendo vídeo y audio en MP4...") process.wait() if process.returncode == 0: self.finished_signal.emit(True, "¡Vídeo descargado y unido correctamente!") else: self.finished_signal.emit(False, "Error durante la descarga o la fusión.") except Exception as e: self.finished_signal.emit(False, f"Error de ejecución: {str(e)}")