npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cody-music

v2.10.8

Published

mac osx spotify and itunes music player controller, spotify audio features, itunes and spotify genre, and playlist control

Downloads

223

Readme

cody-music

Cody Music is an open source package designed to help you perform Mac iTunes and Spotify Web API playback functionality

Player control support

  • Mac Spotify and iTunes desktop
  • Spotify Web

Spotify web API support

  • Fetching Spotify audio features
  • Playlists (create, delete, fetch playlist tracks, replace playlist tracks)
  • Genre search (Recently updated to return highest frequency Spotify Genre)
  • Fetching Spotify devices
  • Access token refresh retry
  • Track recommendations from Spotify
  • Add and remove to the Liked playlist
  • Prevents adding duplicate playlists by name
  • Follow or unfollow a playlist
  • Spotify Recommendations

iTunes API support

  • Genre search

Installation

npm

$ npm install cody-music --save

yarn

$ yarn add cody-music

Running unit tests

$ npm test

Load the module

import {
    getRunningTrack,
    Track,
    PlayerType,
    TrackStatus,
    setConfig } from "cody-music";

...

// update the CodyMusic spotify credentials and other settings
setConfig({
    spotifyAccessToken: <spotify_access_token>,
    spotifyRefreshToken: <spotify_refresh_token>;
    spotifyClientSecret: <spotify_client_secret>;
    spotifyClientId: <spotify_client_id>;
    enableItunesDesktop: <enable_itunes_desktop_track_lookup>;
    enableSpotifyDesktop: <enable_spotify_desktop_track_lookup>;
    enableSpotifyApi: <enable_spotify_api>;
});

const track:Track = await getRunningTrack();

if (track.state === TrackStatus.Playing) {
    // track is playing
}

if (track.playerType === PlayerType.WebSpotify) {
    // track running has been identified as your spotify web player
}

OR

import * as CodyMusic from "cody-music";

OR

const CodyMusic = require("cody-music");

API

playTrack(uri)

Play a track with Music URI uri.

Specify either "Spotify" or "iTunes" (case-insensitive).

// get the track info using get state
await CodyMusic.getRunningTrack().then((track: Track) => {
    // returns the Track data
});

// play a specific spotify track
await CodyMusic.playTrack(
    "spotify",
    "spotify:track:2YarjDYjBJuH63dUIh9OWv"
).then((result) => {
    // track is playing
});

// play an iTunes track number
await CodyMusic.playTrack("itunes", 1).then((result) => {
    // track is playing
});

// handling errors
await CodyMusic.playTrack("spotify", 1000000000).then((result) => {
    // result will contain the "error" attribute with the error message
    if (result.error) {
        console.log(`Unable to play track, error: ${result.error}`);
    }
});
await CodyMusic.getRunningTrack().then((result) => {
    // result will be the best effort track that is playing.
    // i.e. if you have your itunes app running, it would show you that track
});

Full set of APIs

/**
 * Initialize/set music credentials and settings
 * @param config <CodyConfig>
 */
setConfig(config: CodyConfig)

/**
 * Valid types are: album, artist, playlist, and track
 * keywords: send the keywords to search against.
 * Use specific filter name if you want to search against certain
 * fields.
 * Example searchTracks("track:what a time artist:tom")
 * If you use track and artist and internally it doesn't return a match,
 * it will search again with only the track part of the query.
 *
 * @param string
 * @param limit (min of 1 and a max of 50)
 */
searchTracks(keywords: string, limit: number = 50)

/**
 * Valid types are: album, artist, playlist, and track
 * keywords: send the keywords to search against.
 * Use specific filter name if you want to search against certain
 * fields.
 * Example searchTracks("track:what a time artist:tom")
 *
 * @param string
 * @param limit (min of 1 and a max of 50)
 */
searchArtists(keywords: string, limit: number = 50)

/**
 * Returns true if the user has granted Mac OS access for iTunes control
 */
isItunesAccessGranted()

/**
 * Set this if you would like a general flag itunes is not supported,
 * but you will also need to set isItunesDesktopSongTrackingEnabled
 * to the same value if you want to ensure it's not returning itunes songs.
 *
 * Returns false if cody music has been configured to to disable it
 * or if it's the OS is not Mac,
 * otherwise it's set to true by default
 */
isItunesDesktopEnabled()

/**
 * This will allow or disallow song tracking.
 * Returns false if cody music has been configured to to disable it
 * or if it's the OS is not Mac,
 * otherwise it's set to true by default
 */
isItunesDesktopSongTrackingEnabled()

/**
 * Get the Spotify accessToken provided via through the setConfig api
 * @returns {string} the spotify access token string
 */
getSpotifyAccessToken()

/**
 * Refresh the Spotify accessToken
 * @returns {Promise<boolean>} whether or not the refresh was successful
 */
refreshSpotifyAccessToken()

/**
 * Returns false if cody music has been configured to to disable it,
 * otherwise it's set to true by default
 */
isSpotifyDesktopEnabled()

/**
 * Checks if the Spotify desktop or web player is running or not
 * @returns {Promise<boolean>}
 */
isSpotifyRunning()

/**
 * Checks if the iTunes desktop player is running or not
 * @returns {Promise<boolean>}
 */
isItunesRunning()

/**
 * Checks if one of the specified players is running
 * @param player {spotify|spotify-web|itunes}
 * @returns {Promise<boolean>}
 */
isPlayerRunning(player: PlayerName)

/**
 * Returns whether there's an active track,
 * (spotify web, spotify desktop, or itunes desktop)
 * @returns {Promise<boolean>}
 */
hasActiveTrack(): Promise<boolean>

/**
 * Returns the recommended tracks for the
 * @param trackIds (optional) track IDs or URIs (5 max)
 * @param limit (optional) will default to 40 if not specified
 * @param market (optional) will default to none if not specified
 * @param min_popularity (optional) will default to a min or 20
 * @param target_popularity (optional) will default to 100
 * @param seed_genres (optional) the supported spotify genres (5 max)
 * @param seed_genres (optional) artist IDs or URIs (5 max)
 * @param features (optional) supports the tunable track attributes using min_*, max_*, and target_*
 *   i.e. {max_valence: 0.3, target_valence: 0.1}
 */
getRecommendationsForTracks(
    trackIds: string[] = [],
    limit: number = 40,
    market: string = "",
    min_popularity: number = 20,
    target_popularity: number = 100,
    seed_genres: string[] = [],
    seed_artists: string[] = [],
    features: any = {}
): Promise<Track[]>

/**
 * Returns the currently running track.
 * Spotify web, desktop, or itunes desktop.
 * If it finds a spotify device but it's not playing, and mac iTunes is not playing
 * or paused, then it will return the Spotify track.
 * It will return an empty Track object if it's unable to
 * find a running track.
 * @returns {Promise<Track>}
 **/
getRunningTrack(): Promise<Track>

/**
 * Fetch the recently played spotify tracks
 * @param limit - The maximum number of items to return. Default: 50. Minimum: 1. Maximum: 50
 */
getSpotifyRecentlyPlayedTracks(
    limit: number = 50
): Promise<Track[]>

/**
 * Fetch the recently played spotify tracks
 * @param limit - The maximum number of items to return. Default: 50. Minimum: 1. Maximum: 50
 * @param before - Returns all items before (but not including) this cursor position
 */
getSpotifyRecentlyPlayedBefore(
    limit: number = 50,
    before: number = 0
): Promise<Track[]>

/**
 * Fetch the recently played spotify tracks
 * @param limit - The maximum number of items to return. Default: 50. Minimum: 1. Maximum: 50
 * @param after - Returns all items before (but not including) this cursor position
 */
getSpotifyRecentlyPlayedAfter(
    limit: number = 50,
    after: number = 0
): Promise<Track[]>

