From df3643612ca064aac2bddeadfee7d2a86406dc2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Wed, 29 Jul 2026 23:47:50 +0000 Subject: [PATCH] soulseek: fix browse crashing on the wrapped share-tree response GET /users/{u}/browse returns { directories, directoryCount, lockedDirectories, lockedDirectoryCount }, not the bare Directory[] that 0.26.0's source suggests, so spreading it threw "E is not iterable" and every browse failed. Read .directories, tolerating both shapes since the two disagree. Also note in the type that browsed files carry only a basename, unlike search results which carry the full remote path slskd needs to enqueue. Co-Authored-By: Claude Opus 4.8 --- .../officerdev/src/apps/Soulseek/SoulseekUsers.tsx | 5 ++++- src/workspaces/officerdev/src/apps/Soulseek/shared.ts | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/workspaces/officerdev/src/apps/Soulseek/SoulseekUsers.tsx b/src/workspaces/officerdev/src/apps/Soulseek/SoulseekUsers.tsx index 976ae9de..d404b510 100644 --- a/src/workspaces/officerdev/src/apps/Soulseek/SoulseekUsers.tsx +++ b/src/workspaces/officerdev/src/apps/Soulseek/SoulseekUsers.tsx @@ -7,6 +7,7 @@ import { useSoulseekFavorites } from './useSoulseekFavorites'; import { SOULSEEK_USER_CHANNEL, type SlskdBrowseDirectory, + type SlskdBrowseResponse, type SlskdUserInfo, type SlskdUserStatus, type SoulseekUserRequest, @@ -51,9 +52,11 @@ export const SoulseekUsers = () => { const browse = async (username: string) => { setBrowsing(true); try { - const tree = await client.get( + const res = await client.get( `/slskd/api/v0/users/${encodeURIComponent(username)}/browse`, ); + // Tolerate both shapes: 0.26.0's source returns a bare array, the running build wraps it. + const tree = Array.isArray(res) ? res : (res?.directories ?? []); setDirs([...tree].sort((a, b) => a.name.localeCompare(b.name))); } catch (err) { toast.error(`Browse failed: ${err instanceof Error ? err.message : String(err)}`); diff --git a/src/workspaces/officerdev/src/apps/Soulseek/shared.ts b/src/workspaces/officerdev/src/apps/Soulseek/shared.ts index 6208d4d7..79a07b08 100644 --- a/src/workspaces/officerdev/src/apps/Soulseek/shared.ts +++ b/src/workspaces/officerdev/src/apps/Soulseek/shared.ts @@ -174,6 +174,16 @@ export type SlskdUserStatus = { presence?: string; // 'Offline' | 'Away' | 'Online' }; export type SlskdBrowseDirectory = { name: string; fileCount: number; files?: SlskdFile[] }; +// GET /users/{u}/browse wraps the share tree rather than returning a bare array, and inlines every +// file of every folder in one shot (real peers hit 18k folders / tens of MB). Note that a browsed +// file's `filename` is only the basename — unlike search results, where it's the full remote path — +// so enqueuing one means rejoining it to its directory name with a backslash. +export type SlskdBrowseResponse = { + directories: SlskdBrowseDirectory[]; + directoryCount: number; + lockedDirectories: SlskdBrowseDirectory[]; + lockedDirectoryCount: number; +}; // Server connection state (GET /server, and the server block of GET /application). export type SlskdServerState = { address?: string; state?: string; isConnected?: boolean; username?: string };