Skip to content
← Public packages

@kentcdodds/spotify

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

src/lib/accounts.ts

82 lines · 2.7 KB · TypeScript
export type SpotifyAccountAlias = 'personal' | 'family'

export type SpotifyAccountInfo = {
	account: SpotifyAccountAlias
	integrationName: string
	label: string
	userIdHint: string | null
	displayNameHint: string | null
	/** Default Spotify Connect device id used when nothing is playing, or null. */
	fallbackDeviceId: string | null
	/** Context started when play is requested and nothing is playing, or null. */
	defaultCollectionUri: string | null
	useWhen: string
}

export const SPOTIFY_ACCOUNTS: Record<SpotifyAccountAlias, SpotifyAccountInfo> = {
	personal: {
		account: 'personal',
		integrationName: 'spotify',
		label: 'Personal Spotify',
		userIdHint: 'ajxrmwjv1y6so642k7b6qad0t',
		displayNameHint: 'Kent C. Dodds',
		fallbackDeviceId: '2084292936',
		defaultCollectionUri: 'spotify:user:kentcdodds:collection',
		useWhen:
			'Default account. Use for Kent\u2019s personal playback, playlists, library, and listening history unless the user asks for the family account.',
	},
	family: {
		account: 'family',
		integrationName: 'spotify-family',
		label: 'Family Spotify',
		userIdHint: '1mgpsy0fwzzsvuczyh4upar42',
		displayNameHint: 'Dodds Kids',
		fallbackDeviceId: null,
		defaultCollectionUri: null,
		useWhen:
			'Use when the user asks for the family / kids / household Spotify account, such as playlists meant for whole-home Sonos playback.',
	},
}

export const SPOTIFY_ACCOUNT_ALIASES = ['personal', 'family'] as const

const DEFAULT_ACCOUNT: SpotifyAccountAlias = 'personal'

function cloneAccount(account: SpotifyAccountInfo): SpotifyAccountInfo {
	return { ...account }
}

export function listSpotifyAccounts(): SpotifyAccountInfo[] {
	return SPOTIFY_ACCOUNT_ALIASES.map((alias) => cloneAccount(SPOTIFY_ACCOUNTS[alias]))
}

/**
 * Resolve an account alias or integration name to account info.
 * Unlike google-products, the account is optional and defaults to `personal`.
 */
export function resolveSpotifyAccount(
	input: { account?: string } | string = {},
): SpotifyAccountInfo {
	const account = (typeof input === 'string' ? input : input.account) ?? DEFAULT_ACCOUNT
	const fromAlias = SPOTIFY_ACCOUNTS[account as SpotifyAccountAlias]
	if (fromAlias) return cloneAccount(fromAlias)
	const fromIntegration = SPOTIFY_ACCOUNT_ALIASES.map((alias) => SPOTIFY_ACCOUNTS[alias]).find(
		(candidate) => candidate.integrationName === account,
	)
	if (fromIntegration) return cloneAccount(fromIntegration)
	throw new Error(
		'Unknown Spotify account "' +
			account +
			'". Pass one of: ' +
			SPOTIFY_ACCOUNT_ALIASES.join(', ') +
			' (defaults to personal when omitted).',
	)
}

export default function accounts() {
	return {
		accounts: listSpotifyAccounts(),
		aliases: [...SPOTIFY_ACCOUNT_ALIASES],
		defaultAccount: DEFAULT_ACCOUNT,
	}
}