/**
 * Fetch the spotify player context
 * Info about the device, is playing state, etc.
 */
getSpotifyPlayerContext(): Promise<PlayerContext>

/**
 * Returns a track by the given spotify track id
 * @param id
 * @param includeFullArtistData (optional - if true it will return full artist info)
 * @package includeAudioFeaturesData (optional)
 * @param includeGenre (optional)
 */
getSpotifyTrackById(
    id: string,
    includeFullArtistData: boolean = false,
    includeAudioFeaturesData: boolean = false,
    includeGenre: boolean = false
): Promise<Track>

/**
 * Returns tracks by the given spotify track ids
 * @param ids
 * @param includeFullArtistData (optional - if true it will return full artist info)
 * @package includeAudioFeaturesData (optional)
 * @param includeGenre (optional)
 */
export async function getSpotifyTracks(
    ids: string[],
    includeFullArtistData: boolean = false,
    includeAudioFeaturesData: boolean = false,
    includeGenre: boolean = false
): Promise<Track[]>

/**
 * Returns the track of a given player {spotify|spotify-web|itunes}
 * - Spotify does not return a "genre"
 * - duration is in milliseconds
 * @param player {spotify|spotif-web|itunes}
 * @returns {artist, album, genre, disc_number, duration, played_count, track_number, id, name, state}
 */
getTrack(player: PlayerName): Promise<Track>

/**
 * Returns the tracks that are found for itunes
 * @param player {itunes}
 * @param playListName
 */
getTracksByPlaylistName(
    player: PlayerName,
    playListName: string
): Promise<Track[]>

/**
 * Returns tracks of a Spotify Album
 * @param albumId: spotify album uri or album ID
 **/
getSpotifyAlbumTracks(albumId: string): Promise<Track[]>

/**
 * Currently only returns Spotify Web tracks not associated with a playlist.
 * @param player
 * @param qsOptions
 */
getSpotifyLikedSongs(
    qsOptions: any = {}
): Promise<Track[]>

/**
 * Currently only returns Spotify Web tracks not associated with a playlist.
 * @param player
 * @param qsOptions
 */
getSavedTracks(player: PlayerName, qsOptions: any = {}): Promise<Track[]>


/**
 * Returns a playlist by ID
 * @param playlist_id ID is preferred, but we'll transform a URI to an ID
 */
getSpotifyPlaylist(playlist_id: string): Promise<PlaylistItem>

/**
 * Returns the tracks that are found by the given playlist name
 * - currently spofity-web support only
 * @param player {spotify-web}
 * @param playlist_id (optional)
 * @param qsOptions (optional) {offset, limit}
 */
getPlaylistTracks(
    player: PlayerName,
    playlist_id: string,
    qsOptions: any = {}
): Promise<CodyResponse>

/**
 * Plays a playlist at the beginning if the starting track id is not provided.
 * @param playlistId either the ID or URI of the playlist
 * @param startingTrackId either the ID or URI of the track
 * @param deviceId
 */
playSpotifyPlaylist(
    playlistId: string,
    startingTrackId: string = "",
    deviceId: string = ""
)

/**
 * Plays a specific track on the Spotify or iTunes desktop
 * @param player
 * @param params
 * spotify example  ["spotify:track:0R8P9KfGJCDULmlEoBagcO", "spotify:album:6ZG5lRT77aJ3btmArcykra"]
 *   -- provide the trackID then the album or playlist ID
 *   -- they can either be in either URI or ID format
 * itunes example   ["Let Me Down Slowly", "MostRecents"]
 *   -- provide the track name then the playlist name
 */
playTrackInContext(player: PlayerName, params: any[])

/**
 * Mac iTunes only
 * This will allow you to play a playlist starting at a specific playlist track number.
 */
playItunesTrackNumberInPlaylist(
    playlistName: string,
    trackNumber: number
)

/**
 * Quits/closes the mac Spotify or iTunes player
 * @param player
 */
