v3.5.0
This commit is contained in:
@@ -348,6 +348,282 @@ class DownloadThread(QThread):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.error_signal.emit(str(e))
|
self.error_signal.emit(str(e))
|
||||||
|
|
||||||
|
class YTDLPUpdateDialog(QDialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle('Update yt-dlp')
|
||||||
|
self.setMinimumWidth(400)
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Version info and message
|
||||||
|
self.version_label = QLabel()
|
||||||
|
self.version_label.setWordWrap(True)
|
||||||
|
layout.addWidget(self.version_label)
|
||||||
|
|
||||||
|
self.message_label = QLabel(
|
||||||
|
"Would you like to update yt-dlp to the latest version?\n"
|
||||||
|
"This will download and install the latest yt-dlp executable."
|
||||||
|
)
|
||||||
|
self.message_label.setWordWrap(True)
|
||||||
|
layout.addWidget(self.message_label)
|
||||||
|
|
||||||
|
# Progress bar (hidden initially)
|
||||||
|
self.progress_bar = QProgressBar()
|
||||||
|
self.progress_bar.setVisible(False)
|
||||||
|
layout.addWidget(self.progress_bar)
|
||||||
|
|
||||||
|
# Status label
|
||||||
|
self.status_label = QLabel()
|
||||||
|
self.status_label.setWordWrap(True)
|
||||||
|
layout.addWidget(self.status_label)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
self.update_btn = QPushButton("Update yt-dlp")
|
||||||
|
self.update_btn.clicked.connect(self.start_update)
|
||||||
|
self.update_btn.setEnabled(False) # Disabled until version check
|
||||||
|
|
||||||
|
self.close_btn = QPushButton("Close")
|
||||||
|
self.close_btn.clicked.connect(self.close)
|
||||||
|
|
||||||
|
button_layout.addWidget(self.update_btn)
|
||||||
|
button_layout.addWidget(self.close_btn)
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
# Style the dialog
|
||||||
|
self.setStyleSheet("""
|
||||||
|
QDialog {
|
||||||
|
background-color: #2b2b2b;
|
||||||
|
}
|
||||||
|
QLabel {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
QPushButton {
|
||||||
|
padding: 8px 15px;
|
||||||
|
background-color: #ff0000;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #cc0000;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
background-color: #666666;
|
||||||
|
}
|
||||||
|
QProgressBar {
|
||||||
|
border: 2px solid #3d3d3d;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
QProgressBar::chunk {
|
||||||
|
background-color: #ff0000;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Check versions when dialog opens
|
||||||
|
self.check_versions()
|
||||||
|
|
||||||
|
def check_versions(self):
|
||||||
|
threading.Thread(target=self._check_versions_thread, daemon=True).start()
|
||||||
|
|
||||||
|
def _check_versions_thread(self):
|
||||||
|
try:
|
||||||
|
# Get current version with proper path
|
||||||
|
yt_dlp_path = self.get_yt_dlp_path()
|
||||||
|
if os.path.exists(yt_dlp_path):
|
||||||
|
try:
|
||||||
|
# Use the full path to yt-dlp executable
|
||||||
|
result = subprocess.run([yt_dlp_path, '--version'],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == 'win32' else 0)
|
||||||
|
if result.returncode == 0:
|
||||||
|
current_version = result.stdout.strip()
|
||||||
|
else:
|
||||||
|
# Try using pip to get version as fallback
|
||||||
|
try:
|
||||||
|
import yt_dlp
|
||||||
|
current_version = yt_dlp.version.__version__
|
||||||
|
except:
|
||||||
|
current_version = "Not installed"
|
||||||
|
except (subprocess.SubprocessError, subprocess.TimeoutExpired):
|
||||||
|
# Try using pip to get version as fallback
|
||||||
|
try:
|
||||||
|
import yt_dlp
|
||||||
|
current_version = yt_dlp.version.__version__
|
||||||
|
except:
|
||||||
|
current_version = "Not installed"
|
||||||
|
else:
|
||||||
|
# Try using pip to get version as fallback
|
||||||
|
try:
|
||||||
|
import yt_dlp
|
||||||
|
current_version = yt_dlp.version.__version__
|
||||||
|
except:
|
||||||
|
current_version = "Not installed"
|
||||||
|
|
||||||
|
# Get latest version from GitHub API with timeout
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
"https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest",
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
latest_version = response.json()["tag_name"]
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Could not fetch latest version: {str(e)}")
|
||||||
|
|
||||||
|
self.version_label.setText(
|
||||||
|
f"Current version: {current_version}\n"
|
||||||
|
f"Latest version: {latest_version}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if current_version == "Not installed" or current_version != latest_version:
|
||||||
|
self.update_btn.setEnabled(True)
|
||||||
|
self.message_label.setText("An update is available!")
|
||||||
|
else:
|
||||||
|
self.message_label.setText("You have the latest version installed.")
|
||||||
|
self.update_btn.setEnabled(False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.version_label.setText("Could not check versions")
|
||||||
|
self.message_label.setText(f"Error: {str(e)}")
|
||||||
|
self.update_btn.setEnabled(True)
|
||||||
|
|
||||||
|
def get_yt_dlp_path(self):
|
||||||
|
"""Get the appropriate yt-dlp path based on platform and deployment method"""
|
||||||
|
try:
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
# For macOS .app bundle
|
||||||
|
if 'Contents/MacOS' in sys.executable:
|
||||||
|
# Inside .app bundle
|
||||||
|
return os.path.join(os.path.dirname(sys.executable), 'yt-dlp')
|
||||||
|
else:
|
||||||
|
# Fallback to user's home directory for macOS
|
||||||
|
base_path = os.path.expanduser('~/Library/Application Support/YTSage')
|
||||||
|
os.makedirs(base_path, exist_ok=True)
|
||||||
|
return os.path.join(base_path, 'yt-dlp')
|
||||||
|
elif sys.platform == 'win32':
|
||||||
|
# For Windows executable
|
||||||
|
app_data = os.getenv('APPDATA')
|
||||||
|
if app_data:
|
||||||
|
base_path = os.path.join(app_data, 'YTSage')
|
||||||
|
else:
|
||||||
|
base_path = os.path.dirname(sys.executable)
|
||||||
|
os.makedirs(base_path, exist_ok=True)
|
||||||
|
return os.path.join(base_path, 'yt-dlp.exe')
|
||||||
|
else:
|
||||||
|
# For Linux AppImage or binary
|
||||||
|
if 'APPIMAGE' in os.environ:
|
||||||
|
# Inside AppImage
|
||||||
|
xdg_data = os.getenv('XDG_DATA_HOME', os.path.expanduser('~/.local/share'))
|
||||||
|
base_path = os.path.join(xdg_data, 'YTSage')
|
||||||
|
else:
|
||||||
|
base_path = os.path.dirname(sys.executable)
|
||||||
|
os.makedirs(base_path, exist_ok=True)
|
||||||
|
return os.path.join(base_path, 'yt-dlp')
|
||||||
|
else:
|
||||||
|
# For development/script mode
|
||||||
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
'yt-dlp.exe' if sys.platform == 'win32' else 'yt-dlp')
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error determining yt-dlp path: {e}")
|
||||||
|
# Fallback to current directory
|
||||||
|
return os.path.join(os.getcwd(), 'yt-dlp.exe' if sys.platform == 'win32' else 'yt-dlp')
|
||||||
|
|
||||||
|
def start_update(self):
|
||||||
|
self.update_btn.setEnabled(False)
|
||||||
|
self.progress_bar.setVisible(True)
|
||||||
|
self.status_label.setText("Starting update...")
|
||||||
|
|
||||||
|
# Start update in a separate thread
|
||||||
|
threading.Thread(target=self._update_thread, daemon=True).start()
|
||||||
|
|
||||||
|
def _update_thread(self):
|
||||||
|
temp_path = None
|
||||||
|
try:
|
||||||
|
# Create necessary directories
|
||||||
|
target_path = self.get_yt_dlp_path()
|
||||||
|
os.makedirs(os.path.dirname(target_path), exist_ok=True)
|
||||||
|
|
||||||
|
# Determine platform and get URLs
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
url = 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe'
|
||||||
|
else:
|
||||||
|
url = 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp'
|
||||||
|
|
||||||
|
temp_path = target_path + '.download'
|
||||||
|
|
||||||
|
# Download with proper error handling
|
||||||
|
try:
|
||||||
|
response = requests.get(url, stream=True, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
total_size = int(response.headers.get('content-length', 0))
|
||||||
|
block_size = 8192 # Increased for better performance
|
||||||
|
|
||||||
|
with open(temp_path, 'wb') as f:
|
||||||
|
for data in response.iter_content(block_size):
|
||||||
|
if data: # Filter out keep-alive chunks
|
||||||
|
f.write(data)
|
||||||
|
progress = int((f.tell() * 100) / total_size) if total_size > 0 else 0
|
||||||
|
self.progress_bar.setValue(progress)
|
||||||
|
self.status_label.setText(f"Downloading: {progress}%")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
raise Exception(f"Download failed: {str(e)}")
|
||||||
|
|
||||||
|
# Set proper permissions
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
try:
|
||||||
|
os.chmod(temp_path, 0o755)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Could not set executable permissions: {e}")
|
||||||
|
|
||||||
|
# Handle file replacement
|
||||||
|
try:
|
||||||
|
if os.path.exists(target_path):
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
# Windows-specific file replacement
|
||||||
|
import ctypes
|
||||||
|
if not ctypes.windll.kernel32.MoveFileExW(target_path, None, 4): # MOVEFILE_DELAY_UNTIL_REBOOT
|
||||||
|
os.remove(target_path)
|
||||||
|
else:
|
||||||
|
os.remove(target_path)
|
||||||
|
except PermissionError:
|
||||||
|
raise Exception("Cannot update while yt-dlp is in use. Please close any active downloads and try again.")
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Error removing existing file: {str(e)}")
|
||||||
|
|
||||||
|
# Move temporary file to final location
|
||||||
|
try:
|
||||||
|
os.rename(temp_path, target_path)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Error moving new file into place: {str(e)}")
|
||||||
|
|
||||||
|
self.status_label.setText("yt-dlp updated successfully!")
|
||||||
|
self.close_btn.setText("Done")
|
||||||
|
|
||||||
|
# Refresh version info
|
||||||
|
self.check_versions()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.status_label.setText(f"Error updating yt-dlp: {str(e)}")
|
||||||
|
finally:
|
||||||
|
# Clean up temp file if it exists
|
||||||
|
if temp_path and os.path.exists(temp_path):
|
||||||
|
try:
|
||||||
|
os.remove(temp_path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.update_btn.setEnabled(True)
|
||||||
|
|
||||||
class YTSage(QMainWindow):
|
class YTSage(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -355,7 +631,7 @@ class YTSage(QMainWindow):
|
|||||||
if not self.check_ffmpeg():
|
if not self.check_ffmpeg():
|
||||||
self.show_ffmpeg_dialog()
|
self.show_ffmpeg_dialog()
|
||||||
|
|
||||||
self.version = "3.0.0"
|
self.version = "3.5.0"
|
||||||
self.check_for_updates() # Check for updates on startup
|
self.check_for_updates() # Check for updates on startup
|
||||||
self.config_file = Path.home() / '.ytsage_config.json'
|
self.config_file = Path.home() / '.ytsage_config.json'
|
||||||
self.load_saved_path()
|
self.load_saved_path()
|
||||||
@@ -363,9 +639,8 @@ class YTSage(QMainWindow):
|
|||||||
self.signals = SignalManager()
|
self.signals = SignalManager()
|
||||||
self.download_paused = False
|
self.download_paused = False
|
||||||
self.current_download = None
|
self.current_download = None
|
||||||
self.log_window = None
|
|
||||||
self.show_log = False
|
|
||||||
self.download_cancelled = False
|
self.download_cancelled = False
|
||||||
|
self.save_thumbnail = False
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QMainWindow {
|
QMainWindow {
|
||||||
@@ -558,7 +833,7 @@ class YTSage(QMainWindow):
|
|||||||
print(f"Error saving settings: {e}")
|
print(f"Error saving settings: {e}")
|
||||||
|
|
||||||
def init_ui(self):
|
def init_ui(self):
|
||||||
self.setWindowTitle('YTSage v3.0.0')
|
self.setWindowTitle('YTSage v3.5.0')
|
||||||
self.setMinimumSize(900, 600)
|
self.setMinimumSize(900, 600)
|
||||||
|
|
||||||
# Main widget and layout
|
# Main widget and layout
|
||||||
@@ -660,6 +935,33 @@ class YTSage(QMainWindow):
|
|||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
subtitle_layout.addWidget(self.subtitle_combo)
|
subtitle_layout.addWidget(self.subtitle_combo)
|
||||||
|
|
||||||
|
# Add thumbnail save toggle button
|
||||||
|
self.save_thumbnail_btn = QPushButton('Save Thumbnail')
|
||||||
|
self.save_thumbnail_btn.setCheckable(True)
|
||||||
|
self.save_thumbnail_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #363636;
|
||||||
|
border: 2px solid #3d3d3d;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
QPushButton:checked {
|
||||||
|
background-color: #ff0000;
|
||||||
|
border-color: #cc0000;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #444444;
|
||||||
|
}
|
||||||
|
QPushButton:checked:hover {
|
||||||
|
background-color: #cc0000;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.save_thumbnail_btn.clicked.connect(self.toggle_save_thumbnail)
|
||||||
|
|
||||||
|
# Add the button to the subtitle layout
|
||||||
|
subtitle_layout.addWidget(self.save_thumbnail_btn)
|
||||||
|
|
||||||
subtitle_layout.addStretch()
|
subtitle_layout.addStretch()
|
||||||
|
|
||||||
# Add all info widgets to the video info layout
|
# Add all info widgets to the video info layout
|
||||||
@@ -801,11 +1103,6 @@ class YTSage(QMainWindow):
|
|||||||
self.cancel_btn.setVisible(False)
|
self.cancel_btn.setVisible(False)
|
||||||
self.cancel_btn.setStyleSheet(button_style)
|
self.cancel_btn.setStyleSheet(button_style)
|
||||||
|
|
||||||
self.log_btn = QPushButton('yt-dlp Log')
|
|
||||||
self.log_btn.setCheckable(True)
|
|
||||||
self.log_btn.clicked.connect(self.toggle_log_window)
|
|
||||||
self.log_btn.setStyleSheet(button_style)
|
|
||||||
|
|
||||||
self.custom_cmd_btn = QPushButton('Custom Command')
|
self.custom_cmd_btn = QPushButton('Custom Command')
|
||||||
self.custom_cmd_btn.clicked.connect(self.show_custom_command)
|
self.custom_cmd_btn.clicked.connect(self.show_custom_command)
|
||||||
self.custom_cmd_btn.setStyleSheet(button_style)
|
self.custom_cmd_btn.setStyleSheet(button_style)
|
||||||
@@ -815,7 +1112,6 @@ class YTSage(QMainWindow):
|
|||||||
self.update_ytdlp_btn.setStyleSheet(button_style)
|
self.update_ytdlp_btn.setStyleSheet(button_style)
|
||||||
|
|
||||||
# Add all buttons to layout
|
# Add all buttons to layout
|
||||||
download_layout.addWidget(self.log_btn)
|
|
||||||
download_layout.addWidget(self.custom_cmd_btn)
|
download_layout.addWidget(self.custom_cmd_btn)
|
||||||
download_layout.addWidget(self.update_ytdlp_btn)
|
download_layout.addWidget(self.update_ytdlp_btn)
|
||||||
download_layout.addWidget(self.path_input)
|
download_layout.addWidget(self.path_input)
|
||||||
@@ -910,7 +1206,7 @@ class YTSage(QMainWindow):
|
|||||||
raise Exception("Could not extract basic video information")
|
raise Exception("Could not extract basic video information")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"First extraction failed: {str(e)}")
|
print(f"First extraction failed: {str(e)}")
|
||||||
raise Exception("Could not extract video information")
|
raise Exception("Could not extract video information, please check your link")
|
||||||
|
|
||||||
self.signals.update_status.emit("Analyzing (40%)... Extracting detailed info")
|
self.signals.update_status.emit("Analyzing (40%)... Extracting detailed info")
|
||||||
# Configure options for detailed extraction
|
# Configure options for detailed extraction
|
||||||
@@ -1077,9 +1373,10 @@ class YTSage(QMainWindow):
|
|||||||
|
|
||||||
def download_thumbnail(self, url):
|
def download_thumbnail(self, url):
|
||||||
try:
|
try:
|
||||||
|
self.thumbnail_url = url # Store the URL for later use
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
image = Image.open(BytesIO(response.content))
|
self.thumbnail_image = Image.open(BytesIO(response.content)) # Store the image
|
||||||
image = image.resize((320, 180), Image.Resampling.LANCZOS)
|
image = self.thumbnail_image.resize((320, 180), Image.Resampling.LANCZOS)
|
||||||
img_byte_arr = BytesIO()
|
img_byte_arr = BytesIO()
|
||||||
image.save(img_byte_arr, format='PNG')
|
image.save(img_byte_arr, format='PNG')
|
||||||
img_byte_arr = img_byte_arr.getvalue()
|
img_byte_arr = img_byte_arr.getvalue()
|
||||||
@@ -1214,6 +1511,10 @@ class YTSage(QMainWindow):
|
|||||||
|
|
||||||
self.download_thread.start()
|
self.download_thread.start()
|
||||||
|
|
||||||
|
# Add thumbnail download if enabled
|
||||||
|
if self.save_thumbnail and hasattr(self, 'thumbnail_url'):
|
||||||
|
self.download_thumbnail_file(self.thumbnail_url, path)
|
||||||
|
|
||||||
def download_finished(self):
|
def download_finished(self):
|
||||||
self.download_btn.setEnabled(True)
|
self.download_btn.setEnabled(True)
|
||||||
self.pause_btn.setVisible(False)
|
self.pause_btn.setVisible(False)
|
||||||
@@ -1243,17 +1544,6 @@ class YTSage(QMainWindow):
|
|||||||
self.pause_btn.setText('Pause')
|
self.pause_btn.setText('Pause')
|
||||||
self.signals.update_status.emit("Download resumed")
|
self.signals.update_status.emit("Download resumed")
|
||||||
|
|
||||||
def toggle_log_window(self):
|
|
||||||
if self.log_btn.isChecked():
|
|
||||||
if not self.log_window:
|
|
||||||
self.log_window = LogWindow(self)
|
|
||||||
self.log_window.show()
|
|
||||||
self.show_log = True
|
|
||||||
else:
|
|
||||||
if self.log_window:
|
|
||||||
self.log_window.hide()
|
|
||||||
self.show_log = False
|
|
||||||
|
|
||||||
def check_for_updates(self):
|
def check_for_updates(self):
|
||||||
try:
|
try:
|
||||||
# Get the latest release info from GitHub
|
# Get the latest release info from GitHub
|
||||||
@@ -1358,55 +1648,30 @@ class YTSage(QMainWindow):
|
|||||||
self.url_input.setText(clipboard.text())
|
self.url_input.setText(clipboard.text())
|
||||||
|
|
||||||
def update_ytdlp(self):
|
def update_ytdlp(self):
|
||||||
self.signals.update_status.emit("Updating yt-dlp...")
|
dialog = YTDLPUpdateDialog(self)
|
||||||
threading.Thread(target=self._update_ytdlp_thread, daemon=True).start()
|
dialog.exec()
|
||||||
|
|
||||||
def _update_ytdlp_thread(self):
|
def toggle_save_thumbnail(self):
|
||||||
|
self.save_thumbnail = self.save_thumbnail_btn.isChecked()
|
||||||
|
|
||||||
|
def download_thumbnail_file(self, url, path):
|
||||||
try:
|
try:
|
||||||
# Show initial status
|
if not hasattr(self, 'thumbnail_image'):
|
||||||
self.signals.update_status.emit("Updating yt-dlp (0%)...")
|
response = requests.get(url)
|
||||||
|
self.thumbnail_image = Image.open(BytesIO(response.content))
|
||||||
|
|
||||||
# Run pip install with output capture
|
# Create thumbnails directory if it doesn't exist
|
||||||
process = subprocess.Popen(
|
thumbnails_path = os.path.join(path, 'thumbnails')
|
||||||
[sys.executable, "-m", "pip", "install", "--upgrade", "yt-dlp"],
|
os.makedirs(thumbnails_path, exist_ok=True)
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
text=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Track progress through output lines
|
# Save the thumbnail
|
||||||
progress = 0
|
video_title = self.video_info.get('title', 'thumbnail').replace('/', '_')
|
||||||
progress_steps = ['Collecting', 'Downloading', 'Installing', 'Successfully']
|
thumbnail_path = os.path.join(thumbnails_path, f"{video_title}.jpg")
|
||||||
current_step = 0
|
self.thumbnail_image.save(thumbnail_path)
|
||||||
|
|
||||||
while True:
|
self.signals.update_status.emit(f"Thumbnail saved to {thumbnail_path}")
|
||||||
line = process.stdout.readline() or process.stderr.readline()
|
|
||||||
if not line and process.poll() is not None:
|
|
||||||
break
|
|
||||||
|
|
||||||
line = line.strip()
|
|
||||||
if line:
|
|
||||||
# Update progress based on steps
|
|
||||||
if any(step in line for step in progress_steps[current_step:]):
|
|
||||||
current_step = next(i for i, step in enumerate(progress_steps) if step in line)
|
|
||||||
progress = (current_step + 1) * 25
|
|
||||||
self.signals.update_status.emit(f"Updating yt-dlp ({progress}%)...")
|
|
||||||
|
|
||||||
# Get the installed version
|
|
||||||
version_process = subprocess.run(
|
|
||||||
[sys.executable, "-m", "yt_dlp", "--version"],
|
|
||||||
capture_output=True,
|
|
||||||
text=True
|
|
||||||
)
|
|
||||||
version = version_process.stdout.strip()
|
|
||||||
|
|
||||||
self.signals.update_status.emit(f"yt-dlp updated successfully! (Version: {version})")
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
error_msg = e.stderr.decode() if e.stderr else str(e)
|
|
||||||
self.signals.update_status.emit(f"Error updating yt-dlp: {error_msg}")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.signals.update_status.emit(f"Error updating yt-dlp: {str(e)}")
|
self.signals.update_status.emit(f"Error saving thumbnail: {str(e)}")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|||||||
Reference in New Issue
Block a user