Skip to content

Commit 5fbe7fa

Browse files
committed
fix: handle invalid download paths when removable drives are disconnected
1 parent 636986f commit 5fbe7fa

File tree

5 files changed

+135
-21
lines changed

5 files changed

+135
-21
lines changed

core/downloader.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,22 @@ def run(self):
130130

131131
self.log_signal.emit(f"Starting download to: {self.task.folder}")
132132

133-
if not os.path.exists(self.task.folder):
134-
os.makedirs(self.task.folder, exist_ok=True)
135-
self.log_signal.emit(f"Created download directory: {self.task.folder}")
133+
try:
134+
if not os.path.exists(self.task.folder):
135+
os.makedirs(self.task.folder, exist_ok=True)
136+
self.log_signal.emit(f"Created download directory: {self.task.folder}")
137+
except (OSError, PermissionError) as e:
138+
self.status_signal.emit(self.row, "Download Error")
139+
error_msg = f"Cannot access download directory: {self.task.folder}\n"
140+
error_msg += f"Error: {str(e)}\n"
141+
error_msg += "Possible causes:\n"
142+
error_msg += "- Drive not connected (USB/external drive)\n"
143+
error_msg += "- Network drive disconnected\n"
144+
error_msg += "- Insufficient permissions\n"
145+
error_msg += "- Path no longer exists\n\n"
146+
error_msg += "Please select a valid download location in Settings."
147+
self.log_signal.emit(error_msg)
148+
return
136149

137150
if self.task.playlist:
138151
self.status_signal.emit(self.row, "Loading Playlist...")

core/profile.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,15 @@ def remove_profile_picture(self):
102102
self.save_profile()
103103

104104
def get_download_path(self):
105-
return self.data.get("download_path", os.getcwd())
105+
path = self.data.get("download_path", os.getcwd())
106+
if not os.path.exists(path):
107+
fallback = os.path.join(os.path.expanduser("~"), "Downloads")
108+
if not os.path.exists(fallback):
109+
fallback = os.getcwd()
110+
self.data["download_path"] = fallback
111+
self.save_profile()
112+
return fallback
113+
return path
106114

107115
def get_proxy(self):
108116
return self.data.get("proxy", "")

core/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
VERSION = "v5.0.20"
1+
VERSION = "v5.0.21"
22
VERSION_SHORT = "v5.0"
33

44
def get_version(short=False):