quitMacPlayer(player: PlayerName)

/**
 * This is only meant for Mac iTunes or Mac Spotify desktop
 * @param player
 * @param params
 */
playTrackInLibrary(player: PlayerName, params: any[])

/**
 * Initiate and play the specified Spotify device
 * @param device_id {string}
 */
playSpotifyDevice(device_id: string)

/**
 * Initiate and play the specified Spotify device
 * @param device_id {string}
 * @param play {boolean} true to play and false to keep current play state
 */
transferSpotifyDevice(device_id: string, play: boolean)

/**
 * Fetch the user's profile
 */
getUserProfile(): Promise<SpotifyUser>

/**
 * Helper API to return whether or not the user is logged in to their spotify account or not.
 * It's not fool proof as it only determines if there are any devices found or not.
 * {oauthActivated, loggedIn}
 */
spotifyAuthState(): Promise<SpotifyAuthState>

/**
 * Initiate the play command for a specific player
 * @param player {spotify|spotify-web|itunes}
 * @param options { uris, device_id }
 * example
 * -- the uris can be in either URI or ID format
 * {device_id: <spotify_device_id>, uris: ["spotify:track:4iV5W9uYEdYUVa79Axb7Rh", "spotify:track:1301WleyT98MSxVHPZCA6M"], context_uri: <playlist_uri, album_uri>}
 */
play(player: PlayerName, options: any = {})

/**
 * Play a specific spotify track by trackId (it can be the URI or the ID)
 * @param trackId
 * @param deviceId (optional)
 */
playSpotifyTrack(trackId: string, deviceId: string = "")

/**
 * Initiate the play command for a given trackId for a specific player
 * @param player {spotify|spotify-web|itunes}
 * @param trackId {any (string|number)}
 */
playTrack(PlayerName: PlayerName, trackId: any)

/**
 * Initiate the pause command for a given player
 * @param player {spotify|spotify-web|itunes}
 * @param options
 */
pause(player: PlayerName, options: any = {})

/**
 * Initiate the play/pause command for a given player
 * @param player {spotify|spotify-web|itunes}
 * @param options
 */
playPause(player: PlayerName)

/**
 * Initiate the next command for a given player
 * @param player {spotify|spotify-web|itunes}
 * @param options
 */
next(player: PlayerName, options: any = {})

/**
 * Initiate the previous command for a given player
 * @param player {spotify|spotify-web|itunes}
 * @param options
 */
previous(player: PlayerName, options: any = {})

/**
 * Repeats a playlist
 * @param player
 * @param deviceId
 */
setRepeatPlaylist(player: PlayerName, deviceId: string = "")

/**
 * Repeats a track
 * @param player
 * @param deviceId
 */
setRepeatTrack(player: PlayerName, deviceId: string = "")

/**
 * Turn repeat off
 * @param player
 * @param deviceId
 */
setRepeatOff(player: PlayerName, deviceId: string = "")

/**
 * Turn on/off repeat for a given player
 * @param player {spotify|spotify-web|itunes}
 * @param options
 */
setRepeat(player: PlayerName, repeat: boolean)

/**
 * Turn on/off shuffling for a given player
 * @param player {spotify|spotify-web|itunes}
 * @param shuffle (true or false)
 * @param deviceId (optional)
 */
setShuffle(player: PlayerName, shuffle: boolean, deviceId: string = "")

/**
 * Return whether shuffling is on or not
 * @param player {spotify|spotify-web|itunes}
 */
isShuffling(player: PlayerName)

/**
 * Returns whether the player is on repeat or not
 * - spotify returns true or false, and itunes returns "off", "one", "all"
 * @param player {spotify|spotify-web|itunes}
 */
isRepeating(player: PlayerName)

/**
 * Update the players volume
 * @param player {spotify|spotify-web|itunes}
 * @param volume {0-100}
 */
setVolume(player: PlayerName, volume: number)

