feat(torrents): add seeding lifecycle and upload controls

This commit is contained in:
NimBold
2026-08-01 21:27:49 +03:30
parent bb64c4cd52
commit dea6ad1974
26 changed files with 663 additions and 25 deletions
+3
View File
@@ -59,6 +59,9 @@ export interface AddDownloadDraftRow {
torrentInfoHash?: string;
torrentFiles?: TorrentFile[];
selectedTorrentFileIndices?: number[];
torrentSeedTime?: number;
torrentSeedRatio?: number;
torrentUploadLimit?: string;
}
/**
+4 -1
View File
@@ -20,7 +20,7 @@ describe('download action policy', () => {
expect(canStartDownload(status)).toBe(true);
expect(canPauseDownload(status)).toBe(false);
}
for (const status of ['staged', 'queued', 'downloading', 'processing', 'retrying'] as const) {
for (const status of ['staged', 'queued', 'downloading', 'seeding', 'processing', 'retrying'] as const) {
expect(canPauseDownload(status)).toBe(true);
}
for (const status of ['queued', 'downloading', 'processing', 'retrying'] as const) {
@@ -33,12 +33,14 @@ describe('download action policy', () => {
expect(canRedownload('failed')).toBe(true);
expect(canRedownload('paused')).toBe(true);
expect(canRedownload('downloading')).toBe(false);
expect(canRedownload('seeding')).toBe(false);
});
it('only exposes pause or resume for the details-view toggle', () => {
expect(getPauseResumeAction('queued')).toBe('pause');
expect(getPauseResumeAction('downloading')).toBe('pause');
expect(getPauseResumeAction('processing')).toBe('pause');
expect(getPauseResumeAction('seeding')).toBe('pause');
expect(getPauseResumeAction('retrying')).toBe('pause');
expect(getPauseResumeAction('paused')).toBe('resume');
@@ -53,6 +55,7 @@ describe('download action policy', () => {
expect(startActionLabel('failed')).toBe('Start');
expect(startActionLabel('paused')).toBe('Resume');
expect(isTransferLocked('processing')).toBe(true);
expect(isTransferLocked('seeding')).toBe(true);
expect(isIdentityLocked('completed')).toBe(true);
expect(isTransferLocked('completed')).toBe(false);
});
+2 -1
View File
@@ -11,6 +11,7 @@ const PAUSABLE_STATUSES: ReadonlySet<DownloadStatus> = new Set([
'staged',
'queued',
'downloading',
'seeding',
'processing',
'retrying',
]);
@@ -63,7 +64,7 @@ export const startActionLabel = (status: DownloadStatus): 'Start' | 'Resume' =>
status === 'ready' || status === 'staged' || status === 'failed' ? 'Start' : 'Resume';
export const isTransferLocked = (status: DownloadStatus): boolean =>
status === 'downloading' || status === 'processing' || status === 'retrying';
status === 'downloading' || status === 'processing' || status === 'seeding' || status === 'retrying';
export const isIdentityLocked = (status: DownloadStatus): boolean =>
isTransferLocked(status) || status === 'completed';
+2
View File
@@ -73,6 +73,8 @@ export const downloadProgressColorClass = (status: string): string => {
return 'download-status-failed';
case 'processing':
return 'download-status-processing';
case 'seeding':
return 'download-status-seeding';
case 'queued':
case 'staged':
return 'download-status-queued';
+1
View File
@@ -20,6 +20,7 @@ const isFreshDownloadStatus = (status: DownloadItem['status']): boolean =>
status === 'staged' ||
status === 'queued' ||
status === 'downloading' ||
status === 'seeding' ||
status === 'processing' ||
status === 'retrying';
+4 -2
View File
@@ -30,6 +30,7 @@ const ACTIVE_DOWNLOAD_STATUSES: ReadonlySet<DownloadStatus> = new Set([
'queued',
'downloading',
'processing',
'seeding',
'retrying',
]);
@@ -38,7 +39,7 @@ export const isActiveDownloadStatus = (status: DownloadStatus): boolean =>
/** Transfer states that consume a worker/permit. Queued is intentionally excluded. */
export const isTransferActiveStatus = (status: DownloadStatus): boolean =>
status === 'downloading' || status === 'processing' || status === 'retrying';
status === 'downloading' || status === 'processing' || status === 'seeding' || status === 'retrying';
export const DOWNLOAD_CONNECTIONS_MIN = 1;
export const DOWNLOAD_CONNECTIONS_MAX = 16;
@@ -255,7 +256,8 @@ export const isMediaUrl = (rawUrl: string): boolean => {
*/
const DOWNLOAD_SECRET_FIELDS = ['password', 'cookies', 'headers'] as const;
const VOLATILE_PROGRESS_STATUSES = new Set([
'downloading'
'downloading',
'seeding'
]);
/**