From 8610f4d95cf5e4236f1a80b58525828ed438e9f3 Mon Sep 17 00:00:00 2001 From: luzadev Date: Mon, 8 Jun 2026 23:06:59 +0200 Subject: [PATCH] Fix Windows: nascondi finestra cmd per ogni subprocess yt-dlp/ffmpeg Aggiunto helper subprocess_flags() in core/paths.py che ritorna CREATE_NO_WINDOW su Windows. Applicato a tutte le subprocess.run/Popen in downloader, upgrader e recorder per evitare il flash di console nera ad ogni traccia scaricata. Co-Authored-By: Claude Opus 4.7 --- core/config.py | 2 +- core/downloader.py | 11 +++++++---- core/paths.py | 13 +++++++++++++ core/recorder.py | 5 ++++- core/upgrader.py | 9 ++++++--- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/core/config.py b/core/config.py index 6fc0aea..92cf644 100644 --- a/core/config.py +++ b/core/config.py @@ -5,7 +5,7 @@ import os import sys from pathlib import Path -VERSION = "v1.5.0" +VERSION = "v1.5.1" APP_NAME = "MusicTools" diff --git a/core/downloader.py b/core/downloader.py index 9eac552..2376e4e 100644 --- a/core/downloader.py +++ b/core/downloader.py @@ -9,7 +9,7 @@ import threading from pathlib import Path from typing import Callable, Optional -from core.paths import find_ytdlp, find_ffmpeg_dir +from core.paths import find_ytdlp, find_ffmpeg_dir, subprocess_flags # Flag globale per interruzione @@ -102,7 +102,7 @@ def _search_youtube(query: str, cookies_path: Optional[str] = None) -> tuple[str if cookies_path and Path(cookies_path).exists(): cmd.extend(["--cookies", cookies_path]) - result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) + result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, **subprocess_flags()) if result.returncode != 0: raise ValueError(f"Ricerca fallita: {result.stderr.strip()}") @@ -204,6 +204,7 @@ def download_playlist( with _process_lock: _current_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + **subprocess_flags(), ) for line in _current_process.stdout: @@ -278,7 +279,7 @@ def download_direct_url( probe_cmd.extend(["--cookies", cookies_path]) try: - result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=60) + result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=60, **subprocess_flags()) if result.returncode != 0: raise ValueError(result.stderr.strip()) except Exception as e: @@ -356,6 +357,7 @@ def download_direct_url( with _process_lock: _current_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + **subprocess_flags(), ) for line in _current_process.stdout: @@ -443,7 +445,7 @@ def download_video( probe_cmd.extend(["--cookies", cookies_path]) try: - result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=60) + result = subprocess.run(probe_cmd, capture_output=True, text=True, timeout=60, **subprocess_flags()) if result.returncode != 0: raise ValueError(result.stderr.strip()) except Exception as e: @@ -522,6 +524,7 @@ def download_video( with _process_lock: _current_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + **subprocess_flags(), ) for line in _current_process.stdout: diff --git a/core/paths.py b/core/paths.py index 25b71a1..e379d2d 100644 --- a/core/paths.py +++ b/core/paths.py @@ -48,6 +48,19 @@ def _exe(name: str) -> str: return name +def subprocess_flags() -> dict: + """Kwargs per subprocess.run/Popen che nascondono la console su Windows. + + Senza CREATE_NO_WINDOW, ogni subprocess (yt-dlp, ffmpeg, ffprobe) lanciato + da un'app GUI PyInstaller apre una finestra cmd nera per la durata del + processo. Su macOS/Linux il flag non esiste, quindi ritorniamo dict vuoto. + """ + if _IS_WINDOWS: + # 0x08000000 == CREATE_NO_WINDOW + return {"creationflags": 0x08000000} + return {} + + def find_ytdlp() -> str: """Trova il path di yt-dlp. diff --git a/core/recorder.py b/core/recorder.py index d32136a..468d222 100644 --- a/core/recorder.py +++ b/core/recorder.py @@ -15,7 +15,7 @@ import time from pathlib import Path from typing import Callable, Optional -from core.paths import find_ffmpeg +from core.paths import find_ffmpeg, subprocess_flags _IS_MAC = sys.platform == "darwin" @@ -79,6 +79,7 @@ def _list_macos(ffmpeg: str) -> list[dict]: [ffmpeg, "-hide_banner", "-f", "avfoundation", "-list_devices", "true", "-i", ""], capture_output=True, text=True, timeout=10, + **subprocess_flags(), ) except Exception: return [] @@ -116,6 +117,7 @@ def _list_windows(ffmpeg: str) -> list[dict]: [ffmpeg, "-hide_banner", "-f", "dshow", "-list_devices", "true", "-i", "dummy"], capture_output=True, text=True, timeout=10, + **subprocess_flags(), ) except Exception: return [] @@ -247,6 +249,7 @@ def start_recording( stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=False, + **subprocess_flags(), ) drain_t = threading.Thread(target=_drain_stderr, diff --git a/core/upgrader.py b/core/upgrader.py index b5c6cf8..986a1a3 100644 --- a/core/upgrader.py +++ b/core/upgrader.py @@ -9,7 +9,7 @@ import threading from pathlib import Path from typing import Callable, Optional -from core.paths import find_ytdlp, find_ffmpeg_dir, find_ffprobe +from core.paths import find_ytdlp, find_ffmpeg_dir, find_ffprobe, subprocess_flags AUDIO_EXTENSIONS = {".mp3", ".m4a", ".wav", ".flac"} @@ -61,6 +61,7 @@ def get_bitrate(filepath: str | Path) -> int: capture_output=True, text=True, timeout=30, + **subprocess_flags(), ) data = json.loads(result.stdout) bit_rate = int(data.get("format", {}).get("bit_rate", 0)) @@ -94,7 +95,7 @@ def _search_youtube(query: str, cookies_path: Optional[str] = None) -> str: if cookies_path and Path(cookies_path).exists(): cmd.extend(["--cookies", cookies_path]) - result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) + result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, **subprocess_flags()) if result.returncode != 0: raise ValueError("Ricerca fallita") @@ -128,7 +129,7 @@ def update_cover_only( if cookies_path and Path(cookies_path).exists(): cmd.extend(["--cookies", cookies_path]) - subprocess.run(cmd, capture_output=True, timeout=60) + subprocess.run(cmd, capture_output=True, timeout=60, **subprocess_flags()) cover_files = list(temp_dir.glob("cover*.jpg")) if not cover_files: @@ -152,6 +153,7 @@ def update_cover_only( ], capture_output=True, timeout=60, + **subprocess_flags(), ) except Exception: _cleanup_temp(temp_dir) @@ -284,6 +286,7 @@ def upgrade_folder( with _process_lock: _current_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + **subprocess_flags(), ) for line in _current_process.stdout: