Skip to content
← Public packages

@kentcdodds/spotify

Spotify playback, playlist, search, library, and device helpers.

src/exports/playback-controller.ts

85 lines · 3.0 KB · TypeScript
import {
	pausePlayback,
	startOrResumePlayback,
} from '../lib/spotify-api.ts'
import type { PlaybackControllerParams } from '../lib/export-types.ts'
import { buildRemoteState } from '../lib/remote-state.ts'
import spotifyAddToQueue from './add-to-queue.ts'
import getSpotifyQueue from './get-queue.ts'
import playPause from './play-pause.ts'
import spotifySetVolume from './set-volume.ts'
import spotifySkip from './skip.ts'
import spotifyTransferPlayback from './transfer-playback.ts'

/**
 * Run a remote Spotify playback action and return human-friendly state.
 * All params accept an optional `account` ('personal' | 'family', default 'personal').
 * @returns Remote state object with playback summary; queue actions include queue data.
 * @example
 * import playbackController from 'kody:@kentcdodds/spotify/playback-controller'
 * const state = await playbackController({ action: 'status', account: 'personal' })
 * // => { message: null, isPlaying: true, track: { name: '...' }, devices: [...] }
 */
async function playbackController(params: PlaybackControllerParams = {}) {
	const action = params.action ?? 'status'
	const account = params.account
	switch (action) {
		case 'status':
		case 'state':
			return await buildRemoteState({ account })
		case 'queue': {
			const [state, queue] = await Promise.all([
				buildRemoteState({ account }),
				getSpotifyQueue(params),
			])
			return { ...state, queue }
		}
		case 'play':
			await startOrResumePlayback({
				...params,
				account,
				fallbackDeviceId: params.fallbackDeviceId,
			})
			return await buildRemoteState({ account, message: 'Playback resumed.' })
		case 'pause':
			await pausePlayback({ ...params, account })
			return await buildRemoteState({ account, message: 'Playback paused.' })
		case 'play-pause': {
			const result = await playPause(params)
			const message =
				result.action === 'paused'
					? 'Playback paused.'
					: result.action === 'resumed'
						? 'Playback resumed.'
						: 'Playback started on the fallback device.'
			return await buildRemoteState({ account, message })
		}
		case 'next':
			await spotifySkip({ ...params, direction: 'next' })
			return await buildRemoteState({ account, message: 'Skipped to the next track.' })
		case 'previous':
			await spotifySkip({ ...params, direction: 'previous' })
			return await buildRemoteState({ account, message: 'Returned to the previous track.' })
		case 'transfer':
			await spotifyTransferPlayback(params)
			return await buildRemoteState({
				account,
				message: 'Playback transferred to the selected device.',
			})
		case 'set-volume':
			await spotifySetVolume(params)
			return await buildRemoteState({ account, message: 'Volume updated.' })
		case 'add-to-queue': {
			await spotifyAddToQueue(params)
			const [state, queue] = await Promise.all([
				buildRemoteState({ account, message: 'Added the item to the queue.' }),
				getSpotifyQueue(params),
			])
			return { ...state, queue }
		}
		default:
			throw new Error('Unknown Spotify controller action: ' + String(action))
	}
}

export default playbackController