/**
 * Increments the players volume by a number
 * @param player {spotify|spotify-web|itunes}
 */
volumeUp(player: PlayerName)

/**
 * Decrements the players volume by a number
 * @param player {spotify|spotify-web|itunes}
 */
volumeDown(player: PlayerName)

/**
 * Mutes the players volume
 * @param player {spotify|spotify-web|itunes}
 */
mute(player: PlayerName)

/**
 * Unmutes the players volume
 * @param player {spotify|spotify-web|itunes}
 */
unmute(player: PlayerName)

/**
 * Unmutes the players volume
 * @param player {spotify|spotify-web|itunes}
 */
setItunesLoved(loved: boolean)

/**
 * Save tracks to your liked playlist
 * @param trackIds (i.e. ["4iV5W9uYEdYUVa79Axb7Rh", "1301WleyT98MSxVHPZCA6M"])
 */
saveToSpotifyLiked(trackIds: string[]): Promise<CodyResponse>

/**
 * Remove tracks from your liked playlist
 * @param trackIds (i.e. ["4iV5W9uYEdYUVa79Axb7Rh", "1301WleyT98MSxVHPZCA6M"])
 */
removeFromSpotifyLiked(
    trackIds: string[]
): Promise<CodyResponse>

/**
 * Returns the playlists for a given player
 * @param player {spotify|spotify-web|itunes}
 * @param (optional) {limit, offset, all}
 */
getPlaylists(
    player: PlayerName,
    qsOptions: any = {}
): Promise<PlaylistItem[]>

/**
 * Get the full list of the playlist names for a given player
 * @param player {spotify|spotify-web|itunes}
 * @param qsOptions (optional) {limit, offset}
 */
getPlaylistNames(
    player: PlayerName,
    qsOptions: any = {}
): Promise<string[]>

/**
 * Launches a player device
 * @param playerName {spotify|spotify-web|itunes}
 * @param options (spotify-web only) {playlist_id | album_id | track_id }
 */
launchPlayer(playerName: PlayerName, options: any = {})

/**
 * Plays a Spotify track within a playlist.
 * It will also launch Spotify if it is not already available by checking the device Ids.
 * @param trackId (optional) If it's not supplied then the playlistId must be provided
 * @param playlistId (optional) If it's not supplied then the trackId must be provided
 * @param playerName (optional) SpotifyWeb or SpotifyDesktop
 */
launchAndPlaySpotifyTrack(trackId: string = "", playlistId: string = "", playerName: string = PlayerName.SpotifyWeb)

/**
 * Plays a Spotify Mac Desktop track within a playlist.
 * It will also launch Spotify if it is not already available by checking the device Ids.
 * @param trackId (optional) If it's not supplied then the playlistId must be provided
 * @param playlistId (optional) If it's not supplied then the trackId must be provided
 */
playSpotifyMacDesktopTrack(trackId: string = "", playlistId: string = "")

/**
 * Returns available Spotify devices
 * @returns {Promise<PlayerDevice[]>}
 */
getSpotifyDevices(): Promise<PlayerDevice[]>

/**
 * Returns the genre for a provided arguments
 * @param artist {string} is required
 * @param songName {string} is optional
 * @param spotifyArtistId {string} is optional (uri or id is fine)
 */
getGenre(
    artist: string,
    songName: string = "",
    spotifyArtistId: string = ""
): Promise<string>

/**
 * Returns the spotify genre for a provided arguments
 * @param artist {string} is required
 */
getSpotifyGenre(artist: string): Promise<string>

/**
 * Returns the highest frequency single genre from a list
 **/
getHighestFrequencySpotifyGenre(genreList: string[]): string

/**
 * Returns the spotify genre for a provided arguments
 * @param spotifyArtistId {string} is required (uri or id is fine)
 */
