diff --git a/pyipv8 b/pyipv8 index 89798d3206e..6233e9cb193 160000 --- a/pyipv8 +++ b/pyipv8 @@ -1 +1 @@ -Subproject commit 89798d3206ed7626fb7858134dae7b824a10c256 +Subproject commit 6233e9cb193908271e9a09b371b054acb7e50c22 diff --git a/src/tribler/core/libtorrent/download_manager/download_state.py b/src/tribler/core/libtorrent/download_manager/download_state.py index bf2fd983a11..440a0d47e39 100644 --- a/src/tribler/core/libtorrent/download_manager/download_state.py +++ b/src/tribler/core/libtorrent/download_manager/download_state.py @@ -99,12 +99,12 @@ def get_status(self) -> DownloadStatus: """ Returns the status of the torrent. """ + if self.get_error(): + return DownloadStatus.STOPPED_ON_ERROR if self.lt_status: if self.lt_status.paused: return DownloadStatus.STOPPED return DOWNLOAD_STATUS_MAP[self.lt_status.state] - if self.get_error(): - return DownloadStatus.STOPPED_ON_ERROR return DownloadStatus.STOPPED def get_error(self) -> str | None: diff --git a/src/tribler/core/restapi/file_endpoint.py b/src/tribler/core/restapi/file_endpoint.py index 89c88aae5aa..79bfe51692e 100644 --- a/src/tribler/core/restapi/file_endpoint.py +++ b/src/tribler/core/restapi/file_endpoint.py @@ -55,9 +55,16 @@ async def browse(self, request: web.Request) -> RESTResponse: # Move up until we find a directory parent_path = Path(path).resolve() - while not parent_path.is_dir(): + while not parent_path.is_dir() and parent_path != parent_path.parent: parent_path = parent_path.parent + # Did we find an existing directory? + if not parent_path.is_dir() or not parent_path.exists(): + return RESTResponse({"error": { + "handled": True, + "message": f"No directory named {parent_path} exists" + }}, status=HTTP_NOT_FOUND) + # Get all files/subdirs results = [] for file in parent_path.iterdir(): diff --git a/src/tribler/test_unit/core/libtorrent/download_manager/test_download_manager.py b/src/tribler/test_unit/core/libtorrent/download_manager/test_download_manager.py index b976fe3f548..a7516f4e4f3 100644 --- a/src/tribler/test_unit/core/libtorrent/download_manager/test_download_manager.py +++ b/src/tribler/test_unit/core/libtorrent/download_manager/test_download_manager.py @@ -398,7 +398,7 @@ async def test_readd_download_safe_seeding(self) -> None: download = Download(TorrentDefNoMetainfo(b"\x01" * 20, b"name", None), None, checkpoint_disabled=True, config=config) download.futures["save_resume_data"] = succeed(True) - download_state = DownloadState(download, Mock(state=4, paused=False), None) + download_state = DownloadState(download, Mock(state=4, paused=False, error=None), None) self.manager.downloads = {b"\x01" * 20: download} self.manager.config.set("libtorrent/download_defaults/number_hops", 42) diff --git a/src/tribler/test_unit/core/libtorrent/download_manager/test_stream.py b/src/tribler/test_unit/core/libtorrent/download_manager/test_stream.py index b1f6395e028..7da8ce2949d 100644 --- a/src/tribler/test_unit/core/libtorrent/download_manager/test_stream.py +++ b/src/tribler/test_unit/core/libtorrent/download_manager/test_stream.py @@ -152,7 +152,7 @@ def create_mock_download(self, piece_size: int | None = None, pieces: list[bool] checkpoint_disabled=True) download.handle = Mock(is_valid=Mock(return_value=True), file_priorities=Mock(return_value=[0] * 6), torrent_file=Mock(return_value=download.tdef.torrent_info)) - download.lt_status = Mock(state=3, paused=False, pieces=[]) + download.lt_status = Mock(state=3, paused=False, error=None, pieces=[]) if piece_size is not None: self.convert_to_piece_size(download, piece_size) if pieces is not None: diff --git a/src/tribler/ui/src/dialogs/SelectRemotePath.tsx b/src/tribler/ui/src/dialogs/SelectRemotePath.tsx index d976372112b..0289474feb1 100644 --- a/src/tribler/ui/src/dialogs/SelectRemotePath.tsx +++ b/src/tribler/ui/src/dialogs/SelectRemotePath.tsx @@ -49,8 +49,17 @@ export default function SelectRemotePath(props: SelectRemotePathProps & JSX.Intr const response = await triblerService.browseFiles(dir, showFiles || false); if (response === undefined) { toast.error(`${t("ToastErrorBrowseFiles")} ${t("ToastErrorGenNetworkErr")}`); - } else if (isErrorDict(response)) { + } else if (isErrorDict(response) && response.errorCode != 404) { toast.error(`${t("ToastErrorBrowseFiles")} ${response.error.message}`); + } else if (isErrorDict(response)) { + // If we couldn't get the requested path, browse the default path instead. + let settings = await triblerService.getSettings() + if (settings !== undefined && !isErrorDict(settings)) { + let nextDir = settings.libtorrent.download_defaults.saveas; + if (dir != nextDir) { + reloadPaths(nextDir); + } + } } else { setPaths(response.paths); setCurrentPath(response.current); diff --git a/src/tribler/ui/src/models/download.model.tsx b/src/tribler/ui/src/models/download.model.tsx index d7635307cac..49722a6c971 100644 --- a/src/tribler/ui/src/models/download.model.tsx +++ b/src/tribler/ui/src/models/download.model.tsx @@ -5,8 +5,20 @@ import { File } from "./file.model"; import { Tracker } from "./tracker.model"; -type state = 'ALLOCATING_DISKSPACE' | 'WAITING_FOR_HASHCHECK' | 'HASHCHECKING' | 'DOWNLOADING' | - 'SEEDING' | 'STOPPED' | 'STOPPED_ON_ERROR' | 'METADATA' | 'LOADING' | 'EXIT_NODES'; +export enum StatusCode { + ALLOCATING_DISKSPACE = 0, + WAITING_FOR_HASHCHECK = 1, + HASHCHECKING = 2, + DOWNLOADING = 3, + SEEDING = 4, + STOPPED = 5, + STOPPED_ON_ERROR = 6, + METADATA = 7, + LOADING = 8, + EXIT_NODES = 9 +} + +export type Status = keyof typeof StatusCode; export interface Download { name: string; @@ -14,8 +26,8 @@ export interface Download { infohash: string; speed_down: number; speed_up: number; - status: state; - status_code: number; + status: Status; + status_code: StatusCode; size: number; eta: number; num_peers: number; diff --git a/src/tribler/ui/src/pages/Downloads/Details.tsx b/src/tribler/ui/src/pages/Downloads/Details.tsx index c80b2fad5a8..835bc81cead 100644 --- a/src/tribler/ui/src/pages/Downloads/Details.tsx +++ b/src/tribler/ui/src/pages/Downloads/Details.tsx @@ -10,7 +10,7 @@ import Peers from "./Peers"; import Trackers from "./Trackers"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -import { Download } from "@/models/download.model"; +import { Download, StatusCode } from "@/models/download.model"; import Pieces from "./Pieces"; import { useLayoutEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -57,7 +57,10 @@ export default function DownloadDetails({ download }: { download: Download | und