ui/main_window.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ def add_scheduled_dialog(self):
159159
dialog = ScheduleAddDialog(self)
160160
dialog.exec_()
161161
def start_queue(self):
162+
download_path = self.user_profile.get_download_path()
163+
if not os.path.exists(download_path):
164+
QMessageBox.warning(self, "Invalid Download Path",
165+
f"Download folder does not exist:\n{download_path}\n\n"
166+
"The folder may have been deleted or the drive may be disconnected.\n"
167+
"Please select a new download location in Settings.")
168+
self.append_log(f"Queue start aborted - invalid path: {download_path}")
169+
return
170+
162171
count_started = 0
163172
for r in range(self.queue_table.rowCount()):
164173
st_item = self.queue_table.item(r, 4)
@@ -170,7 +179,7 @@ def start_queue(self):
170179
playlist = ("playlist" in typ)
171180
current_format = "mp4"
172181
row_idx = r
173-
tsk = DownloadTask(url, self.user_profile.get_default_resolution(), self.user_profile.get_download_path(), self.user_profile.get_proxy(), audio_only=audio, playlist=playlist, output_format=current_format, audio_format=self.user_profile.get_audio_format() if audio else None, audio_quality=self.user_profile.get_audio_quality() if audio else "320", from_queue=True)
182+
tsk = DownloadTask(url, self.user_profile.get_default_resolution(), download_path, self.user_profile.get_proxy(), audio_only=audio, playlist=playlist, output_format=current_format, audio_format=self.user_profile.get_audio_format() if audio else None, audio_quality=self.user_profile.get_audio_quality() if audio else "320", from_queue=True)
174183
self.run_task(tsk, row_idx)
175184
self.queue_table.setItem(r, 4, QTableWidgetItem("Started"))
176185
count_started += 1
@@ -182,6 +191,11 @@ def remove_scheduled_item(self):
182191
for r in sorted(sel, reverse=True):
183192
self.scheduler_table.removeRow(r)
184193
def check_scheduled_downloads(self):
194+
download_path = self.user_profile.get_download_path()
195+
if not os.path.exists(download_path):
196+
self.append_log(f"Scheduled download skipped - invalid path: {download_path}")
197+
return
198+
185199
now = QDateTime.currentDateTime()
186200
for r in range(self.scheduler_table.rowCount()):
187201
dt_str = self.scheduler_table.item(r, 0).text()
@@ -192,16 +206,25 @@ def check_scheduled_downloads(self):
192206
t = self.scheduler_table.item(r, 2).text().lower()
193207
s = (self.scheduler_table.item(r, 3).text() == "Yes")
194208
audio = ("audio" in t)
195-
task = DownloadTask(u, self.user_profile.get_default_resolution(), self.user_profile.get_download_path(), self.user_profile.get_proxy(), audio_only=audio, playlist=False, subtitles=s, audio_format=self.user_profile.get_audio_format() if audio else None, audio_quality=self.user_profile.get_audio_quality() if audio else "320", from_queue=True)
209+
task = DownloadTask(u, self.user_profile.get_default_resolution(), download_path, self.user_profile.get_proxy(), audio_only=audio, playlist=False, subtitles=s, audio_format=self.user_profile.get_audio_format() if audio else None, audio_quality=self.user_profile.get_audio_quality() if audio else "320", from_queue=True)
196210
self.run_task(task, r)
197211
self.scheduler_table.setItem(r, 4, QTableWidgetItem("Started"))
198212
def start_download_simple(self, url_edit, audio=False, playlist=False):
199213
link = url_edit.text().strip()
200214
if not link:
201215
QMessageBox.warning(self, "Error", "No URL given.")
202216
return
203-
task = DownloadTask(link, self.user_profile.get_default_resolution(), self.user_profile.get_download_path(), self.user_profile.get_proxy(), audio_only=audio, playlist=playlist, audio_format=self.user_profile.get_audio_format() if audio else None, audio_quality=self.user_profile.get_audio_quality() if audio else "320", from_queue=False)
204-
# History will be written directly by the downloader
217+
218+
download_path = self.user_profile.get_download_path()
219+
if not os.path.exists(download_path):
220+
QMessageBox.warning(self, "Invalid Download Path",
221+
f"Download folder does not exist:\n{download_path}\n\n"
222+
"The folder may have been deleted or the drive may be disconnected.\n"
223+
"Please select a new download location in Settings.")
224+
self.append_log(f"Download aborted - invalid path: {download_path}")
225+
return
226+
227+
task = DownloadTask(link, self.user_profile.get_default_resolution(), download_path, self.user_profile.get_proxy(), audio_only=audio, playlist=playlist, audio_format=self.user_profile.get_audio_format() if audio else None, audio_quality=self.user_profile.get_audio_quality() if audio else "320", from_queue=False)
205228
self.run_task(task, None)
206229
def run_task(self, task, row):
207230
if task.playlist:
@@ -283,12 +306,20 @@ def update_queue_info(self, row, title, channel):
283306
# History will be written directly by the downloader
284307
def open_download_folder(self):
285308
folder = self.user_profile.get_download_path()
309+
if not os.path.exists(folder):
310+
QMessageBox.warning(self, "Folder Not Found",
311+
f"Download folder does not exist:\n{folder}\n\n"
312+
"The folder may have been deleted or the drive may be disconnected.\n"
313+
"Please select a new download location in Settings.")
314+
self.append_log(f"Cannot open folder - path does not exist: {folder}")
315+
return
316+
286317
try:
287318
if sys.platform.startswith('win'):
288319
os.startfile(folder)
289-
elif sys.platform.startswith('darwin'): # macOS
320+
elif sys.platform.startswith('darwin'):
290321
subprocess.run(['open', folder])
291-
else: # Linux
322+
else:
292323
subprocess.run(['xdg-open', folder])
293324
except (OSError, subprocess.SubprocessError, FileNotFoundError) as e:
294325
QMessageBox.warning(self, "Error", f"Could not open folder: {str(e)}")

ui/pages/settings_page.py

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
from PySide6.QtGui import QFont
66
from ui.components.animated_button import AnimatedButton
77
from core.version import get_version
8+
import os
89

910
class SettingsPage(QWidget):
1011
def __init__(self, parent=None):
1112
super().__init__(parent)
1213
self.parent = parent
1314
self.init_ui()
15+
16+
def showEvent(self, event):
17+
super().showEvent(event)
18+
self.check_download_path_validity()
1419

1520
def init_ui(self):
1621

