fa3dc0236f
Renamed 'src/core' to 'ytsage/core' and updated all internal imports to use relative paths. This improves package structure and ensures correct module resolution after the directory move.
715 lines
26 KiB
Python
715 lines
26 KiB
Python
import os
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
import requests
|
|
from PySide6.QtCore import Qt, QThread, Signal
|
|
from PySide6.QtGui import QIcon
|
|
from PySide6.QtWidgets import (
|
|
QDialog,
|
|
QFileDialog,
|
|
QHBoxLayout,
|
|
QLabel,
|
|
QMessageBox,
|
|
QProgressBar,
|
|
QPushButton,
|
|
QRadioButton,
|
|
QVBoxLayout,
|
|
QWidget,
|
|
)
|
|
|
|
from ..utils.ytsage_logger import logger
|
|
from ..utils.ytsage_constants import (
|
|
APP_BIN_DIR,
|
|
ICON_PATH,
|
|
OS_FULL_NAME,
|
|
OS_NAME,
|
|
SUBPROCESS_CREATIONFLAGS,
|
|
YTDLP_APP_BIN_PATH,
|
|
YTDLP_DOWNLOAD_URL,
|
|
YTDLP_SHA256_URL,
|
|
)
|
|
from .ytsage_ffmpeg import get_file_sha256
|
|
from ..utils.ytsage_localization import _
|
|
|
|
# YTDLP_URLS moved to src\utils\ytsage_constants.py
|
|
# get_ytdlp_install_dir() moved to src\utils\ytsage_constants.py
|
|
# get_ytdlp_executable_path() moved to src\utils\ytsage_constants.py
|
|
# get_os_type() moved to src\utils\ytsage_constants.py
|
|
# ensure_install_dir_exists() moved to src\utils\ytsage_constants.py
|
|
|
|
|
|
def verify_ytdlp_sha256(file_path: Path, download_url: str) -> bool:
|
|
"""
|
|
Verify yt-dlp file SHA256 hash against official checksums.
|
|
|
|
Args:
|
|
file_path: Path to the downloaded yt-dlp file
|
|
download_url: The URL used to download the file (to determine the filename)
|
|
|
|
Returns:
|
|
bool: True if verification successful, False otherwise
|
|
"""
|
|
try:
|
|
# Download the SHA2-256SUMS file
|
|
logger.info(f"Downloading SHA256 checksums from: {YTDLP_SHA256_URL}")
|
|
response = requests.get(YTDLP_SHA256_URL, timeout=10)
|
|
response.raise_for_status()
|
|
checksum_content = response.text
|
|
|
|
# Extract filename from download URL (e.g., yt-dlp.exe, yt-dlp_macos, yt-dlp)
|
|
filename = download_url.split("/")[-1]
|
|
logger.info(f"Looking for checksum for file: {filename}")
|
|
|
|
# Parse the checksum file to find the matching hash
|
|
expected_hash = None
|
|
for line in checksum_content.strip().split("\n"):
|
|
if filename in line:
|
|
# Format: "hash filename"
|
|
parts = line.strip().split()
|
|
if len(parts) >= 2 and parts[1] == filename:
|
|
expected_hash = parts[0]
|
|
break
|
|
|
|
if not expected_hash:
|
|
logger.error(f"Could not find SHA256 hash for {filename} in checksums file")
|
|
return False
|
|
|
|
# Calculate actual hash of downloaded file
|
|
logger.info("Calculating SHA256 hash of downloaded file...")
|
|
actual_hash = get_file_sha256(file_path)
|
|
|
|
# Compare hashes
|
|
if actual_hash.lower() == expected_hash.lower():
|
|
logger.info("✓ SHA256 verification successful!")
|
|
logger.info(f" Expected: {expected_hash}")
|
|
logger.info(f" Actual: {actual_hash}")
|
|
return True
|
|
else:
|
|
logger.error("✗ SHA256 verification failed!")
|
|
logger.error(f" Expected: {expected_hash}")
|
|
logger.error(f" Actual: {actual_hash}")
|
|
return False
|
|
|
|
except requests.RequestException as e:
|
|
logger.error(f"Failed to download SHA256 checksums: {e}")
|
|
return False
|
|
except Exception as e:
|
|
logger.exception(f"Error during SHA256 verification: {e}")
|
|
return False
|
|
|
|
|
|
class DownloadYtdlpThread(QThread):
|
|
progress_signal = Signal(int)
|
|
finished_signal = Signal(bool, str)
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def run(self) -> None:
|
|
try:
|
|
# Extra logic moved to src\utils\ytsage_constants.py
|
|
exe_path = YTDLP_APP_BIN_PATH
|
|
|
|
# Download with progress reporting
|
|
logger.info(f"Downloading yt-dlp from: {YTDLP_DOWNLOAD_URL}")
|
|
response = requests.get(YTDLP_DOWNLOAD_URL, stream=True)
|
|
response.raise_for_status()
|
|
total_size = int(response.headers.get("content-length", 0))
|
|
block_size = 1024 # 1 Kibibyte
|
|
|
|
if total_size == 0:
|
|
self.progress_signal.emit(100)
|
|
|
|
with open(exe_path, "wb") as f:
|
|
downloaded = 0
|
|
for data in response.iter_content(block_size):
|
|
f.write(data)
|
|
downloaded += len(data)
|
|
if total_size > 0:
|
|
progress = int(downloaded / total_size * 100)
|
|
self.progress_signal.emit(progress)
|
|
|
|
logger.info("Download complete, verifying SHA256 hash...")
|
|
|
|
# Verify SHA256 hash
|
|
if not verify_ytdlp_sha256(exe_path, YTDLP_DOWNLOAD_URL):
|
|
# Hash verification failed - delete the downloaded file
|
|
logger.error("SHA256 verification failed! Removing downloaded file.")
|
|
if Path(exe_path).exists():
|
|
Path(exe_path).unlink()
|
|
self.finished_signal.emit(
|
|
False,
|
|
"SHA256 verification failed. The downloaded file may be corrupted or tampered with."
|
|
)
|
|
return
|
|
|
|
# Make executable on macOS and Linux
|
|
if OS_NAME != "Windows":
|
|
os.chmod(exe_path, 0o755)
|
|
|
|
logger.info("yt-dlp downloaded and verified successfully!")
|
|
self.finished_signal.emit(True, str(exe_path))
|
|
|
|
except Exception as e:
|
|
logger.exception(f"Error downloading yt-dlp: {e}")
|
|
self.finished_signal.emit(False, str(e))
|
|
|
|
|
|
class YtdlpSetupDialog(QDialog):
|
|
setup_complete = Signal(str) # Signal emitting the path to yt-dlp
|
|
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle(_("ytdlp_setup.required_title"))
|
|
self.setMinimumWidth(520)
|
|
self.setMinimumHeight(350)
|
|
self.resize(520, 380)
|
|
|
|
# Set the window icon to match the main app
|
|
if parent and parent.windowIcon():
|
|
self.setWindowIcon(parent.windowIcon())
|
|
else:
|
|
# icon_path logic moved to src\utils\ytsage_constants.py
|
|
icon_path = ICON_PATH
|
|
if Path.exists(icon_path):
|
|
self.setWindowIcon(QIcon(str(icon_path)))
|
|
|
|
self.init_ui()
|
|
|
|
# Apply dark theme styling to match app
|
|
self.setStyleSheet(
|
|
"""
|
|
QDialog {
|
|
background-color: #15181b;
|
|
color: #ffffff;
|
|
}
|
|
QLabel {
|
|
color: #cccccc;
|
|
line-height: 1.4;
|
|
}
|
|
QPushButton {
|
|
padding: 10px 20px;
|
|
background-color: #c90000;
|
|
border: none;
|
|
border-radius: 6px;
|
|
color: white;
|
|
font-weight: bold;
|
|
font-size: 13px;
|
|
min-width: 120px;
|
|
min-height: 20px;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #a50000;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #800000;
|
|
}
|
|
QPushButton:disabled {
|
|
background-color: #666666;
|
|
color: #999999;
|
|
}
|
|
QProgressBar {
|
|
border: 2px solid #1d1e22;
|
|
border-radius: 6px;
|
|
text-align: center;
|
|
color: white;
|
|
background-color: #1d1e22;
|
|
height: 25px;
|
|
font-weight: bold;
|
|
}
|
|
QProgressBar::chunk {
|
|
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0,
|
|
stop: 0 #e60000, stop: 0.5 #ff3333, stop: 1 #c90000);
|
|
border-radius: 4px;
|
|
margin: 1px;
|
|
}
|
|
QRadioButton {
|
|
color: #ffffff;
|
|
spacing: 10px;
|
|
padding: 8px;
|
|
font-size: 13px;
|
|
}
|
|
QRadioButton::indicator {
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 9px;
|
|
}
|
|
QRadioButton::indicator:unchecked {
|
|
border: 2px solid #666666;
|
|
background: #1d1e22;
|
|
}
|
|
QRadioButton::indicator:checked {
|
|
border: 2px solid #c90000;
|
|
background: #c90000;
|
|
}
|
|
"""
|
|
)
|
|
|
|
def init_ui(self) -> None:
|
|
layout = QVBoxLayout()
|
|
layout.setSpacing(15)
|
|
layout.setContentsMargins(25, 25, 25, 25)
|
|
|
|
# Header title
|
|
title_label = QLabel(_("ytdlp_setup.required_title"))
|
|
title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #ffffff; padding: 5px 0;")
|
|
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
layout.addWidget(title_label)
|
|
|
|
# Information label with improved styling
|
|
# os_name logic moved to src\utils\ytsage_constants.py
|
|
|
|
info_label = QLabel(
|
|
_("ytdlp_setup.description", os_name=OS_FULL_NAME)
|
|
)
|
|
info_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
info_label.setWordWrap(True)
|
|
info_label.setStyleSheet("font-size: 13px; color: #cccccc; padding: 5px; line-height: 1.4;")
|
|
layout.addWidget(info_label)
|
|
|
|
# Radio buttons with minimal spacing
|
|
option_widget = QWidget()
|
|
option_layout = QVBoxLayout(option_widget)
|
|
option_layout.setSpacing(8)
|
|
option_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
self.auto_radio = QRadioButton(_("ytdlp_setup.option_auto"))
|
|
self.auto_radio.setChecked(True)
|
|
self.manual_radio = QRadioButton(_("ytdlp_setup.option_manual"))
|
|
|
|
option_layout.addWidget(self.auto_radio)
|
|
option_layout.addWidget(self.manual_radio)
|
|
layout.addWidget(option_widget)
|
|
|
|
# Progress bar with proper sizing
|
|
self.progress_bar = QProgressBar()
|
|
self.progress_bar.setVisible(False)
|
|
self.progress_bar.setFixedHeight(20) # Fixed height for consistency
|
|
self.progress_bar.setStyleSheet(
|
|
"""
|
|
QProgressBar {
|
|
border: 1px solid #3d3d3d;
|
|
border-radius: 8px;
|
|
background-color: #1d1e22;
|
|
text-align: center;
|
|
color: #ffffff;
|
|
font-size: 12px;
|
|
font-weight: bold;
|
|
height: 20px;
|
|
}
|
|
QProgressBar::chunk {
|
|
background-color: #c90000;
|
|
border-radius: 6px;
|
|
margin: 1px;
|
|
}
|
|
"""
|
|
)
|
|
layout.addWidget(self.progress_bar)
|
|
|
|
# Status label with better spacing
|
|
self.status_label = QLabel("")
|
|
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
self.status_label.setStyleSheet("font-size: 12px; color: #cccccc; padding: 8px 0;")
|
|
self.status_label.setWordWrap(True)
|
|
layout.addWidget(self.status_label)
|
|
|
|
# Add stretch to push buttons to bottom
|
|
layout.addStretch()
|
|
|
|
# Button layout with improved spacing
|
|
button_layout = QHBoxLayout()
|
|
button_layout.setSpacing(15)
|
|
button_layout.setContentsMargins(0, 10, 0, 0) # Add top margin for buttons
|
|
|
|
self.setup_button = QPushButton(_("ytdlp_setup.setup_button"))
|
|
self.setup_button.clicked.connect(self.setup_ytdlp)
|
|
|
|
self.cancel_button = QPushButton(_("buttons.cancel"))
|
|
self.cancel_button.clicked.connect(self.reject)
|
|
|
|
button_layout.addWidget(self.setup_button)
|
|
button_layout.addWidget(self.cancel_button)
|
|
layout.addLayout(button_layout)
|
|
|
|
self.setLayout(layout)
|
|
|
|
def setup_ytdlp(self) -> None:
|
|
if self.auto_radio.isChecked():
|
|
self.download_ytdlp()
|
|
else:
|
|
self.select_ytdlp_path()
|
|
|
|
def download_ytdlp(self) -> None:
|
|
self.progress_bar.setVisible(True)
|
|
self.progress_bar.setValue(0)
|
|
self.status_label.setText(_("ytdlp_setup.downloading"))
|
|
self.setup_button.setEnabled(False)
|
|
self.cancel_button.setEnabled(False)
|
|
|
|
self.download_thread = DownloadYtdlpThread()
|
|
self.download_thread.progress_signal.connect(self.update_progress)
|
|
self.download_thread.finished_signal.connect(self.download_finished)
|
|
self.download_thread.start()
|
|
|
|
def update_progress(self, value) -> None:
|
|
self.progress_bar.setValue(value)
|
|
|
|
def download_finished(self, success, result) -> None:
|
|
self.setup_button.setEnabled(True)
|
|
self.cancel_button.setEnabled(True)
|
|
|
|
if success:
|
|
self.status_label.setText(_("ytdlp_setup.success"))
|
|
self.setup_complete.emit(result)
|
|
self.accept()
|
|
else:
|
|
self.status_label.setText(_("ytdlp_setup.error", error=result))
|
|
error_dialog = QMessageBox(self)
|
|
error_dialog.setIcon(QMessageBox.Icon.Critical)
|
|
error_dialog.setWindowTitle(_("ytdlp_setup.download_failed_title"))
|
|
error_dialog.setText(_("ytdlp_setup.download_failed_message", error=result))
|
|
# Set the window icon to match the main dialog
|
|
error_dialog.setWindowIcon(self.windowIcon())
|
|
error_dialog.setStyleSheet(
|
|
"""
|
|
QMessageBox {
|
|
background-color: #15181b;
|
|
color: #ffffff;
|
|
}
|
|
QLabel {
|
|
color: #ffffff;
|
|
}
|
|
QPushButton {
|
|
padding: 8px 15px;
|
|
background-color: #c90000;
|
|
border: none;
|
|
border-radius: 4px;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #a50000;
|
|
}
|
|
"""
|
|
)
|
|
error_dialog.exec()
|
|
|
|
def select_ytdlp_path(self) -> None:
|
|
if OS_NAME == "Windows":
|
|
file_filter = _("ytdlp_setup.file_filter_windows")
|
|
else:
|
|
file_filter = _("ytdlp_setup.file_filter_all")
|
|
|
|
# Apply style to QFileDialog
|
|
file_dialog = QFileDialog(self)
|
|
file_dialog.setStyleSheet(
|
|
"""
|
|
QFileDialog {
|
|
background-color: #15181b;
|
|
color: #ffffff;
|
|
}
|
|
QLabel, QCheckBox, QListView, QTreeView, QComboBox, QLineEdit {
|
|
color: #ffffff;
|
|
background-color: #1b2021;
|
|
}
|
|
QPushButton {
|
|
padding: 8px 15px;
|
|
background-color: #c90000;
|
|
border: none;
|
|
border-radius: 4px;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #a50000;
|
|
}
|
|
"""
|
|
)
|
|
|
|
file_path, _ = file_dialog.getOpenFileName(
|
|
self, _("ytdlp_setup.select_executable_title"), "", file_filter
|
|
)
|
|
|
|
if file_path:
|
|
logger.debug(f"User selected file: {file_path}")
|
|
# Verify the selected file
|
|
try:
|
|
# Extra logic moved to src\utils\ytsage_constants.py
|
|
# Try to run yt-dlp --version
|
|
logger.debug(f"Verifying file with --version command")
|
|
result = subprocess.run(
|
|
[file_path, "--version"], capture_output=True, text=True, check=False, creationflags=SUBPROCESS_CREATIONFLAGS
|
|
)
|
|
logger.debug(f"Version check result: {result.returncode}, Output: {result.stdout.strip()}")
|
|
|
|
if result.returncode == 0:
|
|
# File is valid, copy it to our app's bin directory
|
|
try:
|
|
# Ensure the bin directory exists
|
|
logger.debug(f"Install directory: {APP_BIN_DIR}")
|
|
|
|
# Determine the target filename based on OS
|
|
target_path = YTDLP_APP_BIN_PATH
|
|
logger.debug(f"Target path: {target_path}")
|
|
|
|
# Copy the file
|
|
shutil.copy2(file_path, target_path)
|
|
logger.debug(f"File copied successfully")
|
|
|
|
# Set executable permissions on Unix systems
|
|
if OS_NAME != "Windows":
|
|
os.chmod(target_path, 0o755)
|
|
logger.debug(f"Permissions set on Unix system")
|
|
|
|
# Return the path of the copied file
|
|
self.status_label.setText(_("ytdlp_setup.copied_to", path=target_path))
|
|
logger.debug(f"Emitting setup_complete signal with path: {target_path}")
|
|
self.setup_complete.emit(target_path)
|
|
self.accept()
|
|
except Exception as copy_error:
|
|
logger.debug(f"Error copying file: {copy_error}", exc_info=True)
|
|
error_dialog = QMessageBox(self)
|
|
error_dialog.setIcon(QMessageBox.Icon.Critical)
|
|
error_dialog.setWindowTitle(_("ytdlp_setup.setup_error_title"))
|
|
error_dialog.setText(_("ytdlp_setup.copy_error", error=copy_error))
|
|
error_dialog.setStyleSheet(
|
|
"""
|
|
QMessageBox {
|
|
background-color: #15181b;
|
|
color: #ffffff;
|
|
}
|
|
QLabel {
|
|
color: #ffffff;
|
|
}
|
|
QPushButton {
|
|
padding: 8px 15px;
|
|
background-color: #c90000;
|
|
border: none;
|
|
border-radius: 4px;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #a50000;
|
|
}
|
|
"""
|
|
)
|
|
error_dialog.exec()
|
|
else:
|
|
logger.debug(f"File verification failed with return code: {result.returncode}")
|
|
error_dialog = QMessageBox(self)
|
|
error_dialog.setIcon(QMessageBox.Icon.Warning)
|
|
error_dialog.setWindowTitle(_("ytdlp_setup.invalid_executable_title"))
|
|
error_dialog.setText(_("ytdlp_setup.invalid_executable_message"))
|
|
error_dialog.setStyleSheet(
|
|
"""
|
|
QMessageBox {
|
|
background-color: #15181b;
|
|
color: #ffffff;
|
|
}
|
|
QLabel {
|
|
color: #ffffff;
|
|
}
|
|
QPushButton {
|
|
padding: 8px 15px;
|
|
background-color: #c90000;
|
|
border: none;
|
|
border-radius: 4px;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #a50000;
|
|
}
|
|
"""
|
|
)
|
|
error_dialog.exec()
|
|
except Exception as e:
|
|
logger.debug(f"Exception during verification: {e}", exc_info=True)
|
|
error_dialog = QMessageBox(self)
|
|
error_dialog.setIcon(QMessageBox.Icon.Critical)
|
|
error_dialog.setWindowTitle(_("main_ui.error_title"))
|
|
error_dialog.setText(_("ytdlp_setup.verify_error", error=e))
|
|
error_dialog.setStyleSheet(
|
|
"""
|
|
QMessageBox {
|
|
background-color: #15181b;
|
|
color: #ffffff;
|
|
}
|
|
QLabel {
|
|
color: #ffffff;
|
|
}
|
|
QPushButton {
|
|
padding: 8px 15px;
|
|
background-color: #c90000;
|
|
border: none;
|
|
border-radius: 4px;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #a50000;
|
|
}
|
|
"""
|
|
)
|
|
error_dialog.exec()
|
|
|
|
|
|
def check_ytdlp_binary() -> Optional[Path]:
|
|
"""
|
|
Check if yt-dlp binary exists in the app's bin directory ONLY.
|
|
We now ignore system PATH and only use our managed binary.
|
|
Returns:
|
|
Path or None: Path to yt-dlp binary if found in app bin, None otherwise
|
|
"""
|
|
exe_path = YTDLP_APP_BIN_PATH
|
|
if exe_path.exists():
|
|
# Make sure it's executable on Unix systems
|
|
if OS_NAME != "Windows" and not os.access(exe_path, os.X_OK):
|
|
try:
|
|
os.chmod(exe_path, 0o755)
|
|
logger.info(f"Fixed permissions on yt-dlp at {exe_path}")
|
|
except Exception as e:
|
|
logger.exception(f"Could not set executable permissions on {exe_path}: {e}")
|
|
logger.info(f"Found yt-dlp in app bin directory: {exe_path}")
|
|
return exe_path
|
|
|
|
# Binary not found in app directory - return None to trigger setup
|
|
logger.warning(f"yt-dlp binary not found in app bin directory: {exe_path}")
|
|
return None
|
|
|
|
|
|
def check_ytdlp_installed() -> bool:
|
|
"""
|
|
Check if yt-dlp is installed and accessible.
|
|
Returns:
|
|
bool: True if yt-dlp is found and working, False otherwise
|
|
"""
|
|
try:
|
|
ytdlp_path = check_ytdlp_binary()
|
|
if ytdlp_path:
|
|
# Try to run yt-dlp --version to verify it's working
|
|
try:
|
|
# Extra logic moved to src\utils\ytsage_constants.py
|
|
result = subprocess.run(
|
|
[ytdlp_path, "--version"], capture_output=True, text=True, timeout=5, creationflags=SUBPROCESS_CREATIONFLAGS
|
|
)
|
|
return result.returncode == 0
|
|
except Exception:
|
|
return False
|
|
return False
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def get_yt_dlp_path() -> Path:
|
|
"""
|
|
Get the yt-dlp path, either from the app's bin directory or system PATH.
|
|
This replaces the function in ytsage_utils.py.
|
|
Returns:
|
|
str: Path to yt-dlp binary
|
|
"""
|
|
# First check if we have yt-dlp in our app's bin directory or system PATH
|
|
ytdlp_path = check_ytdlp_binary()
|
|
if ytdlp_path:
|
|
logger.info(f"Using yt-dlp from: {ytdlp_path}")
|
|
return ytdlp_path
|
|
|
|
# If not found anywhere, fall back to the command name as a last resort
|
|
logger.info("yt-dlp not found in app directory or PATH, falling back to command name")
|
|
return "yt-dlp" # type: ignore[return-value]
|
|
|
|
|
|
def setup_ytdlp(parent_widget=None):
|
|
"""
|
|
Show the yt-dlp setup dialog and handle the result.
|
|
Returns:
|
|
str: Path to yt-dlp binary
|
|
"""
|
|
logger.debug("Starting yt-dlp setup dialog")
|
|
dialog = YtdlpSetupDialog(parent_widget)
|
|
|
|
# Store the setup result from the signal
|
|
setup_result = {"path": None}
|
|
|
|
def on_setup_complete(path) -> None:
|
|
logger.debug(f"Received setup_complete signal with path: {path}")
|
|
setup_result["path"] = path
|
|
|
|
# Connect to the setup_complete signal
|
|
dialog.setup_complete.connect(on_setup_complete)
|
|
|
|
# Show the dialog
|
|
result = dialog.exec()
|
|
logger.debug(f"Dialog result: {result} (Accepted={QDialog.DialogCode.Accepted})")
|
|
|
|
if result == QDialog.DialogCode.Accepted:
|
|
# First check if we received a path from the signal
|
|
if setup_result["path"]:
|
|
path_obj = Path(setup_result["path"]) if isinstance(setup_result["path"], str) else setup_result["path"]
|
|
if path_obj.exists():
|
|
logger.debug(f"Using path from signal: {setup_result['path']}")
|
|
return str(setup_result["path"])
|
|
|
|
# Get the expected path for verification as fallback
|
|
expected_path = YTDLP_APP_BIN_PATH
|
|
logger.debug(f"Expected yt-dlp path: {expected_path}")
|
|
|
|
# Verify the path exists after dialog is accepted
|
|
if expected_path.exists():
|
|
logger.debug(f"yt-dlp successfully found at expected path: {expected_path}")
|
|
return str(expected_path)
|
|
else:
|
|
logger.debug(f"Expected path does not exist, trying alternate detection")
|
|
# Try to use the get_yt_dlp_path function to find yt-dlp elsewhere
|
|
yt_dlp_path = get_yt_dlp_path()
|
|
logger.debug(f"Alternate detection result: {yt_dlp_path}")
|
|
if yt_dlp_path != "yt-dlp":
|
|
path_obj = Path(yt_dlp_path) if isinstance(yt_dlp_path, str) else yt_dlp_path
|
|
if path_obj.exists():
|
|
logger.debug(f"yt-dlp found at alternate location: {yt_dlp_path}")
|
|
return str(yt_dlp_path)
|
|
|
|
# Something went wrong, show an error message
|
|
logger.debug(f"Setup failed, showing error dialog")
|
|
if parent_widget:
|
|
error_dialog = QMessageBox(parent_widget)
|
|
error_dialog.setIcon(QMessageBox.Icon.Warning)
|
|
error_dialog.setWindowTitle(_("ytdlp_setup.setup_failed_title"))
|
|
error_dialog.setText(_("ytdlp_setup.setup_failed_message"))
|
|
# Set the window icon to match the parent
|
|
error_dialog.setWindowIcon(parent_widget.windowIcon())
|
|
error_dialog.setStyleSheet(
|
|
"""
|
|
QMessageBox {
|
|
background-color: #15181b;
|
|
color: #ffffff;
|
|
}
|
|
QLabel {
|
|
color: #ffffff;
|
|
}
|
|
QPushButton {
|
|
padding: 8px 15px;
|
|
background-color: #c90000;
|
|
border: none;
|
|
border-radius: 4px;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #a50000;
|
|
}
|
|
"""
|
|
)
|
|
error_dialog.exec()
|
|
logger.warning(f"yt-dlp setup failed, path does not exist: {expected_path}")
|
|
else:
|
|
logger.debug("User cancelled the setup dialog")
|
|
|
|
# User cancelled or setup failed, return the fallback command
|
|
logger.debug("Returning fallback command 'yt-dlp'")
|
|
return "yt-dlp"
|