From 0b0e3add3c37030f47f9356abc605c5cff227ed0 Mon Sep 17 00:00:00 2001
From: oop7 <110548351+oop7@users.noreply.github.com>
Date: Tue, 3 Feb 2026 18:51:05 +0200
Subject: [PATCH] Add Deno integration check and system info thread
Introduce check_ytdlp_deno_integration() in ytsage_yt_dlp.py to detect Deno integration by running `yt-dlp --verbose` and scanning debug output for JS runtimes containing "deno" (with timeout and logging/fallback).
Add SystemInfoThread (QThread) in ytsage_dialogs_base.py to collect yt-dlp, ffmpeg and deno presence, versions, paths, cache timestamps and integration status in the background, emitting the gathered info via info_ready. AboutDialog now starts the thread in update_system_info() and populates the UI asynchronously via _populate_system_info(), showing a small "+ yt-dlp" integration indicator next to the Deno status when detected. Refactors usage of version/cache lookups to use the thread-provided info dictionary.
---
ytsage/core/ytsage_yt_dlp.py | 40 ++++++++
.../ytsage_gui_dialogs/ytsage_dialogs_base.py | 93 ++++++++++++++-----
2 files changed, 112 insertions(+), 21 deletions(-)
diff --git a/ytsage/core/ytsage_yt_dlp.py b/ytsage/core/ytsage_yt_dlp.py
index 06efdb7..e9902c3 100644
--- a/ytsage/core/ytsage_yt_dlp.py
+++ b/ytsage/core/ytsage_yt_dlp.py
@@ -712,3 +712,43 @@ def setup_ytdlp(parent_widget=None):
# User cancelled or setup failed, return the fallback command
logger.debug("Returning fallback command 'yt-dlp'")
return "yt-dlp"
+
+
+def check_ytdlp_deno_integration() -> bool:
+ """
+ Check if yt-dlp is integrated with Deno by running 'yt-dlp --verbose'.
+ Returns:
+ bool: True if Deno is detected in JS runtimes, False otherwise
+ """
+ try:
+ ytdlp_path = get_yt_dlp_path()
+ if not ytdlp_path or ytdlp_path == "yt-dlp":
+ return False
+
+ # Run yt-dlp --verbose to check JS runtimes
+ # We use a dummy URL or just --verbose with no URL (which might error but should print debug info)
+ # However, yt-dlp might not print debug info if no URL is provided and it errors out immediately with "usage".
+ # But per user example: "yt-dlp.exe: error: You must provide at least one URL." comes AFTER debug info.
+
+ result = subprocess.run(
+ [str(ytdlp_path), "--verbose"],
+ capture_output=True,
+ text=True,
+ timeout=10,
+ creationflags=SUBPROCESS_CREATIONFLAGS
+ )
+
+ # Check stderr for "[debug] JS runtimes: deno"
+ output = result.stderr
+ if "[debug] JS runtimes:" in output and "deno" in output:
+ # Find the line
+ for line in output.splitlines():
+ if "[debug] JS runtimes:" in line and "deno" in line:
+ logger.info(f"Deno integration detected: {line.strip()}")
+ return True
+
+ return False
+
+ except Exception as e:
+ logger.warning(f"Failed to check yt-dlp Deno integration: {e}")
+ return False
diff --git a/ytsage/gui/ytsage_gui_dialogs/ytsage_dialogs_base.py b/ytsage/gui/ytsage_gui_dialogs/ytsage_dialogs_base.py
index 1f5ad26..6ed900c 100644
--- a/ytsage/gui/ytsage_gui_dialogs/ytsage_dialogs_base.py
+++ b/ytsage/gui/ytsage_gui_dialogs/ytsage_dialogs_base.py
@@ -24,10 +24,55 @@ from ...utils.ytsage_localization import _
from ...core.ytsage_ffmpeg import get_ffmpeg_path
from ...core.ytsage_utils import _version_cache, check_ffmpeg, get_ffmpeg_version, get_ytdlp_version, get_deno_version, refresh_version_cache
-from ...core.ytsage_yt_dlp import check_ytdlp_installed, get_yt_dlp_path
+from ...core.ytsage_yt_dlp import check_ytdlp_installed, get_yt_dlp_path, check_ytdlp_deno_integration
from ...core.ytsage_deno import check_deno_installed, get_deno_path
+class SystemInfoThread(QThread):
+ """Background thread to gather system information."""
+ info_ready = Signal(dict)
+
+ def run(self):
+ info = {}
+
+ # yt-dlp Status
+ ytdlp_found = check_ytdlp_installed()
+ info['ytdlp_found'] = ytdlp_found
+ info['ytdlp_version'] = get_ytdlp_version()
+ info['ytdlp_path'] = get_yt_dlp_path() if ytdlp_found else None
+
+ # yt-dlp cache status
+ ytdlp_cache = _version_cache.get("ytdlp", {})
+ info['ytdlp_last_check'] = ytdlp_cache.get("last_check", 0)
+
+ # FFmpeg Status
+ ffmpeg_found = check_ffmpeg()
+ info['ffmpeg_found'] = ffmpeg_found
+ info['ffmpeg_version'] = get_ffmpeg_version() if ffmpeg_found else _('about.not_available')
+ info['ffmpeg_path'] = get_ffmpeg_path() if ffmpeg_found else None
+
+ # FFmpeg cache status
+ ffmpeg_cache = _version_cache.get("ffmpeg", {})
+ info['ffmpeg_last_check'] = ffmpeg_cache.get("last_check", 0)
+
+ # Deno Status
+ deno_found = check_deno_installed()
+ info['deno_found'] = deno_found
+ info['deno_version'] = get_deno_version() if deno_found else _('about.not_available')
+ info['deno_path'] = get_deno_path() if deno_found else None
+
+ # Deno cache status
+ deno_cache = _version_cache.get("deno", {})
+ info['deno_last_check'] = deno_cache.get("last_check", 0)
+
+ # Check integration with yt-dlp if both are present
+ info['integration_status'] = False
+ if deno_found and ytdlp_found:
+ info['integration_status'] = check_ytdlp_deno_integration()
+
+ self.info_ready.emit(info)
+
+
class LogWindow(QDialog):
def __init__(self, parent=None) -> None:
super().__init__(parent)
@@ -378,7 +423,13 @@ class AboutDialog(QDialog):
return item_widget
def update_system_info(self) -> None:
- """Update the system information display with compact layout."""
+ """Start background thread to gather system info."""
+ self.info_thread = SystemInfoThread()
+ self.info_thread.info_ready.connect(self._populate_system_info)
+ self.info_thread.start()
+
+ def _populate_system_info(self, info: dict) -> None:
+ """Populate UI with gathered info."""
# Clear existing items
for i in reversed(range(self.status_container.count())):
child = self.status_container.itemAt(i).widget()
@@ -386,19 +437,18 @@ class AboutDialog(QDialog):
child.deleteLater()
# yt-dlp Status - compact version with path
- ytdlp_found = check_ytdlp_installed()
+ ytdlp_found = info['ytdlp_found']
ytdlp_status_text = (
f"{_('about.detected')}" if ytdlp_found else f"{_('about.missing')}"
)
- ytdlp_version = get_ytdlp_version()
+ ytdlp_version = info['ytdlp_version']
# Get yt-dlp path
- ytdlp_path = get_yt_dlp_path() if ytdlp_found else None
+ ytdlp_path = info['ytdlp_path']
ytdlp_path_text = ytdlp_path if ytdlp_path and ytdlp_path != "yt-dlp" else None
# Simplified cache status
- ytdlp_cache = _version_cache.get("ytdlp", {})
- last_check = ytdlp_cache.get("last_check", 0)
+ last_check = info['ytdlp_last_check']
cache_status = ""
if last_check > 0:
cache_time = datetime.fromtimestamp(last_check).strftime("%H:%M")
@@ -414,23 +464,19 @@ class AboutDialog(QDialog):
self.status_container.addWidget(ytdlp_item)
# FFmpeg Status - compact version with path
- ffmpeg_found = check_ffmpeg()
+ ffmpeg_found = info['ffmpeg_found']
ffmpeg_status_text = (
f"{_('about.detected')}"
if ffmpeg_found
else f"{_('about.missing')}"
)
- ffmpeg_version = get_ffmpeg_version() if ffmpeg_found else _('about.not_available')
+ ffmpeg_version = info['ffmpeg_version']
# Get FFmpeg path
- ffmpeg_path_text = None
- if ffmpeg_found:
- ffmpeg_path = get_ffmpeg_path()
- ffmpeg_path_text = ffmpeg_path if ffmpeg_path else None
+ ffmpeg_path_text = info['ffmpeg_path']
# Simplified cache status for FFmpeg
- ffmpeg_cache = _version_cache.get("ffmpeg", {})
- last_check = ffmpeg_cache.get("last_check", 0)
+ last_check = info['ffmpeg_last_check']
cache_status = ""
if last_check > 0 and ffmpeg_found:
cache_time = datetime.fromtimestamp(last_check).strftime("%H:%M")
@@ -446,16 +492,16 @@ class AboutDialog(QDialog):
self.status_container.addWidget(ffmpeg_item)
# Deno Status - compact version with path (only show path if in app bin directory)
- deno_found = check_deno_installed()
+ deno_found = info['deno_found']
deno_status_text = (
f"{_('about.detected')}" if deno_found else f"{_('about.missing')}"
)
- deno_version = get_deno_version() if deno_found else _('about.not_available')
+ deno_version = info['deno_version']
# Get Deno path - only show if in app bin directory
deno_path_text = None
if deno_found:
- deno_path = get_deno_path()
+ deno_path = info['deno_path']
# Only show path if it's not the fallback "deno" and the file exists
if deno_path and deno_path != "deno":
from pathlib import Path
@@ -465,22 +511,27 @@ class AboutDialog(QDialog):
deno_path_text = deno_path
# Simplified cache status for Deno
- deno_cache = _version_cache.get("deno", {})
- last_check = deno_cache.get("last_check", 0)
+ last_check = info['deno_last_check']
cache_status = ""
if last_check > 0 and deno_found:
cache_time = datetime.fromtimestamp(last_check).strftime("%H:%M")
cache_status = f" ({cache_time})"
+
+ # Check integration with yt-dlp if both are present
+ integration_status = ""
+ if info.get('integration_status', False):
+ integration_status = f" + yt-dlp"
deno_item = self._create_status_item(
"🦕",
"Deno",
deno_status_text,
- deno_version + cache_status,
+ deno_version + cache_status + integration_status,
deno_path_text,
)
self.status_container.addWidget(deno_item)
+
def refresh_version_info(self) -> None:
"""Refresh version information manually."""
self.refresh_btn.setText(_('about.refreshing'))