@@ -248,14 +253,26 @@ def init_ui(self):
248253
p_hl.setContentsMargins(10, 10, 10, 10)
249254
self.download_path_edit = QLineEdit()
250255
self.download_path_edit.setReadOnly(True)
251-
self.download_path_edit.setText(self.parent.user_profile.get_download_path())
252-
self.download_path_edit.setToolTip(
253-
"Download destination folder:\n"
254-
"• All downloads will be saved here\n"
255-
"• Playlists create subfolders automatically\n"
256-
"• Make sure you have enough disk space\n\n"
257-
"Click Browse to change location"
258-
)
256+
current_path = self.parent.user_profile.get_download_path()
257+
self.download_path_edit.setText(current_path)
258+
259+
if not os.path.exists(current_path):
260+
self.download_path_edit.setStyleSheet("QLineEdit { color: #ff4444; font-weight: bold; }")
261+
self.download_path_edit.setToolTip(
262+
"⚠️ WARNING: Path does not exist!\n\n"
263+
f"Current path: {current_path}\n\n"
264+
"This folder may have been deleted or the drive may be disconnected.\n"
265+
"Please select a new download location by clicking Browse."
266+
)
267+
else:
268+
self.download_path_edit.setStyleSheet("")
269+
self.download_path_edit.setToolTip(
270+
"Download destination folder:\n"
271+
"• All downloads will be saved here\n"
272+
"• Playlists create subfolders automatically\n"
273+
"• Make sure you have enough disk space\n\n"
274+
"Click Browse to change location"
275+
)
259276

260277
b_br = AnimatedButton("Browse")
261278
b_br.clicked.connect(self.select_download_path)
@@ -428,8 +445,23 @@ def geo_country_changed(self, index):
428445
self.parent.append_log(f"Geo-bypass country set to: {country_name}")
429446

430447
def select_download_path(self):
431-
folder = QFileDialog.getExistingDirectory(self, "Select Download Folder")
448+
current_path = self.parent.user_profile.get_download_path()
449+
start_dir = current_path if os.path.exists(current_path) else os.path.expanduser("~")
450+
451+
folder = QFileDialog.getExistingDirectory(self, "Select Download Folder", start_dir)
432452
if folder:
453+
if not os.path.exists(folder):
454+
QMessageBox.warning(self, "Invalid Path",
455+
f"Selected path does not exist:\n{folder}\n\n"
456+
"Please select a valid directory.")
457+
return
458+
459+
if not os.access(folder, os.W_OK):
460+
QMessageBox.warning(self, "Permission Denied",
461+
f"Cannot write to selected path:\n{folder}\n\n"
462+
"Please select a directory with write permissions.")
463+
return
464+
433465
self.parent.user_profile.set_profile(
434466
self.parent.user_profile.data["name"],
435467
self.parent.user_profile.data["profile_picture"],
@@ -444,4 +476,34 @@ def toggle_logs(self):
444476
if self.parent.log_manager.log_dock_visible:
445477
self.show_logs_btn.setText("Hide Logs")
446478
else:
447-
self.show_logs_btn.setText("Show Logs")
479+
self.show_logs_btn.setText("Show Logs")
480+
481+
def check_download_path_validity(self):
482+
current_path = self.parent.user_profile.get_download_path()
483+
self.download_path_edit.setText(current_path)
484+
485+
if not os.path.exists(current_path):
486+
self.download_path_edit.setStyleSheet("QLineEdit { color: #ff4444; font-weight: bold; }")
487+
self.download_path_edit.setToolTip(
488+
"⚠️ WARNING: Path does not exist!\n\n"
489+
f"Current path: {current_path}\n\n"
490+
"This folder may have been deleted or the drive may be disconnected.\n"
491+
"Please select a new download location by clicking Browse."
492+
)
493+
QMessageBox.warning(self, "Invalid Download Path",
494+
f"⚠️ Download folder does not exist:\n{current_path}\n\n"
495+
"Possible causes:\n"
496+
"• External drive (USB/SD card) was removed\n"
497+
"• Network drive disconnected\n"
498+
"• Folder was deleted or moved\n\n"
499+
"Downloads will fail until you select a valid location.\n"
500+
"Click Browse to choose a new download folder.")
501+
else:
502+
self.download_path_edit.setStyleSheet("")
503+
self.download_path_edit.setToolTip(
504+
"Download destination folder:\n"
505+
"• All downloads will be saved here\n"
506+
"• Playlists create subfolders automatically\n"
507+
"• Make sure you have enough disk space\n\n"
508+
"Click Browse to change location"
509+
)

0 commit comments

Comments
 (0)