import { createSpotifyApiClient, type SpotifyApiClient } from "./api-client.js"; import type { CliDeps, ParsedCli } from "./cli.js"; import type { SpotifyPlaylist, SpotifyTrack } from "./types.js"; export interface TrackOutput { id: string; uri: string; name: string; artists: string[]; album?: string; externalUrl?: string; } export interface PlaylistOutput { id: string; name: string; public: boolean | null; owner: string; externalUrl?: string; } function numberOption(value: string | undefined, fallback: number): number { if (!value) { return fallback; } const parsed = Number(value); return Number.isFinite(parsed) ? parsed : fallback; } export function clamp(value: number, min: number, max: number): number { return Math.min(max, Math.max(min, Math.trunc(value))); } export function mapTrack(track: SpotifyTrack): TrackOutput { return { id: track.id, uri: track.uri, name: track.name, artists: track.artists.map((artist) => artist.name), album: track.album?.name, externalUrl: track.external_urls?.spotify }; } export function mapPlaylist(playlist: SpotifyPlaylist): PlaylistOutput { return { id: playlist.id, name: playlist.name, public: playlist.public, owner: playlist.owner.display_name ?? playlist.owner.id, externalUrl: playlist.external_urls?.spotify }; } export function formatTrack(track: TrackOutput): string { const album = track.album ? ` (${track.album})` : ""; return `${track.uri} | ${track.name} | ${track.artists.join(", ")}${album}`; } export function formatPlaylist(playlist: PlaylistOutput): string { const visibility = playlist.public === true ? "public" : playlist.public === false ? "private" : "unknown"; return `${playlist.id} | ${visibility} | ${playlist.owner} | ${playlist.name}`; } export async function runSearchCommand( args: ParsedCli, deps: CliDeps, client: Pick = createSpotifyApiClient() ): Promise { const query = args.positional.join(" ").trim(); if (!query) { throw new Error("Missing search query."); } const limit = clamp(numberOption(args.limit, 5), 1, 10); const tracks = (await client.searchTracks(query, limit)).map(mapTrack); if (args.json) { deps.stdout.write(`${JSON.stringify({ tracks }, null, 2)}\n`); } else { deps.stdout.write(`${tracks.map(formatTrack).join("\n")}${tracks.length ? "\n" : ""}`); } return 0; } export async function runListPlaylistsCommand( args: ParsedCli, deps: CliDeps, client: Pick = createSpotifyApiClient() ): Promise { const limit = clamp(numberOption(args.limit, 50), 1, 50); const offset = Math.max(0, numberOption(args.offset, 0)); const playlists = (await client.listPlaylists(limit, offset)).map(mapPlaylist); if (args.json) { deps.stdout.write(`${JSON.stringify({ playlists }, null, 2)}\n`); } else { deps.stdout.write(`${playlists.map(formatPlaylist).join("\n")}${playlists.length ? "\n" : ""}`); } return 0; }