Skip to content

Apple Music catalog ​

ts
import { fetchAnimatedArtwork, fetchLyrics } from '@corewire/devices/catalog';

A device never goes to the internet by itself. The /catalog entry point holds two functions that do, and you decide when to call them. Both take an id from the now playing snapshot and an optional abort signal.

Animated artwork ​

fetchAnimatedArtwork(albumId, options?) reads the public Apple Music page of an album and returns its animated artwork, as AnimatedArtwork entries with a format, a url and source: 'apple-music'. It needs no account.

ts
import { fetchAnimatedArtwork } from '@corewire/devices/catalog';

const item = tv.nowPlaying.current;

if (item?.storeAlbumId) {
    const artwork = await fetchAnimatedArtwork(item.storeAlbumId, {storefront: 'nl'});

    // The item may have changed while the request ran.
    if (tv.nowPlaying.current?.storeAlbumId === item.storeAlbumId) {
        console.log(artwork.map(entry => `${entry.format}: ${entry.url}`));
    }
}
OptionMeaning
storefrontA two-letter storefront; us when omitted
signalCancels the request

The request times out after 10 seconds. A page over 2 MB is refused. Only video URLs on apple.com and mzstatic.com hosts are returned.

Lyrics ​

fetchLyrics(catalogId, options) fetches the time-synced lyrics of a song, as a TTML string, from the Apple Music catalog. It resolves null when the song has none.

ts
import { fetchLyrics } from '@corewire/devices/catalog';

const id = tv.nowPlaying.current?.lyricsId;

if (id) {
    const ttml = await fetchLyrics(id, {
        bearerToken: process.env.APPLE_MUSIC_BEARER ?? '',
        musicUserToken: process.env.APPLE_MUSIC_USER_TOKEN ?? '',
        storefront: 'us'
    });

    console.log(ttml?.slice(0, 200));
}
OptionMeaning
bearerTokenAn Apple Music developer token
musicUserTokenThe music user token of a subscribed account
storefrontThe two-letter storefront of that account
language, scriptOptional localization of the lyrics
signalCancels the request

This uses a private catalog endpoint. The tokens go with this one request and are never stored. Redirects are refused, so the tokens never reach another host. The request times out after 15 seconds. A 401 or 403 rejects with a message that says which token or subscription is at fault.

Errors ​

Both functions throw a CatalogError, also for a network failure or a timeout. Its reason says what went wrong, and status holds the HTTP status when there was one, or null.

ReasonWhen
invalidArgumentAn id that is not numeric, a storefront that is not two letters, or a missing token
unauthorizedApple Music rejected the bearer token (401)
forbiddenApple Music denied access: the user token, the subscription or the storefront (403)
requestFailedThe request failed, timed out or got another error status
invalidResponseThe response was empty or could not be read
tooLargeThe response is over 2 MB
ts
import { CatalogError, fetchLyrics } from '@corewire/devices/catalog';

try {
    await fetchLyrics('1440857781', {
        bearerToken: process.env.APPLE_MUSIC_BEARER ?? '',
        musicUserToken: process.env.APPLE_MUSIC_USER_TOKEN ?? '',
        storefront: 'us'
    });
} catch (err) {
    if (err instanceof CatalogError && err.reason === 'unauthorized') {
        console.warn('Renew the developer token.');
    }
}

CoreWire is released under the MIT license.