/** The folder door picks FIRST — the phone's own document-tree picker — so * the review only opens for a folder that was actually chosen. The tree has * to be on this phone's storage: the backend walks a path, and a cloud * provider's tree has none. Returns null when the picker was dismissed. */ export async function pickImportFolder() { let picked; try { picked = await api.pickLibraryFolder(); } catch (e) { if (/cancelled/.test(String(e))) return null; throw e; } if (!picked?.path) throw new Error("That folder has no path the phone can read — pick one on this phone's storage."); return picked.path; } /** Stage a folder of audio files. The backend walks the folder and its disc * subfolders, orders by the files' own track numbers when every one carries * a distinct one and by natural filename order otherwise, and matches each * file against the library by path. Every row comes back local, so there is * nothing for the catalog pass to verify. */ export async function startFolder(path) { const g = ++gen; Object.assign(importer, { door: "folder", source: path, busy: true, error: "", progress: null, draft: null, name: "", includeUnresolved: true, checks: {}, committing: false, links: false }); try { const draft = await api.playlistImportFolder(path); if (g !== gen) return; importer.busy = false; if (!draft) { importer.error = "Nothing came back from that folder."; return; } importer.draft = draft; importer.name = draft.name ?? ""; } catch (e) { if (g === gen) { importer.error = String(e); importer.busy = false; } } }