feat(torrents): add stall timeout control

This commit is contained in:
NimBold
2026-08-02 00:48:19 +03:30
parent d0541308c7
commit 2474d2c1cb
18 changed files with 279 additions and 21 deletions
+7 -3
View File
@@ -854,13 +854,15 @@ describe('useDownloadStore', () => {
torrentMaxPeers: 'not-a-number' as unknown as number,
torrentPeerSpeedLimit: 0 as unknown as string,
torrentCheckIntegrity: 'yes' as unknown as boolean,
torrentTrackers: 123 as unknown as string
torrentTrackers: 123 as unknown as string,
torrentStopTimeout: 604801
});
expect(normalized.torrentMaxPeers).toBeUndefined();
expect(normalized.torrentPeerSpeedLimit).toBeUndefined();
expect(normalized.torrentCheckIntegrity).toBeUndefined();
expect(normalized.torrentTrackers).toBeUndefined();
expect(normalized.torrentStopTimeout).toBeUndefined();
});
it('normalizes proxy settings for download dispatch', async () => {
@@ -1512,7 +1514,8 @@ describe('useDownloadStore', () => {
dateAdded: '',
isTorrent: true,
torrentCheckIntegrity: true,
torrentTrackers: 'https://tracker.example/announce'
torrentTrackers: 'https://tracker.example/announce',
torrentStopTimeout: 300
}, { type: 'start-now' });
const item = useDownloadStore.getState().downloads[0];
@@ -1524,7 +1527,8 @@ describe('useDownloadStore', () => {
item: expect.objectContaining({
id: 'start-1',
torrent_check_integrity: true,
torrent_trackers: 'https://tracker.example/announce'
torrent_trackers: 'https://tracker.example/announce',
torrent_stop_timeout: 300
})
})
);
+14 -3
View File
@@ -9,7 +9,7 @@ import type { ExtensionCookieScope } from '../bindings/ExtensionCookieScope';
import type { Queue } from '../bindings/Queue';
import { useSettingsStore } from './useSettingsStore';
import { useDownloadProgressStore } from './downloadProgressStore';
import { canonicalizeDownloadFileName, categoryForFileName, isActiveDownloadStatus, isTransferActiveStatus, normalizeSpeedLimitForBackend, redactDownloadForPersistence, resolveDownloadConnections } from '../utils/downloads';
import { canonicalizeDownloadFileName, categoryForFileName, isActiveDownloadStatus, isTransferActiveStatus, MAX_TORRENT_STOP_TIMEOUT, normalizeSpeedLimitForBackend, redactDownloadForPersistence, resolveDownloadConnections } from '../utils/downloads';
import {
resolveCategoryDestination
} from '../utils/downloadLocations';
@@ -352,6 +352,7 @@ async function dispatchItemInternal(id: string, proxyOverride?: string | null):
torrent_peer_speed_limit: item.torrentPeerSpeedLimit || undefined,
torrent_check_integrity: item.torrentCheckIntegrity,
torrent_trackers: item.torrentTrackers || undefined,
torrent_stop_timeout: item.torrentStopTimeout,
lifecycle_generation: lifecycleGeneration.toString(),
};
@@ -636,16 +637,25 @@ export const normalizePersistedDownloadProgress = (download: DownloadItem): Down
const normalizedTrackers = typeof rawTrackers === 'string' && rawTrackers.trim()
? rawTrackers.trim()
: undefined;
const rawStopTimeout = download.torrentStopTimeout as unknown;
const normalizedStopTimeout = typeof rawStopTimeout === 'number' &&
Number.isInteger(rawStopTimeout) &&
rawStopTimeout >= 0 &&
rawStopTimeout <= MAX_TORRENT_STOP_TIMEOUT
? rawStopTimeout
: undefined;
const normalizedOptions = rawMaxPeers !== normalizedMaxPeers ||
rawPeerSpeedLimit !== normalizedPeerSpeedLimit ||
rawCheckIntegrity !== normalizedCheckIntegrity ||
rawTrackers !== normalizedTrackers
rawTrackers !== normalizedTrackers ||
rawStopTimeout !== normalizedStopTimeout
? {
...download,
torrentMaxPeers: normalizedMaxPeers,
torrentPeerSpeedLimit: normalizedPeerSpeedLimit,
torrentCheckIntegrity: normalizedCheckIntegrity,
torrentTrackers: normalizedTrackers
torrentTrackers: normalizedTrackers,
torrentStopTimeout: normalizedStopTimeout
}
: download;
@@ -2178,6 +2188,7 @@ export const useDownloadStore = create<DownloadState>((set, get) => {
torrent_peer_speed_limit: item.torrentPeerSpeedLimit || undefined,
torrent_check_integrity: item.torrentCheckIntegrity,
torrent_trackers: item.torrentTrackers || undefined,
torrent_stop_timeout: item.torrentStopTimeout,
lifecycle_generation: currentDownloadLifecycle(item.id).toString(),
});
}