soulseek: queue downloads straight out of the cached tree
a browsed folder is a path, not a file list, and what clicking one means is "everything under here" — so the expansion happens in the sidecar, off the cache, rather than making the browser walk the tree a level at a time and rebuild paths it only half knows. browse reports file names as basenames, unlike search, so the peer's real path is rejoined from the folder row. sizes come from the cache too: slskd matches a queued download on filename AND size, so a number supplied by the client would be a transfer that silently never starts. a subtree can be the peer's whole share (284k files on one measured peer), so an over-limit request is refused with its count rather than truncated into a partial download nobody asked for. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
deleteSoulseekBrowse,
|
||||
} from 'officerdb';
|
||||
import { startBrowse } from './browse';
|
||||
import { enqueueFromCache, DownloadError } from './download';
|
||||
|
||||
// Officer-owned Soulseek routes — everything slskd itself has no concept of. These are served HERE, by
|
||||
// the sidecar, not forwarded upstream: the platform API stays a pure auth-and-forward proxy forever, and
|
||||
@@ -29,6 +30,7 @@ import { startBrowse } from './browse';
|
||||
// GET /_officer/browse/<u>/tree?parent=&limit=&offset= → { nodes, total } one level (roots if no parent)
|
||||
// GET /_officer/browse/<u>/tree/search?q=&limit= → { nodes, matched, total, truncated }
|
||||
// GET /_officer/browse/<u>/dirs/<id>/files → BrowsedFile[] one folder's files
|
||||
// POST /_officer/browse/<u>/download { path, file? } → { queued } enqueue a folder, or one file
|
||||
// ─────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
// The authenticated user id arrives in X-Officer-User, injected by the platform proxy after auth. We
|
||||
@@ -145,6 +147,22 @@ async function handleBrowse({ req, url, userId, segments }: BrowseRouteParams):
|
||||
return Response.json(result);
|
||||
}
|
||||
|
||||
// /_officer/browse/<username>/download — a folder's whole subtree, or one file inside it.
|
||||
if (segments[2] === 'download' && segments.length === 3) {
|
||||
if (req.method !== 'POST') return methodNotAllowed();
|
||||
const body = (await req.json().catch(() => null)) as { path?: unknown; file?: unknown } | null;
|
||||
const path = typeof body?.path === 'string' ? body.path.trim() : '';
|
||||
if (!path) return badRequest('path required');
|
||||
const file = typeof body?.file === 'string' && body.file.trim() ? body.file : undefined;
|
||||
try {
|
||||
const queued = await enqueueFromCache({ userId, username, path, file });
|
||||
return Response.json({ queued });
|
||||
} catch (err) {
|
||||
if (err instanceof DownloadError) return Response.json({ error: err.message }, { status: err.status });
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
if (segments[2] !== 'dirs') return notFound();
|
||||
|
||||
// /_officer/browse/<username>/dirs/<id>/files
|
||||
|
||||
Reference in New Issue
Block a user