Fix medium-severity defects across download, formats and tooling
- Format table: a missing acodec was treated as "has audio", skipping
the +bestaudio merge and producing silent videos for extractors that
omit the field.
- Progress bar: separate video/audio stream downloads each reported
0-100%, making the bar jump backwards; per-phase scaling now maps the
two streams onto 0-50/50-100.
- Custom commands: parse with shlex (quoted arguments with spaces were
shredded by str.split), keep POSIX mode off on Windows so backslash
paths survive, hide the console window like every other call site,
close the stdout pipe, and support cancellation of a running command.
- Settings dialog: _("settings", "error_saving", ...) passed two
positional args to the i18n helper, raising TypeError inside the
except handler instead of showing the intended error dialog.
- ffmpeg on Windows: Path(os.getenv("LOCALAPPDATA")) crashed with
TypeError when the variable is unset; fall back to the standard
AppData/Local location.
- Version cache: cached path (str) was compared against a Path, so the
cache never hit and every version query spawned a subprocess.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@ Custom functionality dialogs for YTSage application.
|
||||
Contains dialogs for custom commands, cookies, time ranges, and other special features.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import threading
|
||||
from pathlib import Path
|
||||
@@ -32,7 +34,7 @@ from PySide6.QtWidgets import (
|
||||
from ..ytsage_smooth_tab_widget import SmoothTabWidget
|
||||
from ...core.ytsage_yt_dlp import get_yt_dlp_path
|
||||
from ...core.ytsage_utils import update_auto_update_settings
|
||||
from ...utils.ytsage_constants import YTDLP_DOCS_URL
|
||||
from ...utils.ytsage_constants import SUBPROCESS_CREATIONFLAGS, YTDLP_DOCS_URL
|
||||
from ...utils.ytsage_config_manager import ConfigManager
|
||||
from ...utils.ytsage_localization import LocalizationManager, _
|
||||
from ...utils.ytsage_logger import logger
|
||||
@@ -55,16 +57,28 @@ class CommandWorker(QObject):
|
||||
self.command = command
|
||||
self.url = url
|
||||
self.path = path
|
||||
self._proc = None
|
||||
self._cancelled = False
|
||||
|
||||
def cancel(self):
|
||||
"""Terminate a running command."""
|
||||
self._cancelled = True
|
||||
if self._proc and self._proc.poll() is None:
|
||||
try:
|
||||
self._proc.terminate()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def run_command(self):
|
||||
"""Run the yt-dlp command and emit signals for output"""
|
||||
try:
|
||||
# Split command into arguments
|
||||
args = self.command.split()
|
||||
# Split command into arguments; posix=False on Windows so quoted
|
||||
# backslash paths like "C:\Users\..." survive intact
|
||||
args = shlex.split(self.command, posix=(os.name != "nt"))
|
||||
|
||||
# Build the full command
|
||||
yt_dlp_path = get_yt_dlp_path()
|
||||
base_cmd = [yt_dlp_path] + args
|
||||
base_cmd = [str(yt_dlp_path)] + args
|
||||
|
||||
# Add download path if specified
|
||||
if self.path:
|
||||
@@ -78,19 +92,24 @@ class CommandWorker(QObject):
|
||||
self.output_received.emit("=" * 50)
|
||||
|
||||
# Run the command
|
||||
proc = subprocess.Popen(
|
||||
self._proc = subprocess.Popen(
|
||||
base_cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
errors="replace",
|
||||
creationflags=SUBPROCESS_CREATIONFLAGS,
|
||||
)
|
||||
proc = self._proc
|
||||
|
||||
# Stream output
|
||||
for line in proc.stdout: # type: ignore[reportOptionalIterable]
|
||||
if line.strip(): # Only show non-empty lines
|
||||
self.output_received.emit(line.rstrip())
|
||||
with proc.stdout: # type: ignore[union-attr]
|
||||
for line in proc.stdout: # type: ignore[reportOptionalIterable]
|
||||
if self._cancelled:
|
||||
break
|
||||
if line.strip(): # Only show non-empty lines
|
||||
self.output_received.emit(line.rstrip())
|
||||
|
||||
ret = proc.wait()
|
||||
self.output_received.emit("=" * 50)
|
||||
|
||||
@@ -985,5 +985,5 @@ class AutoUpdateSettingsDialog(QDialog):
|
||||
msg_box.exec()
|
||||
except Exception as e:
|
||||
logger.exception(f"Error saving auto-update settings: {e}")
|
||||
msg_box = self._create_styled_message_box(QMessageBox.Icon.Critical, _("settings.error_title"), _("settings", "error_saving", error=str(e)))
|
||||
msg_box = self._create_styled_message_box(QMessageBox.Icon.Critical, _("settings.error_title"), _("settings.error_saving", error=str(e)))
|
||||
msg_box.exec()
|
||||
|
||||
Reference in New Issue
Block a user