import { getSoulseekBrowseDownload } from 'officerdb'; import { getSlskdBase, getSlskdApiKey } from './upstream'; // Enqueueing downloads out of a cached share tree. // // slskd's own enqueue endpoint takes a flat list of {filename, size}, which is fine for search results — // they arrive as individual files with full paths. A browsed folder is neither: it's a path, and what the // user means by clicking it is "everything under here". Expanding that into a file list needs the cache, // so it happens HERE rather than in the browser, which would otherwise have to walk the tree one level // request at a time and reconstruct paths it only half knows. /** * A folder can be enormous — the root of one measured peer is 284,166 files. Enqueueing that would be a * misclick that takes the slskd instance down with it, so it's refused with the count rather than * truncated to a silent partial download. */ export const MAX_ENQUEUE = 10_000; export class DownloadError extends Error { constructor( message: string, readonly status: number, ) { super(message); } } type EnqueueParams = { userId: number; username: string; path: string; file?: string }; /** Queue one file, or every file beneath one folder, from the cached tree. Returns how many were sent. */ export async function enqueueFromCache({ userId, username, path, file }: EnqueueParams): Promise { const base = getSlskdBase(); if (!base) throw new DownloadError('slskd upstream not configured', 503); const files = await getSoulseekBrowseDownload({ userId, username, path, file }); if (!files.length) throw new DownloadError('nothing to download at that path', 404); if (files.length > MAX_ENQUEUE) { throw new DownloadError( `${files.length.toLocaleString()} files is over the ${MAX_ENQUEUE.toLocaleString()} limit`, 413, ); } const headers = new Headers({ 'content-type': 'application/json' }); const apiKey = getSlskdApiKey(); if (apiKey) headers.set('X-API-Key', apiKey); const res = await fetch(`${base}/api/v0/transfers/downloads/${encodeURIComponent(username)}`, { method: 'POST', headers, body: JSON.stringify(files), }); if (!res.ok) { const detail = (await res.text().catch(() => '')).slice(0, 200); throw new DownloadError(`slskd returned ${res.status}${detail ? `: ${detail}` : ''}`, 502); } console.log(`[slskd] queued ${files.length} file(s) from ${username}: ${file ?? path}`); return files.length; }