getSpotifyGenreByArtistId(spotifyArtistId: string): Promise<string> {

/**
 * Returns the recent top tracks Spotify for a user.
 */
getTopSpotifyTracks(): Promise<Track[]>

/**
 * Returns the audio features of the given track IDs
 * @param ids these are the track ids (sans spotify:track)
 */
getSpotifyAudioFeatures(
    ids: string[]
): Promise<SpotifyAudioFeature[]>

/**
 * Create a playlist for a Spotify user. (The playlist will be empty until you add tracks.)
 * @param name the name of the playlist you want to create
 * @param isPublic if the playlist will be public or private
 * @param description (Optioal) displayed in Spotify Clients and in the Web API
 */
createPlaylist(
    name: string,
    isPublic: boolean,
    description: string = ""
)

/**
 * Deletes a playlist of a given playlist ID.
 * @param playlist_id
 */
deletePlaylist(playlist_id: string): CodyResponse

/**
 * Follow a playlist of a given playlist ID.
 * @param playlist_id (uri or id)
 */
followPlaylist(playlist_id: string): CodyResponse

/**
 * Replace tracks of a given playlist. This will wipe out
 * the current set of tracks and add the tracks specified.
 * @param playlist_id
 * @param track_ids
 */
replacePlaylistTracks(
    playlist_id: string,
    track_ids: string[]
)

/**
 * Add tracks to a given Spotify playlist.
 * @param playlist_id the Spotify ID for the playlist
 * @param tracks Tracks should be the uri (i.e. "spotify:track:4iV5W9uYEdYUVa79Axb7Rh")
 * but if it's only the id (i.e. "4iV5W9uYEdYUVa79Axb7Rh") this will add
 * the uri part "spotify:track:"
 * @param position The position to insert the tracks, a zero-based index.
 */
addTracksToPlaylist(
    playlist_id: string,
    tracks: string[],
    position: number = 0
)

/**
 * Remove tracks from a given Spotify playlist.
 * @param playlist_id the Spotify ID for the playlist
 * @param tracks Tracks should be the uri (i.e. "spotify:track:4iV5W9uYEdYUVa79Axb7Rh")
 * but if it's only the id (i.e. "4iV5W9uYEdYUVa79Axb7Rh") this will add
 * the uri part "spotify:track:"
 */
removeTracksFromPlaylist(
    playlist_id: string,
    tracks: string[]
)

/**
 * Returns whether or not the spotify access token has been provided.
 * @returns <boolean>
 */
requiresSpotifyAccessInfo(): boolean

/**
 * Deprecated - use "getTrack(player)"
 */
getPlayerState(player: PlayerName): Promise<Track>

/**
 * Deprecated - use "getRunningTrack()" instead
 */
getCurrentlyRunningTrackState(): Promise<Track>

/**
 * Deprecated - please use "getPlayerState"
 */
getState(player: PlayerName): Promise<Track>

/**
 * Deprecated - please use "launchPlayer('spotify')"
 **/
startSpotifyIfNotRunning()

/**
 * Deprecated - please use "launchPlayer('itunes')"
 */
startItunesIfNotRunning()

/**
 * Deprecated - please use "isSpotifyRunning" or "isItunesRunning"
 */
isRunning(player: PlayerName): Promise<boolean>

/**
 * Deprecated - please use "setRepat(player, repeat)"
 */
repeatOn(player: PlayerName)

/**
 * Deprecated - please use "setRepat(player, repeat)"
 */
repeatOff(player: PlayerName)

/**
 * Deprecated - please use "unmute(player)"
 */
unMute(player: PlayerName)

/**
 * Deprecated - please use "setConfig(config: CodyConfig)"
 * Set Credentials (currently only supports Spotify)
 * Accepted credentials: clientId, clientSecret, refreshToken, accessToken
 * @param credentials
 */
setCredentials(credentials: any)

/**
 * Deprecated - please use "getSpotifyAccessToken()"
 * Get the accessToken provided via through the setCredentials api
 * @returns {string} the access token string
 */
getAccessToken()

Contributors

License