import type { StorageLocation } from "yt-dlp-transcript-common/lib/storageLocations"; // WHERE A MOVE IS GOING, as the two panels that start one can say it. // // A destination is EITHER a configured location, named by its id, OR a root the // operator typed. The difference is not cosmetic: for a location the client // sends the ID AND NOTHING ELSE, and the root is looked up on the server, from // the same settings.json /storage writes. A form that posted the root alongside // the id would be a second copy of a fact that already has one home, and the // copy the server trusted would be the one the browser held when the page was // rendered — stale the moment a re-point moved the location somewhere else. // // NOT IN EITHER ACTIONS FILE, for the reason lib/relocationJob.ts and // lib/queueForSlugs.ts are not either: both carry "use server", where every // non-type export is a server action. A shared resolver cannot live in one. export type MoveDestination = { kind: "location"; locationId: string } | { kind: "custom"; root: string }; export type ResolvedDestination = { root: string } | { error: string }; // The root, or the sentence explaining why there is not one. Pure: the caller // hands it `getSettings().storage.locations`, so this is unit-testable and has // no opinion about which process is asking. export function resolveMoveDestination( dest: MoveDestination, locations: readonly StorageLocation[], ): ResolvedDestination { if (dest.kind === "custom") { const root = dest.root.trim(); return root ? { root } : { error: "Enter a destination root." }; } const id = dest.locationId.trim(); const found = locations.find((l) => l.id === id); if (!found) { return { error: `No storage location "${id}" is configured. ` + `Add it on /storage, or choose a different destination.`, }; } return { root: found.root }; }