soulseek, extracted from the platform as a plugin
Seventh plugin out, and the first that was already dead. Soulseek had been switched off since 2026-08-13: hono.ts had the router import and its mount commented out, so createSidecarProxy never ran, registerProxiedPrefix was never called, the slskd:server announcement fired into a void, and officer-slskd was not in the ecosystem at all. The permission entry had been reduced to api: [], which is why assertPermissionTotality was satisfied and the server booted clean. The UI rendered perfectly and every request 404'd. So this is a resurrection as well as a move. The mount is now /api/soulseek, derived via mountPrefix from the appName — all 47 frontend call sites repointed from /slskd/. The credential key in service_connections stays 'slskd': it names the daemon, not the route. Those two strings differ on purpose and conflating them reads the wrong row. The connection stays in core service_connections, as transmission does, so 'slskd' remains in a platform union this plugin cannot extend. Recorded in PLUGIN.md. Unlike transmission this plugin does own a schema: favourites and the two halves of the cached browse tree, which slskd has no concept of. Those three tables had been dropped from the drizzle barrel, so bun db:push would have deleted them on any server holding data. 313 palette classes converted to the semantic tone set, including a shared PHASE_TONE the three transfer views previously each copied.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { getSoulseekBrowseDownload } from '../db';
|
||||
import { authHeaders, getSlskdConfig } 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<number> {
|
||||
const cfg = await getSlskdConfig(userId);
|
||||
if (!cfg) 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 res = await fetch(`${cfg.base}/api/v0/transfers/downloads/${encodeURIComponent(username)}`, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', ...authHeaders(cfg) },
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user