mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-11 20:17:46 +00:00
feat(queue): implement backend-driven download queue coordinator
This commit replaces the frontend-imperative download dispatcher with a centralized backend `QueueManager`. It acts as the sole concurrency gatekeeper using a single `tokio::sync::Semaphore` across all download paths (aria2 RPC, native HTTP, yt-dlp media). Backend Changes: - **queue**: Added `QueueManager` to manage an ordered `VecDeque` of tasks, semaphore permits, and retirement debt (CAS resize). - **commands**: Replaced direct start commands with `enqueue_download`, `enqueue_many`, `move_in_queue`, and `remove_from_queue`. - **ipc**: Exported `DownloadStateEvent` and `QueueDirection` to the frontend. - **tests**: Added 11 integration tests covering idle-parking, idempotent releases, CAS underflow prevention, and gid-completion races. Frontend Changes: - **store**: Made `useDownloadStore` reactive to the backend via the `download-state` event. - **store**: Removed `processQueue` and introduced `pendingOrder` to track the accurate sequence of queued items. - **ui**: Updated `DownloadItem` with queue visuals (clock icon, position badge). - **ui**: Added Move Up/Down controls to interact with the backend queue reordering API.
This commit is contained in:
+10
-7
@@ -21,7 +21,7 @@ function App() {
|
||||
const stored = Number(window.localStorage.getItem('firelink-sidebar-width'));
|
||||
return Number.isFinite(stored) && stored >= 190 && stored <= 260 ? stored : 220;
|
||||
});
|
||||
const updateDownload = useDownloadStore(state => state.updateDownload);
|
||||
|
||||
const theme = useSettingsStore(state => state.theme);
|
||||
const isSidebarVisible = useSettingsStore(state => state.isSidebarVisible);
|
||||
const activeView = useSettingsStore(state => state.activeView);
|
||||
@@ -209,8 +209,6 @@ function App() {
|
||||
initDownloadListener();
|
||||
|
||||
const unlistenComplete = listen('download-complete', (event) => {
|
||||
updateDownload(event.payload, { status: 'completed', fraction: 1.0, speed: '-', eta: '-' });
|
||||
|
||||
const settings = useSettingsStore.getState();
|
||||
if (settings.showNotifications) {
|
||||
const item = useDownloadStore.getState().downloads.find(d => d.id === event.payload);
|
||||
@@ -225,10 +223,15 @@ function App() {
|
||||
});
|
||||
|
||||
const unlistenFailed = listen('download-failed', (event) => {
|
||||
// If it's already paused, don't mark as failed (since we aborted it)
|
||||
const current = useDownloadStore.getState().downloads.find(d => d.id === event.payload);
|
||||
if (current && current.status !== 'paused') {
|
||||
updateDownload(event.payload, { status: 'failed', speed: '-', eta: '-' });
|
||||
const settings = useSettingsStore.getState();
|
||||
if (settings.showNotifications) {
|
||||
const item = useDownloadStore.getState().downloads.find(d => d.id === event.payload);
|
||||
const fileName = item?.fileName || 'A file';
|
||||
|
||||
sendNotification({
|
||||
title: 'Download Failed',
|
||||
body: `${fileName} failed to download.`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user