playback.ts•3.11 kB
import type { SpotifyClient } from "../spotify-client.js";
import type { PlaybackState } from "../types.js";
export interface ControlPlaybackParams {
action: "play" | "pause" | "next" | "previous" | "seek";
uri?: string;
position_ms?: number;
}
/**
* Control playback (play, pause, next, previous, seek)
*/
export async function controlPlayback(
client: SpotifyClient,
params: ControlPlaybackParams
): Promise<string> {
const { action, uri, position_ms } = params;
switch (action) {
case "play":
if (uri) {
await client.put("/me/player/play", { uris: [uri] });
return `Started playing: ${uri}`;
} else {
await client.put("/me/player/play");
return "Playback resumed";
}
case "pause":
await client.put("/me/player/pause");
return "Playback paused";
case "next":
await client.post("/me/player/next");
return "Skipped to next track";
case "previous":
await client.post("/me/player/previous");
return "Skipped to previous track";
case "seek":
if (position_ms === undefined) {
throw new Error("position_ms is required for seek action");
}
await client.put("/me/player/seek", undefined, { position_ms });
return `Seeked to ${Math.floor(position_ms / 1000)} seconds`;
default:
throw new Error(`Unknown action: ${action}`);
}
}
/**
* Get current playback state
*/
export async function getPlaybackState(
client: SpotifyClient
): Promise<PlaybackState | null> {
try {
const state = await client.get<PlaybackState>("/me/player");
return state;
} catch (error) {
// 204 No Content means no active playback
if (error instanceof Error && error.message.includes("204")) {
return null;
}
throw error;
}
}
/**
* Format playback state for display
*/
export function formatPlaybackState(state: PlaybackState | null): string {
if (!state || !state.item) {
return "No active playback. Start playing music on Spotify to see playback information.";
}
const track = state.item;
const artists = track.artists.map((a) => a.name).join(", ");
const progress = formatDuration(state.progress_ms || 0);
const duration = formatDuration(track.duration_ms);
const status = state.is_playing ? "▶️ Playing" : "⏸️ Paused";
const lines = [
`**${status}**`,
"",
`**Track:** ${track.name}`,
`**Artist:** ${artists}`,
`**Album:** ${track.album.name}`,
`**Progress:** ${progress} / ${duration}`,
`**Device:** ${state.device.name} (${state.device.type})`,
`**Shuffle:** ${state.shuffle_state ? "On" : "Off"}`,
`**Repeat:** ${state.repeat_state}`,
"",
`**URI:** ${track.uri}`,
`**Spotify Link:** ${track.external_urls.spotify}`,
];
return lines.join("\n");
}
/**
* Format duration from milliseconds to MM:SS
*/
function formatDuration(ms: number): string {
const totalSeconds = Math.floor(ms / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
}