<|start_filename|>src/node-spotify.cc<|end_filename|>
#include "Application.h"
#include "exceptions.h"
#include "objects/node/NodeSpotify.h"
#include "objects/node/NodePlaylist.h"
#include "objects/node/NodePlaylistContainer.h"
#include "objects/node/NodeTrack.h"
#include "objects/node/NodeTrackExtended.h"
#include "objects/node/NodePlayer.h"
#include "objects/node/NodeAlbum.h"
#include "objects/node/NodeArtist.h"
#include "objects/node/NodeSearch.h"
#include "objects/node/NodePlaylistFolder.h"
#include "objects/node/NodeUser.h"

#include <node.h>
#include <nan.h>

#ifdef NODE_SPOTIFY_NATIVE_SOUND
#include "audio/NativeAudioHandler.h"
#endif

Application* application;

static Handle<Object> getInternal() {
  Local<Object> internal = Nan::New<Object>();
  Local<Object> protos = Nan::New<Object>();
  protos->Set(Nan::New<String>("Playlist").ToLocalChecked(), NodePlaylist::getConstructor());
  protos->Set(Nan::New<String>("Track").ToLocalChecked(), NodeTrack::getConstructor());
  protos->Set(Nan::New<String>("TrackExtended").ToLocalChecked(), NodeTrackExtended::getConstructor());
  protos->Set(Nan::New<String>("PlaylistContainer").ToLocalChecked(), NodePlaylistContainer::getConstructor());
  protos->Set(Nan::New<String>("Artist").ToLocalChecked(), NodeArtist::getConstructor());
  protos->Set(Nan::New<String>("Album").ToLocalChecked(), NodeAlbum::getConstructor());
  protos->Set(Nan::New<String>("User").ToLocalChecked(), NodeUser::getConstructor());
  protos->Set(Nan::New<String>("PlaylistFolder").ToLocalChecked(), NodePlaylistFolder::getConstructor());
  internal->Set(Nan::New<String>("protos").ToLocalChecked(), protos);

  return internal;
}

NAN_METHOD(CreateNodespotify) {
  //initiate the javascript ctors and prototypes
  NodePlaylist::init();
  NodeTrack::init();
  NodeTrackExtended::init();
  NodeArtist::init();
  NodePlayer::init();
  NodeAlbum::init();
  NodeSearch::init();
  NodeSpotify::init();
  NodePlaylistFolder::init();
  NodePlaylistContainer::init();
  NodeUser::init();

  //initialize application struct
  application = new Application();

#ifdef NODE_SPOTIFY_NATIVE_SOUND
  application->audioHandler = std::unique_ptr<AudioHandler>(new NativeAudioHandler());
#endif

  v8::Handle<v8::Object> options;
  if(info.Length() < 1) {
    options = Nan::New<Object>();
  } else {
    if(!info[0]->IsObject()) {
      Nan::ThrowError("Please provide an object to the node-spotify initializer function");
      return;
    }
    options = info[0]->ToObject();
  }

  NodeSpotify* nodeSpotify;
  try {
    nodeSpotify = new NodeSpotify(options);
  } catch (const FileException& e) {
    Nan::ThrowError("Appkey file not found");
    return;
  } catch (const SessionCreationException& e) {
    Nan::ThrowError(e.message.c_str());
    return;
  }
  v8::Local<Object> spotifyObject = nodeSpotify->createInstance();

  //Set some fields on the nodeSpotify object
  spotifyObject->Set(Nan::New<String>("Search").ToLocalChecked(), NodeSearch::getConstructor());//TODO: this is ugly but didn't work when done in the NodeSpotify ctor
  spotifyObject->Set(Nan::New<String>("internal").ToLocalChecked(), getInternal());
  application->player = std::make_shared<Player>();
  NodePlayer* nodePlayer = new NodePlayer(application->player);
  spotifyObject->Set(Nan::New<String>("player").ToLocalChecked(), nodePlayer->createInstance());
  info.GetReturnValue().Set(spotifyObject);
};

static void init(v8::Handle<v8::Object> exports, v8::Handle<v8::Object> module) {
  Nan::SetMethod(module, "exports", CreateNodespotify);
}

NODE_MODULE(nodespotify, init)


<|start_filename|>src/objects/node/NodeArtist.h<|end_filename|>
#ifndef _NODE_ARTIST_H
#define _NODE_ARTIST_H

#include "V8Browseable.h"
#include "../spotify/Artist.h"

#include <nan.h>
#include <memory>

using namespace v8;

class NodeArtist : public V8Browseable<NodeArtist> {
private:
  std::unique_ptr<Artist> artist;
public:
  NodeArtist(std::unique_ptr<Artist> artist);
  ~NodeArtist();
  static NAN_GETTER(getName);
  static NAN_GETTER(getLink);
  static NAN_METHOD(browse);
  static NAN_GETTER(getTracks);
  static NAN_GETTER(getTophitTracks);
  static NAN_GETTER(getAlbums);
  static NAN_GETTER(getSimilarArtists);
  static NAN_GETTER(getBiography);
  static NAN_GETTER(isLoaded);
  static void init();
};

#endif


<|start_filename|>src/audio/alsa-audio.c<|end_filename|>
/*
 * Copyright (c) 2006-2009 Spotify Ltd
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 *
 * ALSA audio output driver.
 *
 * This file is part of the libspotify examples suite.
 */

#include <asoundlib.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

#include "audio.h"

extern int audio_stopThread;

static snd_pcm_t *alsa_open(char *dev, int rate, int channels) {
	snd_pcm_hw_params_t *hwp;
	snd_pcm_sw_params_t *swp;
	snd_pcm_t *h;
	int r;
	int dir;
	snd_pcm_uframes_t period_size_min;
	snd_pcm_uframes_t period_size_max;
	snd_pcm_uframes_t buffer_size_min;
	snd_pcm_uframes_t buffer_size_max;
	snd_pcm_uframes_t period_size;
	snd_pcm_uframes_t buffer_size;

	if ((r = snd_pcm_open(&h, dev, SND_PCM_STREAM_PLAYBACK, 0) < 0))
		return NULL;

	hwp = alloca(snd_pcm_hw_params_sizeof());
	memset(hwp, 0, snd_pcm_hw_params_sizeof());
	snd_pcm_hw_params_any(h, hwp);

	snd_pcm_hw_params_set_access(h, hwp, SND_PCM_ACCESS_RW_INTERLEAVED);
	snd_pcm_hw_params_set_format(h, hwp, SND_PCM_FORMAT_S16_LE);
	snd_pcm_hw_params_set_rate(h, hwp, rate, 0);
	snd_pcm_hw_params_set_channels(h, hwp, channels);

	/* Configurue period */

	dir = 0;
	snd_pcm_hw_params_get_period_size_min(hwp, &period_size_min, &dir);
	dir = 0;
	snd_pcm_hw_params_get_period_size_max(hwp, &period_size_max, &dir);

	period_size = 1024;

	dir = 0;
	r = snd_pcm_hw_params_set_period_size_near(h, hwp, &period_size, &dir);

	if (r < 0) {
		fprintf(stderr, "audio: Unable to set period size %lu (%s)\n",
		        period_size, snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	dir = 0;
	r = snd_pcm_hw_params_get_period_size(hwp, &period_size, &dir);

	if (r < 0) {
		fprintf(stderr, "audio: Unable to get period size (%s)\n",
		        snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	/* Configurue buffer size */

	snd_pcm_hw_params_get_buffer_size_min(hwp, &buffer_size_min);
	snd_pcm_hw_params_get_buffer_size_max(hwp, &buffer_size_max);
	buffer_size = period_size * 4;

	dir = 0;
	r = snd_pcm_hw_params_set_buffer_size_near(h, hwp, &buffer_size);

	if (r < 0) {
		fprintf(stderr, "audio: Unable to set buffer size %lu (%s)\n",
		        buffer_size, snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	r = snd_pcm_hw_params_get_buffer_size(hwp, &buffer_size);

	if (r < 0) {
		fprintf(stderr, "audio: Unable to get buffer size (%s)\n",
		        snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	/* write the hw params */
	r = snd_pcm_hw_params(h, hwp);

	if (r < 0) {
		fprintf(stderr, "audio: Unable to configure hardware parameters (%s)\n",
		        snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	/*
	 * Software parameters
	 */

	swp = alloca(snd_pcm_sw_params_sizeof());
	memset(hwp, 0, snd_pcm_sw_params_sizeof());
	snd_pcm_sw_params_current(h, swp);

	r = snd_pcm_sw_params_set_avail_min(h, swp, period_size);

	if (r < 0) {
		fprintf(stderr, "audio: Unable to configure wakeup threshold (%s)\n",
		        snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	snd_pcm_sw_params_set_start_threshold(h, swp, 0);

	if (r < 0) {
		fprintf(stderr, "audio: Unable to configure start threshold (%s)\n",
		        snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	r = snd_pcm_sw_params(h, swp);

	if (r < 0) {
		fprintf(stderr, "audio: Cannot set soft parameters (%s)\n",
		snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	r = snd_pcm_prepare(h);
	if (r < 0) {
		fprintf(stderr, "audio: Cannot prepare audio for playback (%s)\n",
		snd_strerror(r));
		snd_pcm_close(h);
		return NULL;
	}

	return h;
}

void audio_start(void *aux)
{
	audio_fifo_t *audioFifo = aux;
	snd_pcm_t *h = NULL;
	int c;
	int cur_channels = 0;
	int cur_rate = 0;

	audio_fifo_data_t *audioData;

    for (;!audio_stopThread;) {
        audioData = audio_get_native(audioFifo);
        if(audioData == NULL) {
            break;
        }

		if (!h || cur_rate != audioData->sampleRate || cur_channels != audioData->channels) {
			if (h) snd_pcm_close(h);

			cur_rate = audioData->sampleRate;
			cur_channels = audioData->channels;

			h = alsa_open("default", cur_rate, cur_channels);

			if (!h) {
				fprintf(stderr, "Unable to open ALSA device (%d channels, %d Hz), dying\n",
				        cur_channels, cur_rate);
				exit(1);
			}
		}

		c = snd_pcm_wait(h, 1000);

		if (c >= 0)
			c = snd_pcm_avail_update(h);

		if (c == -EPIPE)
			snd_pcm_prepare(h);

		snd_pcm_writei(h, audioData->samples, audioData->numberOfSamples);
		free(audioData);
	}
}


<|start_filename|>src/objects/node/NodePlaylistFolder.cc<|end_filename|>
#include "NodePlaylistFolder.h"

NodePlaylistFolder::NodePlaylistFolder(std::shared_ptr<PlaylistFolder> _playlistFolder) : playlistFolder(_playlistFolder) {

};

NodePlaylistFolder::~NodePlaylistFolder() {

}

NAN_GETTER(NodePlaylistFolder::getName) {
  NodePlaylistFolder* nodePlaylistFolder = Nan::ObjectWrap::Unwrap<NodePlaylistFolder>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodePlaylistFolder->playlistFolder->name().c_str()).ToLocalChecked());
}

NAN_GETTER(NodePlaylistFolder::getType) {
  NodePlaylistFolder* nodePlaylistFolder = Nan::ObjectWrap::Unwrap<NodePlaylistFolder>(info.This());
  info.GetReturnValue().Set(Nan::New<Number>(nodePlaylistFolder->playlistFolder->type()));
}

void NodePlaylistFolder::init() {
  Local<FunctionTemplate> constructorTemplate = NodeWrapped::init("PlaylistFolder");
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("name").ToLocalChecked(), getName);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("type").ToLocalChecked(), getType);
  NodePlaylistFolder::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>src/callbacks/SessionCallbacks_Audio.cc<|end_filename|>
/**
Everything audio related in Session Callbacks.
**/
#include "SessionCallbacks.h"
#include "../Application.h"

extern Application* application;

void SessionCallbacks::end_of_track(sp_session* session) {
  sp_session_player_unload(application->session);

  if(!endOfTrackCallback.IsEmpty()) {
    Nan::HandleScope scope;
    endOfTrackCallback.Call(0, {});
  }
}

void SessionCallbacks::start_playback(sp_session* session) {
}

void SessionCallbacks::stop_playback(sp_session* session) {

}


<|start_filename|>src/objects/node/NodePlaylistContainer.h<|end_filename|>
#ifndef _NODE_PLAYLIST_CONTAINER_H
#define _NODE_PLAYLIST_CONTAINER_H

#include "NodeWrapped.h"
#include "../spotify/PlaylistContainer.h"
#include "../../callbacks/PlaylistContainerCallbacksHolder.h"

#include <nan.h>
#include <memory>

using namespace v8;

class NodePlaylistContainer : public NodeWrapped<NodePlaylistContainer> {
private:
  std::shared_ptr<PlaylistContainer> playlistContainer;
  PlaylistContainerCallbacksHolder playlistContainerCallbacksHolder;
public:
  NodePlaylistContainer(std::shared_ptr<PlaylistContainer> playlistContainer);
  ~NodePlaylistContainer();
  static NAN_GETTER(getOwner);
  static NAN_METHOD(getPlaylist);
  static NAN_GETTER(isLoaded);
  static NAN_GETTER(getNumPlaylists);
  static NAN_METHOD(addPlaylist);
  static NAN_METHOD(addFolder);
  static NAN_METHOD(deletePlaylist);
  static NAN_METHOD(movePlaylist);
  static NAN_METHOD(on);
  static NAN_METHOD(off);
  static void init();
};

#endif


<|start_filename|>src/objects/spotify/TrackExtended.cc<|end_filename|>
#include "TrackExtended.h"

TrackExtended::TrackExtended(sp_track* _track, sp_playlist* _playlist, int _position) : Track(_track), playlist(_playlist), position(_position) {
  sp_playlist_add_ref(playlist);
}

TrackExtended::~TrackExtended() {
  sp_playlist_release(playlist);
}

std::unique_ptr<User> TrackExtended::creator() {
  std::unique_ptr<User> creator;
  sp_user* spCreator = sp_playlist_track_creator(playlist, position);
  if(spCreator != nullptr) {
    creator = std::unique_ptr<User>(new User(spCreator));
  }
  return creator;
}

bool TrackExtended::seen() {
  return sp_playlist_track_seen(playlist, position);
}

void TrackExtended::seen(bool seen) {
  sp_playlist_track_set_seen(playlist, position, seen);
}

std::string TrackExtended::message()  {
  std::string message;
  const char* spMessage = sp_playlist_track_message(playlist, position);
  if(spMessage != nullptr) {
    message = std::string(spMessage);
  }
  return message;
}

double TrackExtended::createTime() {
  return sp_playlist_track_create_time(playlist, position);
}


<|start_filename|>test/basetest.js<|end_filename|>
var spotify = require('../')({appkeyFile: '../spotify_appkey.key'});
var loginData = require('./loginData.js');

/*
 * The user probably wants to logout before quitting, so we set a timeout.
 */
function quit() {
  setTimeout(function() {
    console.log('Exiting');
    process.exit();
  }, 5000);
}

function executeTest(testerFunction) {
  var ready = function() {
    console.log('Awaiting caches...');
    setTimeout(testerFunction, 3000);
  };
  spotify.on({
    ready: ready
  });
  spotify.login(loginData.user, loginData.password, false, false);
}

module.exports = {
  executeTest: executeTest,
  quit: quit,
  spotify: spotify
};


<|start_filename|>src/objects/spotify/Album.cc<|end_filename|>
#include "Album.h"
#include "../../utils/ImageUtils.h"
#include "../../Application.h"
#include "../../callbacks/AlbumBrowseCallbacks.h"

extern Application* application;

Album::Album(sp_album* _album) : album(_album), cover(nullptr), nodeObject(nullptr), albumBrowse(nullptr) {
  sp_album_add_ref(album);
};

Album::Album(const Album& other) : album(other.album), cover(other.cover) {
  sp_album_add_ref(album);
  if(cover != nullptr) {
    sp_image_add_ref(cover);
  }
  if(albumBrowse != nullptr) {
    sp_albumbrowse_add_ref(albumBrowse);
  }
};

Album::~Album() {
  sp_album_release(album);
  if(cover != nullptr) {
    sp_image_release(cover);
  }
  if(albumBrowse != nullptr) {
    sp_albumbrowse_release(albumBrowse);
  }
};

std::string Album::name() {
  std::string name;
  if(sp_album_is_loaded(album)) {
    name = std::string(sp_album_name(album));
  } else {
    name = "Loading...";
  }
  return name;
}

std::string Album::link() {
  std::string link;
  if(sp_album_is_loaded(album)) {
    char linkChar[256];
    sp_link* spLink = sp_link_create_from_album(album);
    sp_link_as_string(spLink, linkChar, 256);
    link = std::string(linkChar);
    sp_link_release(spLink);
  }
  return link;
}

std::string Album::coverLink(sp_image_size size) {
  std::string link;
  if(sp_album_is_loaded(album)) {
    char linkChar[256];
    sp_link* spLink = sp_link_create_from_album_cover(album, size);
    sp_link_as_string(spLink, linkChar, 256);
    link = std::string(linkChar);
    sp_link_release(spLink);
  }
  return link;
}

std::vector<std::shared_ptr<Track>> Album::tracks() {
  std::vector<std::shared_ptr<Track>> tracks;
  if(sp_albumbrowse_is_loaded(albumBrowse)) {
    int numTracks = sp_albumbrowse_num_tracks(albumBrowse);
    tracks.resize(numTracks);
    for(int i = 0; i < numTracks; i++) {
      tracks[i] = std::make_shared<Track>(sp_albumbrowse_track(albumBrowse, i));
    }
  }
  return tracks;
}

std::string Album::review() {
  std::string review;
  if(sp_albumbrowse_is_loaded(albumBrowse)) {
    review = std::string(sp_albumbrowse_review(albumBrowse));
  }
  return review;
}

std::vector<std::string> Album::copyrights() {
  std::vector<std::string> copyrights;
  if(sp_albumbrowse_is_loaded(albumBrowse)) {
    int numCopyrights = sp_albumbrowse_num_copyrights(albumBrowse);
    copyrights.resize(numCopyrights);
    for(int i = 0; i < numCopyrights; i++) {
      copyrights[i] = std::string(sp_albumbrowse_copyright(albumBrowse, i));
    }
  }
  return copyrights;
}

std::unique_ptr<Artist> Album::artist() {
  std::unique_ptr<Artist> artist;
  if(sp_albumbrowse_is_loaded(albumBrowse)) {
    artist = std::unique_ptr<Artist>(new Artist(sp_albumbrowse_artist(albumBrowse)));
  }
  return artist;
}

std::string Album::coverBase64(sp_image_size size) {
  std::string cover;
  if(sp_album_is_loaded(album)) {
    const byte* coverId = sp_album_cover(album, size);
    if(coverId != nullptr) {
      sp_image* image = sp_image_create(application->session, coverId);
      if(sp_image_is_loaded(image)) {
        this->cover = image;
        cover = std::string(ImageUtils::convertImageToBase64(image));
      } else {
        cover = "";
        sp_image_add_load_callback(image, ImageUtils::imageLoadedCallback, nullptr);
      }
    }
  } else {
    cover = "";
  }
  return cover;
}

void Album::browse() {
  this->albumBrowse = sp_albumbrowse_create(application->session, album, AlbumBrowseCallbacks::albumBrowseComplete, this);
}

bool Album::isLoaded() {
  return sp_album_is_loaded(album);
}


<|start_filename|>src/objects/spotify/Search.cc<|end_filename|>
#include "Search.h"
#include "../../Application.h"
#include "../../callbacks/SearchCallbacks.h"

#include <libspotify/api.h>

extern Application* application;

Search::Search(const Search& other) : search(other.search) {
  sp_search_add_ref(search);
};

Search::~Search() {
  sp_search_release(search);
};

std::string Search::link() {
  std::string link;
  if(sp_search_is_loaded(search)) {
    sp_link* spLink = sp_link_create_from_search(search);
    char linkChar[256];
    sp_link_as_string(spLink, linkChar, 256);
    link = std::string(linkChar);
    sp_link_release(spLink);
  }
  return link;
}

std::string Search::didYouMeanText() {
  std::string didYouMeanText;
  if(sp_search_is_loaded(search)) {
    didYouMeanText = std::string(sp_search_did_you_mean(search));
  }
  return didYouMeanText;
}

std::shared_ptr<Track> Search::getTrack(int position) {
  auto track = std::make_shared<Track>(sp_search_track(search, position));
  return track;
}

std::unique_ptr<Album> Search::getAlbum(int position) {
  auto album = std::unique_ptr<Album>(new Album(sp_search_album(search, position)));
  return album;
}

std::unique_ptr<Artist> Search::getArtist(int position) {
  auto artist = std::unique_ptr<Artist>(new Artist(sp_search_artist(search, position)));
  return artist;
}

std::shared_ptr<Playlist> Search::getPlaylist(int position) {
  auto playlist = Playlist::fromCache(sp_search_playlist(search, position));
  return playlist;
}

int Search::numTracks() {
  return sp_search_num_tracks(search);
}

int Search::totalTracks() {
  int totalTracks = 0;
  if(sp_search_is_loaded(search)) {
    totalTracks = sp_search_total_tracks(search);
  }
  return totalTracks;
}

int Search::numAlbums() {
  return sp_search_num_albums(search);
}

int Search::totalAlbums() {
  int totalAlbums = 0;
  if(sp_search_is_loaded(search)) {
    totalAlbums = sp_search_total_albums(search);
  }
  return totalAlbums;
}

int Search::numArtists() {
  return sp_search_num_artists(search);
}

int Search::totalArtists() {
  int totalArtists = 0;
  if(sp_search_is_loaded(search)) {
    totalArtists = sp_search_total_artists(search);
  }
  return totalArtists;
}

int Search::numPlaylists() {
  return sp_search_num_playlists(search);
}

int Search::totalPlaylists() {
  int totalPlaylists = 0;
  if(sp_search_is_loaded(search)) {
    totalPlaylists = sp_search_total_playlists(search);
  }
  return totalPlaylists;
}

void Search::execute(std::string query, int trackOffset, int trackLimit,
    int albumOffset, int albumLimit,
    int artistOffset, int artistLimit,
    int playlistOffset, int playlistLimit) {
  this->search = sp_search_create(application->session, query.c_str(),
    trackOffset, trackLimit,
    albumOffset, albumLimit,
    artistOffset, artistLimit,
    playlistOffset, playlistLimit,
    SP_SEARCH_STANDARD, //?
    SearchCallbacks::searchComplete,
    this
  );
}


<|start_filename|>src/objects/spotify/Artist.cc<|end_filename|>
#include "Artist.h"
#include "../../callbacks/ArtistBrowseCallbacks.h"
#include "../../Application.h"

extern Application* application;

Artist::Artist(sp_artist* _artist) : artist(_artist), artistBrowse(nullptr), nodeObject(nullptr) {
  sp_artist_add_ref(artist);
};

 Artist::Artist(const Artist& other) : artist(other.artist), artistBrowse(other.artistBrowse), nodeObject(other.nodeObject) {
  sp_artist_add_ref(artist);
  if(artistBrowse != nullptr) {
    sp_artistbrowse_add_ref(artistBrowse);
  }
};

Artist::~Artist() {
  sp_artist_release(artist);
  if(artistBrowse != nullptr) {
    sp_artistbrowse_release(artistBrowse);
  }
};

std::string Artist::name() {
  std::string name;
  if(sp_artist_is_loaded(artist)) {
    name = std::string(sp_artist_name(artist));
  } else {
    name = "Loading...";
  }
  return name;
}

std::string Artist::link() {
  std::string link;
  if(sp_artist_is_loaded(artist)) {
    sp_link* spLink = sp_link_create_from_artist(artist);
    char linkChar[256];
    sp_link_as_string(spLink, linkChar, 256);
    link = std::string(linkChar);
    sp_link_release(spLink);
  }
  return link;
}

void Artist::browse(sp_artistbrowse_type artistbrowseType) {
  this->artistBrowse = sp_artistbrowse_create(application->session, artist, artistbrowseType, &ArtistBrowseCallbacks::artistBrowseComplete, this);
}

std::vector<std::shared_ptr<Track>> Artist::tracks() {
  std::vector<std::shared_ptr<Track>> tracks;
  if(sp_artistbrowse_is_loaded(artistBrowse)) {
    int numTracks = sp_artistbrowse_num_tracks(artistBrowse);
    tracks.resize(numTracks);
    for(int i = 0; i < numTracks; i++) {
      tracks[i] = std::make_shared<Track>(sp_artistbrowse_track(artistBrowse, i));
    }
  }
  return tracks;
}

std::vector<std::shared_ptr<Track>> Artist::tophitTracks() {
  std::vector<std::shared_ptr<Track>> tophitTracks;
  if(sp_artistbrowse_is_loaded(artistBrowse)) {
    int numTophitTracks = sp_artistbrowse_num_tophit_tracks(artistBrowse);
    tophitTracks.resize(numTophitTracks);
    for(int i = 0; i < numTophitTracks; i++) {
      tophitTracks[i] = std::make_shared<Track>(sp_artistbrowse_tophit_track(artistBrowse, i));
    }
  }
  return tophitTracks;
}

std::vector<std::unique_ptr<Album>> Artist::albums() {
  std::vector<std::unique_ptr<Album>> albums;
  if(sp_artistbrowse_is_loaded(artistBrowse)) {
    int numAlbums = sp_artistbrowse_num_albums(artistBrowse);
    albums.resize(numAlbums);
    for(int i = 0; i < numAlbums; i++) {
      albums[i] = std::unique_ptr<Album>(new Album(sp_artistbrowse_album(artistBrowse, i)));
    }
  }
  return albums;
}

std::vector<std::unique_ptr<Artist>> Artist::similarArtists() {
  std::vector<std::unique_ptr<Artist>> similarArtists;
  if(sp_artistbrowse_is_loaded(artistBrowse)) {
    int numSimilarArtists = sp_artistbrowse_num_similar_artists(artistBrowse);
    similarArtists.resize(numSimilarArtists);
    for(int i = 0; i < numSimilarArtists; i++) {
      similarArtists[i] = std::unique_ptr<Artist>(new Artist(sp_artistbrowse_similar_artist(artistBrowse, i)));
    }
  }
  return similarArtists;
}

std::string Artist::biography() {
  std::string biography;
  if(sp_artistbrowse_is_loaded(artistBrowse)) {
    biography = std::string(sp_artistbrowse_biography(artistBrowse));
  }
  return biography;
}

bool Artist::isLoaded() {
  return sp_artist_is_loaded(artist);
}


<|start_filename|>src/objects/node/NodeTrackExtended.cc<|end_filename|>
#include "NodeTrackExtended.h"
#include "NodeUser.h"

//Since NodeWrapped uses a templating technique to assign the static constructor to each childclass we need to improvise here.
Nan::Persistent<FunctionTemplate> NodeTrackExtended::constructorTemplate;

NodeTrackExtended::NodeTrackExtended(std::shared_ptr<TrackExtended> _trackExtended) : NodeTrack(_trackExtended), trackExtended(_trackExtended) {
}

/**
  We need rewrite this method because we need to use our own constructor, not the one from NodeTrack.
**/
Local<Object> NodeTrackExtended::createInstance() {
  Local<Object> object = getConstructor()->NewInstance();
  Nan::SetInternalFieldPointer(object, 0, this);
  return object;
}

/**
  Same for this... we need to rewrite so NodeTrackExtended::constructor is used and not NodeTrack::constructor.
**/
Local<Function> NodeTrackExtended::getConstructor() {
  return Nan::New(constructorTemplate)->GetFunction();
}

NAN_GETTER(NodeTrackExtended::getCreator) {
  NodeTrackExtended* nodeTrackExtended = Nan::ObjectWrap::Unwrap<NodeTrackExtended>(info.This());
  Local<Value> nodeCreator = Nan::Undefined();
  if(nodeTrackExtended->trackExtended->creator()) {
    NodeUser* nodeUser = new NodeUser(nodeTrackExtended->trackExtended->creator());
    nodeCreator = nodeUser->createInstance();
  }
  info.GetReturnValue().Set(nodeCreator);
}

NAN_GETTER(NodeTrackExtended::getSeen) {
  NodeTrackExtended* nodeTrackExtended = Nan::ObjectWrap::Unwrap<NodeTrackExtended>(info.This());
  info.GetReturnValue().Set(Nan::New<Boolean>(nodeTrackExtended->trackExtended->seen()));
}

NAN_SETTER(NodeTrackExtended::setSeen) {
  NodeTrackExtended* nodeTrackExtended = Nan::ObjectWrap::Unwrap<NodeTrackExtended>(info.This());
  nodeTrackExtended->trackExtended->seen(value->ToBoolean()->Value());
}

NAN_GETTER(NodeTrackExtended::getCreateTime) {
  NodeTrackExtended* nodeTrackExtended = Nan::ObjectWrap::Unwrap<NodeTrackExtended>(info.This());
  info.GetReturnValue().Set(Nan::New<Date>(nodeTrackExtended->trackExtended->createTime() * 1000).ToLocalChecked());
}

NAN_GETTER(NodeTrackExtended::getMessage) {
  NodeTrackExtended* nodeTrackExtended = Nan::ObjectWrap::Unwrap<NodeTrackExtended>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeTrackExtended->trackExtended->message().c_str()).ToLocalChecked());
}

void NodeTrackExtended::init() {
  Local<FunctionTemplate> constructorTemplate = NodeWrapped::init("TrackExtended");
  Handle<FunctionTemplate> nodeTrackTemplate = NodeTrack::init();
  constructorTemplate->Inherit(nodeTrackTemplate);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("creator").ToLocalChecked(), getCreator);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("seen").ToLocalChecked(), getSeen);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("createTime").ToLocalChecked(), getCreateTime);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("message").ToLocalChecked(), getMessage);
  NodeTrackExtended::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>src/objects/node/NodeSearch.h<|end_filename|>
#ifndef _NODE_SEARCH_RESULT_H
#define _NODE_SEARCH_RESULT_H

#include "V8Browseable.h"
#include "../spotify/Search.h"

#include <nan.h>
#include <memory>

using namespace v8;

class NodeSearch : public V8Browseable<NodeSearch> {
private:
  std::unique_ptr<Search> search;
  std::string searchQuery;
  int trackOffset, albumOffset, artistOffset, playlistOffset;
  int trackLimit, albumLimit, artistLimit, playlistLimit;
  void setupAdditionalMethods();
public:
  NodeSearch(const char* _query);
  NodeSearch(const char* _query, int offset);
  NodeSearch(const char* _query, int offset, int limit);

  static NAN_GETTER(getTrackOffset);
  static NAN_SETTER(setTrackOffset);
  static NAN_GETTER(getAlbumOffset);
  static NAN_SETTER(setAlbumOffset);
  static NAN_GETTER(getArtistOffset);
  static NAN_SETTER(setArtistOffset);
  static NAN_GETTER(getPlaylistOffset);
  static NAN_SETTER(setPlaylistOffset);
  static NAN_GETTER(getTrackLimit);
  static NAN_SETTER(setTrackLimit);
  static NAN_GETTER(getAlbumLimit);
  static NAN_SETTER(setAlbumLimit);
  static NAN_GETTER(getArtistLimit);
  static NAN_SETTER(setArtistLimit);
  static NAN_GETTER(getPlaylistLimit);
  static NAN_SETTER(setPlaylistLimit);
  static NAN_GETTER(didYouMean);
  static NAN_GETTER(getTotalTracks);
  static NAN_GETTER(getNumTracks);
  static NAN_GETTER(getTotalAlbums);
  static NAN_GETTER(getNumAlbums);
  static NAN_GETTER(getTotalArtists);
  static NAN_GETTER(getNumArtists);
  static NAN_GETTER(getTotalPlaylists);
  static NAN_GETTER(getNumPlaylists);
  static NAN_METHOD(New);
  static NAN_METHOD(execute);
  static NAN_METHOD(getTrack);
  static NAN_METHOD(getAlbum);
  static NAN_METHOD(getArtist);
  static NAN_METHOD(getPlaylist);
  static NAN_GETTER(getLink);
  static void init();
};

#endif


<|start_filename|>src/objects/node/NodeSpotify.cc<|end_filename|>
#include "NodeSpotify.h"
#include "../../audio/AudioHandler.h"
#include "../../audio/NativeAudioHandler.h"
#include "../../audio/NodeAudioHandler.h"
#include "../../Application.h"
#include "../../exceptions.h"
#include "../../callbacks/SessionCallbacks.h"
#include "../spotify/SpotifyOptions.h"
#include "NodePlaylist.h"
#include "NodePlaylistFolder.h"
#include "NodePlaylistContainer.h"
#include "NodePlayer.h"
#include "NodeArtist.h"
#include "NodeAlbum.h"
#include "NodeTrack.h"
#include "NodeUser.h"

extern Application* application;

NodeSpotify::NodeSpotify(Handle<Object> options) {
  /*
   * Important note: The session callbacks must be initialized before
   * the sp_session is started. The uv timer and async handle must be set up
   * as they will be used as soon as the sp_session is created, which happens in the
   * NodeSpotify ctor.
   */
  SessionCallbacks::init();

  SpotifyOptions _options;
  Handle<String> settingsFolderKey = Nan::New<String>("settingsFolder").ToLocalChecked();
  Handle<String> cacheFolderKey = Nan::New<String>("cacheFolder").ToLocalChecked();
  Handle<String> traceFileKey = Nan::New<String>("traceFile").ToLocalChecked();
  Handle<String> appkeyFileKey = Nan::New<String>("appkeyFile").ToLocalChecked();
  if(options->Has(settingsFolderKey)) {
    String::Utf8Value settingsFolderValue(options->Get(settingsFolderKey)->ToString());
    _options.settingsFolder = *settingsFolderValue;
  } else {
    _options.settingsFolder = "settings";
  }
  if(options->Has(cacheFolderKey)) {
    String::Utf8Value cacheFolderValue(options->Get(cacheFolderKey)->ToString());
    _options.cacheFolder = *cacheFolderValue;
  } else {
    _options.cacheFolder = "cache";
  }
  if(options->Has(traceFileKey)) {
    String::Utf8Value traceFileValue(options->Get(traceFileKey)->ToString());
    _options.traceFile = *traceFileValue;
  }
  if(options->Has(appkeyFileKey)) {
    String::Utf8Value appkeyFileValue(options->Get(appkeyFileKey)->ToString());
    _options.appkeyFile = *appkeyFileValue;
  }
  spotify = std::unique_ptr<Spotify>(new Spotify(_options));
}

NodeSpotify::~NodeSpotify() {

}

NAN_METHOD(NodeSpotify::createFromLink) {
  Local<Value> out;
  String::Utf8Value linkToParse(info[0]->ToString());
  sp_link* parsedLink = sp_link_create_from_string(*linkToParse);
  if(parsedLink != nullptr) {
    sp_linktype linkType = sp_link_type(parsedLink);
    switch(linkType) {
      case SP_LINKTYPE_TRACK:
      {
        sp_track* track = sp_link_as_track(parsedLink);
        NodeTrack* nodeTrack = new NodeTrack(std::make_shared<Track>(track));
        out = nodeTrack->createInstance();
        break;
      }
      case SP_LINKTYPE_ALBUM:
      {
        sp_album* album = sp_link_as_album(parsedLink);
        NodeAlbum* nodeAlbum = new NodeAlbum(std::unique_ptr<Album>(new Album(album)));
        out = nodeAlbum->createInstance();
        break;
      }
      case SP_LINKTYPE_ARTIST:
      {
        sp_artist* artist = sp_link_as_artist(parsedLink);
        NodeArtist* nodeArtist = new NodeArtist(std::unique_ptr<Artist>(new Artist(artist)));
        out = nodeArtist->createInstance();
        break;
      }
      case SP_LINKTYPE_PROFILE:
      {
        sp_user* user = sp_link_as_user(parsedLink);
        NodeUser* nodeUser = new NodeUser(std::unique_ptr<User>(new User(user)));
        out = nodeUser->createInstance();
        break;
      }
      case SP_LINKTYPE_PLAYLIST:
      {
        sp_playlist* spPlaylist = sp_playlist_create(application->session, parsedLink);
        auto playlist = Playlist::fromCache(spPlaylist);
        NodePlaylist* nodePlaylist = new NodePlaylist(playlist);
        out = nodePlaylist->createInstance();
        break;
      }
      case SP_LINKTYPE_LOCALTRACK:
      {
        sp_track* track = sp_link_as_track(parsedLink);
        NodeTrack* nodeTrack = new NodeTrack(std::make_shared<Track>(track));
        out = nodeTrack->createInstance();
        break;
      }
      default:
        out = Nan::Undefined();
    }
    sp_link_release(parsedLink);
  } else {
    out = Nan::Undefined();
  }
  info.GetReturnValue().Set(out);
}

NAN_METHOD(NodeSpotify::login) {
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  String::Utf8Value v8User(info[0]->ToString());
  String::Utf8Value v8Password(info[1]->ToString());
  bool rememberMe = info[2]->ToBoolean()->Value();
  bool withRemembered = info[3]->ToBoolean()->Value();
  std::string user(*v8User);
  std::string password(*<PASSWORD>);
  nodeSpotify->spotify->login(user, password, rememberMe, withRemembered);
  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodeSpotify::logout) {
  if(info.Length() > 0) {
    SessionCallbacks::logoutCallback.SetFunction(info[0].As<Function>());
  }
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  nodeSpotify->spotify->logout();
  info.GetReturnValue().SetUndefined();
}

NAN_GETTER(NodeSpotify::getPlaylistContainer) {
  NodePlaylistContainer* nodePlaylistContainer = new NodePlaylistContainer(application->playlistContainer);
  info.GetReturnValue().Set(nodePlaylistContainer->createInstance());
}

NAN_GETTER(NodeSpotify::getRememberedUser) {
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeSpotify->spotify->rememberedUser().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeSpotify::getSessionUser) {
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  NodeUser* nodeUser = new NodeUser(std::move(nodeSpotify->spotify->sessionUser()));
  info.GetReturnValue().Set(nodeUser->createInstance());
}

NAN_GETTER(NodeSpotify::getSessionUserCountry) {
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeSpotify->spotify->sessionUserCountry().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeSpotify::getConstants) {
  Local<Object> constants = Nan::New<Object>();
  constants->Set(Nan::New<String>("ARTISTBROWSE_FULL").ToLocalChecked(), Nan::New<Number>(SP_ARTISTBROWSE_FULL));
  constants->Set(Nan::New<String>("ARTISTBROWSE_NO_TRACKS").ToLocalChecked(), Nan::New<Number>(SP_ARTISTBROWSE_NO_TRACKS));
  constants->Set(Nan::New<String>("ARTISTBROWSE_NO_ALBUMS").ToLocalChecked(), Nan::New<Number>(SP_ARTISTBROWSE_NO_ALBUMS));

  constants->Set(Nan::New<String>("IMAGE_SIZE_NORMAL").ToLocalChecked(), Nan::New<Number>(SP_IMAGE_SIZE_NORMAL));
  constants->Set(Nan::New<String>("IMAGE_SIZE_SMALL").ToLocalChecked(), Nan::New<Number>(SP_IMAGE_SIZE_SMALL));
  constants->Set(Nan::New<String>("IMAGE_SIZE_LARGE").ToLocalChecked(), Nan::New<Number>(SP_IMAGE_SIZE_LARGE));

  constants->Set(Nan::New<String>("PLAYLIST_TYPE_PLAYLIST").ToLocalChecked(), Nan::New<Number>(SP_PLAYLIST_TYPE_PLAYLIST));
  constants->Set(Nan::New<String>("PLAYLIST_TYPE_START_FOLDER").ToLocalChecked(), Nan::New<Number>(SP_PLAYLIST_TYPE_START_FOLDER));
  constants->Set(Nan::New<String>("PLAYLIST_TYPE_END_FOLDER").ToLocalChecked(), Nan::New<Number>(SP_PLAYLIST_TYPE_END_FOLDER));
  constants->Set(Nan::New<String>("PLAYLIST_TYPE_PLACEHOLDER").ToLocalChecked(), Nan::New<Number>(SP_PLAYLIST_TYPE_PLACEHOLDER));

  constants->Set(Nan::New<String>("SOCIAL_PROVIDER_SPOTIFY").ToLocalChecked(), Nan::New<Number>(SP_SOCIAL_PROVIDER_SPOTIFY));
  constants->Set(Nan::New<String>("SOCIAL_PROVIDER_FACEBOOK").ToLocalChecked(), Nan::New<Number>(SP_SOCIAL_PROVIDER_FACEBOOK));
  constants->Set(Nan::New<String>("SOCIAL_PROVIDER_LASTFM").ToLocalChecked(), Nan::New<Number>(SP_SOCIAL_PROVIDER_LASTFM));

  constants->Set(Nan::New<String>("SCROBBLING_STATE_USE_GLOBAL_SETTING").ToLocalChecked(), Nan::New<Number>(SP_SCROBBLING_STATE_USE_GLOBAL_SETTING));
  constants->Set(Nan::New<String>("SCROBBLING_STATE_LOCAL_ENABLED").ToLocalChecked(), Nan::New<Number>(SP_SCROBBLING_STATE_LOCAL_ENABLED));
  constants->Set(Nan::New<String>("SCROBBLING_STATE_LOCAL_DISABLED").ToLocalChecked(), Nan::New<Number>(SP_SCROBBLING_STATE_LOCAL_DISABLED));
  constants->Set(Nan::New<String>("SCROBBLING_STATE_GLOBAL_ENABLED").ToLocalChecked(), Nan::New<Number>(SP_SCROBBLING_STATE_GLOBAL_ENABLED));
  constants->Set(Nan::New<String>("SCROBBLING_STATE_GLOBAL_DISABLED").ToLocalChecked(), Nan::New<Number>(SP_SCROBBLING_STATE_GLOBAL_DISABLED));

  constants->Set(Nan::New<String>("SP_TRACK_AVAILABILITY_UNAVAILABLE").ToLocalChecked(), Nan::New<Number>(SP_TRACK_AVAILABILITY_UNAVAILABLE));
  constants->Set(Nan::New<String>("SP_TRACK_AVAILABILITY_AVAILABLE").ToLocalChecked(), Nan::New<Number>(SP_TRACK_AVAILABILITY_AVAILABLE));
  constants->Set(Nan::New<String>("SP_TRACK_AVAILABILITY_NOT_STREAMABLE").ToLocalChecked(), Nan::New<Number>(SP_TRACK_AVAILABILITY_NOT_STREAMABLE));
  constants->Set(Nan::New<String>("SP_TRACK_AVAILABILITY_BANNED_BY_ARTIST").ToLocalChecked(), Nan::New<Number>(SP_TRACK_AVAILABILITY_BANNED_BY_ARTIST));

  info.GetReturnValue().Set(constants);
}

#ifdef NODE_SPOTIFY_NATIVE_SOUND
NAN_METHOD(NodeSpotify::useNativeAudio) {
  //Since the old audio handler has to be deleted first, do an empty reset.
  application->audioHandler.reset();
  application->audioHandler = std::unique_ptr<AudioHandler>(new NativeAudioHandler());
  info.GetReturnValue().SetUndefined();
}
#endif

NAN_METHOD(NodeSpotify::useNodejsAudio) {
  if(info.Length() < 1) {
    Nan::ThrowError("useNodjsAudio needs a function as its first argument.");
    return;
  }
  //Since the old audio handler has to be deleted first, do an empty reset.
  application->audioHandler.reset();
  application->audioHandler = std::unique_ptr<AudioHandler>(new NodeAudioHandler(info[0].As<Function>()));

  Local<Function> needMoreDataSetter = Nan::New<FunctionTemplate>(NodeAudioHandler::setNeedMoreData)->GetFunction();
  info.GetReturnValue().Set(needMoreDataSetter);
}

NAN_METHOD(NodeSpotify::on) {
  if(info.Length() < 1 || !info[0]->IsObject()) {
    Nan::ThrowError("on needs an object as its first argument.");
    return;
  }
  Handle<Object> callbacks = info[0]->ToObject();
  Handle<String> metadataUpdatedKey = Nan::New<String>("metadataUpdated").ToLocalChecked();
  if(callbacks->Has(metadataUpdatedKey)) {
    SessionCallbacks::metadataUpdatedCallback.SetFunction(callbacks->Get(metadataUpdatedKey).As<Function>());
  }

  Handle<String> readyKey = Nan::New<String>("ready").ToLocalChecked();
  if(callbacks->Has(readyKey)) {
    SessionCallbacks::loginCallback.SetFunction(callbacks->Get(readyKey).As<Function>());
  }

  Handle<String> logoutKey = Nan::New<String>("logout").ToLocalChecked();
  if(callbacks->Has(logoutKey)) {
    SessionCallbacks::logoutCallback.SetFunction(callbacks->Get(logoutKey).As<Function>());
  }

  Handle<String> playTokenLostKey = Nan::New<String>("playTokenLost").ToLocalChecked();
  if(callbacks->Has(playTokenLostKey)) {
    SessionCallbacks::playTokenLostCallback.SetFunction(callbacks->Get(playTokenLostKey).As<Function>());
  }
  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodeSpotify::isScrobbling) {
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  if(info.Length() < 1 || !info[0]->IsNumber()) {
    Nan::ThrowError("Please provide a social provider from spotify.constants.SOCIAL_PROVIDER_*");
    return;
  }
  sp_social_provider provider = static_cast<sp_social_provider>(info[0]->ToNumber()->IntegerValue());
  try {
    info.GetReturnValue().Set(Nan::New<Number>(nodeSpotify->spotify->isScrobbling(provider)));
  } catch(const SocialProviderScrobblingException& e) {
    Nan::ThrowError(e.message.c_str());
  }
}

NAN_METHOD(NodeSpotify::isScrobblingPossible) {
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  if(info.Length() < 1 || !info[0]->IsNumber()) {
    Nan::ThrowError("Please provide a social provider from spotify.constants.SOCIAL_PROVIDER_*");
    return;
  }
  sp_social_provider provider = static_cast<sp_social_provider>(info[0]->ToNumber()->IntegerValue());
  try {
    info.GetReturnValue().Set(Nan::New<Number>(nodeSpotify->spotify->isScrobblingPossible(provider)));
  } catch(const SocialProviderScrobblingException& e) {
    Nan::ThrowError(e.message.c_str());
  }
}

NAN_METHOD(NodeSpotify::setScrobbling) {
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  if(info.Length() < 2 || !info[0]->IsNumber() || !info[1]->IsNumber()) {
    Nan::ThrowError("Please provide a social provider from spotify.constants.SOCIAL_PROVIDER_* as the first argument and a state from spotify.constants.SCROBBLING_STATE* as the second.");
    return;
  }
  sp_social_provider provider = static_cast<sp_social_provider>(info[0]->ToNumber()->IntegerValue());
  sp_scrobbling_state state = static_cast<sp_scrobbling_state>(info[1]->ToNumber()->IntegerValue());
  try {
    nodeSpotify->spotify->setScrobbling(provider, state);
  } catch(const SocialProviderScrobblingException& e) {
    Nan::ThrowError(e.message.c_str());
  }
}

NAN_METHOD(NodeSpotify::setSocialCredentials) {
  NodeSpotify* nodeSpotify = Nan::ObjectWrap::Unwrap<NodeSpotify>(info.This());
  if(info.Length() < 3 || !info[0]->IsNumber() || !info[1]->IsString() || !info[2]->IsString()) {
    Nan::ThrowError("Please provide a social provider from spotify.constants.SOCIAL_PROVIDER_* and a username and password as parameters.");
    return;
  }
  sp_social_provider provider = static_cast<sp_social_provider>(info[0]->ToNumber()->IntegerValue());
  String::Utf8Value v8User(info[1]->ToString());
  String::Utf8Value v8Password(info[2]->ToString());
  std::string user(*v8User);
  std::string password(*<PASSWORD>);
  try {
    nodeSpotify->spotify->setSocialCredentials(provider, user, password);
  } catch(const SocialProviderScrobblingException& e) {
    Nan::ThrowError(e.message.c_str());
  }
}

void NodeSpotify::init() {
  Local<FunctionTemplate> constructorTemplate = NodeWrapped::init("Spotify");
  Nan::SetPrototypeMethod(constructorTemplate, "login", login);
  Nan::SetPrototypeMethod(constructorTemplate, "logout", logout);
  Nan::SetPrototypeMethod(constructorTemplate, "createFromLink", createFromLink);
  Nan::SetPrototypeMethod(constructorTemplate, "on", on);
#ifdef NODE_SPOTIFY_NATIVE_SOUND
  Nan::SetPrototypeMethod(constructorTemplate, "useNativeAudio", useNativeAudio);
#endif
  Nan::SetPrototypeMethod(constructorTemplate, "useNodejsAudio", useNodejsAudio);
  Nan::SetPrototypeMethod(constructorTemplate, "isScrobbling", isScrobbling);
  Nan::SetPrototypeMethod(constructorTemplate, "isScrobblingPossible", isScrobblingPossible);
  Nan::SetPrototypeMethod(constructorTemplate, "setScrobbling", setScrobbling);
  Nan::SetPrototypeMethod(constructorTemplate, "setSocialCredentials", setSocialCredentials);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("rememberedUser").ToLocalChecked(), getRememberedUser);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("sessionUser").ToLocalChecked(), getSessionUser);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("sessionUserCountry").ToLocalChecked(), getSessionUserCountry);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("playlistContainer").ToLocalChecked(), getPlaylistContainer);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("constants").ToLocalChecked(), getConstants);

  NodeSpotify::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>src/objects/node/NodeTrack.cc<|end_filename|>
#include "NodeTrack.h"
#include "NodeArtist.h"
#include "NodeAlbum.h"

NodeTrack::NodeTrack(std::shared_ptr<Track> _track) : track(_track) {

};

NodeTrack::~NodeTrack() {

}

NAN_GETTER(NodeTrack::getName) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeTrack->track->name().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeTrack::getLink) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeTrack->track->link().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeTrack::getDuration) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeTrack->track->duration()/1000));
}

NAN_GETTER(NodeTrack::getAvailability) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeTrack->track->getAvailability()));
}

NAN_GETTER(NodeTrack::getPopularity) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeTrack->track->popularity()));
}

NAN_GETTER(NodeTrack::getArtists) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  Local<Array> jsArtists = Nan::New<Array>(nodeTrack->track->artists().size());
  for(int i = 0; i < (int)nodeTrack->track->artists().size(); i++) {
    if(nodeTrack->track->artists()[i]) {
      NodeArtist* nodeArtist = new NodeArtist(std::move(nodeTrack->track->artists()[i]));
      jsArtists->Set(Nan::New<Number>(i), nodeArtist->createInstance());
    } else {
      jsArtists->Set(Nan::New<Number>(i), Nan::Undefined());
    }
  }
  info.GetReturnValue().Set(jsArtists);
}

NAN_GETTER(NodeTrack::getAlbum) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  if(nodeTrack->track->album()) {
    NodeAlbum* nodeAlbum = new NodeAlbum(nodeTrack->track->album());
    info.GetReturnValue().Set(nodeAlbum->createInstance());
  } else {
    info.GetReturnValue().SetUndefined();
  }
}

NAN_GETTER(NodeTrack::getStarred) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  info.GetReturnValue().Set(Nan::New<Boolean>(nodeTrack->track->starred()));
}

NAN_SETTER(NodeTrack::setStarred) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  nodeTrack->track->setStarred(value->ToBoolean()->Value());
}

NAN_GETTER(NodeTrack::isLoaded) {
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info.This());
  info.GetReturnValue().Set(Nan::New<Boolean>(nodeTrack->track->isLoaded()));
}

Handle<FunctionTemplate> NodeTrack::init() {
  Nan::EscapableHandleScope scope;
  Local<FunctionTemplate> constructorTemplate = NodeWrapped::init("Track");
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("name").ToLocalChecked(), getName);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("link").ToLocalChecked(), getLink);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("duration").ToLocalChecked(), getDuration);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("artists").ToLocalChecked(), getArtists);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("album").ToLocalChecked(), getAlbum);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("starred").ToLocalChecked(), getStarred, setStarred);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("popularity").ToLocalChecked(), getPopularity);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("isLoaded").ToLocalChecked(), isLoaded);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("availability").ToLocalChecked(), getAvailability);
  NodeTrack::constructorTemplate.Reset(constructorTemplate);
  return scope.Escape(constructorTemplate);
}


<|start_filename|>src/objects/spotify/Track.cc<|end_filename|>
#include "Track.h"
#include "../../Application.h"

extern Application* application;

Track::Track(sp_track* _track) : track(_track) {
  sp_track_add_ref(track);
};

std::string Track::name() {
  std::string name;
  if(sp_track_is_loaded(track)) {
    name = std::string(sp_track_name(track));
  } else {
    name = "Loading...";
  }
  return name;
}

std::string Track::link() {
  std::string link;
  if(sp_track_is_loaded(track)) {
    sp_link* spLink = sp_link_create_from_track(track, 0);
    char linkChar[256];
    sp_link_as_string(spLink, linkChar, 256);
    link = std::string(linkChar);
    sp_link_release(spLink);
  }
  return link;
}

std::vector<std::unique_ptr<Artist>> Track::artists() {
  std::vector<std::unique_ptr<Artist>> artists;
  if(sp_track_is_loaded(track)) {
    int numArtists = sp_track_num_artists(track);
    artists.resize(numArtists);
    for(int i = 0; i < numArtists; i++) {
      sp_artist* spArtist = sp_track_artist(track, i);
      artists[i] = std::unique_ptr<Artist>(new Artist(spArtist));
    }
  }
  return artists;
}

std::unique_ptr<Album> Track::album() {
  std::unique_ptr<Album> album;
  if(sp_track_is_loaded(track)) {
    sp_album* spAlbum = sp_track_album(track);
    if(spAlbum != nullptr) {
      album = std::unique_ptr<Album>(new Album(spAlbum));
    }
  }
  return std::move(album);
}

int Track::duration() {
  int duration = -1;
  if(sp_track_is_loaded(track)) {
    duration = sp_track_duration(track);
  }
  return duration;
}

bool Track::starred() {
  bool starred = false;
  if(sp_track_is_loaded(track)) {
    starred = sp_track_is_starred(application->session, track);
  }
  return starred;
}

int Track::getAvailability() {
  int trackAvailability = 0;
  if(sp_track_is_loaded(track)) {
    trackAvailability = sp_track_get_availability(application->session, track);
  }
  return trackAvailability;
}

int Track::popularity() {
  int popularity = -1;
  if(sp_track_is_loaded(track)) {
    popularity = sp_track_popularity(track);
  }
  return popularity;
}

bool Track::isLoaded() {
  return sp_track_is_loaded(track);
}

void Track::setStarred(bool starred) {
  //This takes an array of pointers to nodeTracks, so we need to tack the adress of the saved spotifyNodeTrack pointer.
  sp_track_set_starred(application->session, &track, 1, starred);
}


<|start_filename|>src/objects/spotify/Spotify.h<|end_filename|>
#ifndef _SPOTIFY_H
#define _SPOTIFY_H

#include "SpotifyOptions.h"
#include "User.h"

#include <libspotify/api.h>
#include <string>
#include <memory>

class Spotify {
friend class NodeSpotify;

public:
  Spotify(SpotifyOptions options);
  ~Spotify() {}
  void login(std::string user, std::string password, bool remeberedUser, bool withRemembered);
  void logout();
  std::string rememberedUser();
  std::unique_ptr<User> sessionUser();
  std::string sessionUserCountry();
  sp_scrobbling_state isScrobbling(sp_social_provider provider);
  bool isScrobblingPossible(sp_social_provider provider);
  void setScrobbling(sp_social_provider provider, sp_scrobbling_state state);
  void setSocialCredentials(sp_social_provider provider, std::string user, std::string password);
private:
  sp_session* session;
  sp_session* createSession(SpotifyOptions options);
};

#endif


<|start_filename|>src/Application.h<|end_filename|>
#ifndef _APPLICATION_H
#define _APPLICATION_H

#include <libspotify/api.h>
#include <memory>
#include "objects/spotify/PlaylistContainer.h"
#include "objects/spotify/Player.h"
#include "audio/AudioHandler.h"

struct Application {
  sp_session* session;
  std::shared_ptr<PlaylistContainer> playlistContainer;
  std::shared_ptr<Player> player;
  std::unique_ptr<AudioHandler> audioHandler;
};

#endif


<|start_filename|>src/objects/spotify/Playlist.h<|end_filename|>
#ifndef PLAYLIST_H
#define PLAYLIST_H

#include "PlaylistBase.h"

#include <string>
#include <map>
#include <vector>
#include <memory>
#include <libspotify/api.h>

class User;
class Track;
class TrackExtended;

class Playlist : public PlaylistBase {
friend class NodePlaylist;
friend class PlaylistCallbacksHolder;
friend class PlaylistContainer;
public:
  Playlist(sp_playlist* playlist);
  Playlist(const Playlist& other);
  ~Playlist();

  std::shared_ptr<TrackExtended> getTrack(int position);
  std::unique_ptr<User> owner();
  virtual std::string name();
  void name(std::string _name);
  std::string link();
  std::string description();
  int numTracks();
  bool isLoaded();
  bool isCollaborative();
  void setCollaborative(bool collaborative);
  void addTracks(std::vector<std::shared_ptr<Track>> tracks, int position);
  void removeTracks(const int* trackPositions, int numberOfTracks);
  void reorderTracks(const int* trackPositions, int numberOfTracks, int newPosition);
  static std::shared_ptr<Playlist> fromCache(sp_playlist* playlist);
private:
  sp_playlist* playlist;
  static std::map<sp_playlist*, std::shared_ptr<Playlist>> cache;
};

#endif


<|start_filename|>src/callbacks/PlaylistContainerCallbacksHolder.cc<|end_filename|>
#include "PlaylistContainerCallbacksHolder.h"
#include "../objects/node/NodePlaylist.h"
#include "../objects/node/NodePlaylistFolder.h"
#include "../objects/spotify/Playlist.h"

using namespace v8;

PlaylistContainerCallbacksHolder::PlaylistContainerCallbacksHolder(sp_playlistcontainer* pc, Nan::ObjectWrap* _userdata) :
  playlistContainer(pc), userdata(_userdata) {
    playlistContainerCallbacks = new sp_playlistcontainer_callbacks();
  }

PlaylistContainerCallbacksHolder::~PlaylistContainerCallbacksHolder() {
  sp_playlistcontainer_remove_callbacks(playlistContainer, playlistContainerCallbacks, this);
  delete playlistContainerCallbacks;
}

void PlaylistContainerCallbacksHolder::playlistAdded(sp_playlistcontainer* pc, sp_playlist* spPlaylist, int position, void* userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistContainerCallbacksHolder*>(userdata);
  sp_playlist_type playlistType = sp_playlistcontainer_playlist_type(pc, position);
  std::shared_ptr<PlaylistBase> playlistBase;
  Local<Object> playlistV8;
  if(playlistType == SP_PLAYLIST_TYPE_PLAYLIST) {
    playlistBase = Playlist::fromCache(spPlaylist);
    NodeWrapped<NodePlaylist>* nodePlaylist = new NodePlaylist(Playlist::fromCache(spPlaylist));
    playlistV8 = nodePlaylist->createInstance();
  } else if(playlistType == SP_PLAYLIST_TYPE_START_FOLDER) {
    char buf[256];
    sp_playlistcontainer_playlist_folder_name(pc, position, buf, 256);
    NodeWrapped<NodePlaylistFolder>* nodePlaylist = new NodePlaylistFolder(std::make_shared<PlaylistFolder>(buf, playlistType));
    playlistV8 = nodePlaylist->createInstance();
  } else if(playlistType == SP_PLAYLIST_TYPE_END_FOLDER) {
    NodeWrapped<NodePlaylistFolder>* nodePlaylist = new NodePlaylistFolder(std::make_shared<PlaylistFolder>(playlistType));
    playlistV8 = nodePlaylist->createInstance();
  } else {
    return;
  }
  Local<Value> argv[] = {Nan::Undefined(), playlistV8, Nan::New<Number>(position)};
  holder->playlistAddedCallback.Call(3, argv);
}

void PlaylistContainerCallbacksHolder::playlistRemoved(sp_playlistcontainer* pc, sp_playlist* spPlaylist, int position, void *userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistContainerCallbacksHolder*>(userdata);
  Nan::ObjectWrap* nodePlaylist = nullptr; //FIXME what??
  if(nodePlaylist != nullptr) {
    Local<Value> argv[] = {Nan::Undefined(), Nan::New<Number>(position), nodePlaylist->handle()};
    holder->playlistRemovedCallback.Call(3, argv);
  } else {
    Local<Value> argv[] = {Nan::Undefined(), Nan::New<Number>(position)};
    holder->playlistRemovedCallback.Call(2, argv);
  }
}

void PlaylistContainerCallbacksHolder::playlistMoved(sp_playlistcontainer* pc, sp_playlist* spPlaylist, int position, int new_position, void *userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistContainerCallbacksHolder*>(userdata);
  Nan::ObjectWrap* nodePlaylist = nullptr; //FIXME what?
  if(nodePlaylist != nullptr) {
    Local<Value> argv[] = {Nan::Undefined(), Nan::New<Number>(position), Nan::New<Number>(new_position), nodePlaylist->handle()};
    holder->playlistMovedCallback.Call(4, argv);
  } else {
    Local<Value> argv[] = {Nan::Undefined(), Nan::New<Number>(position), Nan::New<Number>(new_position)};
    holder->playlistMovedCallback.Call(3, argv);
  }
}

void PlaylistContainerCallbacksHolder::setCallbacks() {
  sp_playlistcontainer_remove_callbacks(playlistContainer, playlistContainerCallbacks, this);
  if(!playlistAddedCallback.IsEmpty()) {
    playlistContainerCallbacks->playlist_added = &PlaylistContainerCallbacksHolder::playlistAdded;
  }
  if(!playlistRemovedCallback.IsEmpty()) {
    playlistContainerCallbacks->playlist_removed = &PlaylistContainerCallbacksHolder::playlistRemoved;
  }
  if(!playlistMovedCallback.IsEmpty()) {
    playlistContainerCallbacks->playlist_moved = &PlaylistContainerCallbacksHolder::playlistMoved;
  }
  sp_playlistcontainer_add_callbacks(playlistContainer, playlistContainerCallbacks, this);
}

void PlaylistContainerCallbacksHolder::unsetCallbacks() {
  sp_playlistcontainer_remove_callbacks(playlistContainer, playlistContainerCallbacks, this);
}


<|start_filename|>src/objects/node/NodeSpotify.h<|end_filename|>
#ifndef _NODE_SPOTIFY_H
#define _NODE_SPOTIFY_H

#include "NodeWrapped.h"
#include "../spotify/Spotify.h"

#include <v8.h>
#include <memory>

using namespace v8;

class NodeSpotify : public NodeWrapped<NodeSpotify> {
public:
  NodeSpotify(Handle<Object> option);
  ~NodeSpotify();
  static NAN_METHOD(login);
  static NAN_METHOD(logout);
  static NAN_GETTER(getPlaylistContainer);
  static NAN_GETTER(getRememberedUser);
  static NAN_GETTER(getSessionUser);
  static NAN_GETTER(getSessionUserCountry);
  static NAN_METHOD(createFromLink);
  static NAN_GETTER(getConstants);
#ifdef NODE_SPOTIFY_NATIVE_SOUND
  static NAN_METHOD(useNativeAudio);
#endif
  static NAN_METHOD(useNodejsAudio);
  static NAN_METHOD(isScrobbling);
  static NAN_METHOD(isScrobblingPossible);
  static NAN_METHOD(setScrobbling);
  static NAN_METHOD(setSocialCredentials);
  static NAN_METHOD(on);
  static void init();
private:
  std::unique_ptr<Spotify> spotify;
};

#endif


<|start_filename|>src/audio/NodeAudioHandler.h<|end_filename|>
#ifndef NODEAUDIOHANDLER_H
#define NODEAUDIOHANDLER_H

#include "AudioHandler.h"

#include <nan.h>
#include <uv.h>

#include <node_version.h>

/**
 * @brief The NodeAudioHandler class
 * Handles audio data by calling a user provided Javascript callback with it.
 */
class NodeAudioHandler : public AudioHandler {
public:
  NodeAudioHandler(v8::Local<v8::Function> musicDeliveryCallback);
  ~NodeAudioHandler();
  void setStopped(bool stopped);
  static NAN_METHOD(setNeedMoreData);
protected:
  void afterMusicDelivery(const sp_audioformat* format);
  bool dataNeeded();
private:
  Nan::Callback musicDeliveryCallback;
  uv_timer_t musicTimer;
  bool needMoreData;
  bool stopped;
  int musicTimerRepeat;
#if NODE_VERSION_AT_LEAST(0, 11, 0) // TODO 11 or 12?
  static void musicTimeout(uv_timer_t* timer);
#else
  static void musicTimeout(uv_timer_t* timer, int status);
#endif
  bool callMusicDeliveryCallback(audio_fifo_data_t* audioData);
};

#endif // NODEAUDIOHANDLER_H


<|start_filename|>src/objects/node/NodePlaylistFolder.h<|end_filename|>
#ifndef _NODE_PLAYLIST_FOLDER_H
#define _NODE_PLAYLIST_FOLDER_H

#include "NodeWrapped.h"
#include "../spotify/PlaylistFolder.h"

#include <nan.h>
#include <memory>

using namespace v8;

class NodePlaylistFolder : public NodeWrapped<NodePlaylistFolder> {
private:
  std::shared_ptr<PlaylistFolder> playlistFolder;
public:
  NodePlaylistFolder(std::shared_ptr<PlaylistFolder> playlistFolder);
  ~NodePlaylistFolder();
  static void init();
  static NAN_GETTER(getName);
  static NAN_GETTER(getType);
};

#endif


<|start_filename|>src/objects/node/NodeTrack.h<|end_filename|>
#ifndef _NODE_TRACK_H
#define _NODE_TRACK_H

#include "NodeWrapped.h"
#include "../spotify/Track.h"

#include <nan.h>
#include <memory>

using namespace v8;

class NodeTrack : public NodeWrapped<NodeTrack> {
friend class NodePlayer;
friend class NodePlaylist;
private:
  std::shared_ptr<Track> track;
public:
  NodeTrack(std::shared_ptr<Track> track);
  ~NodeTrack();
  static NAN_GETTER(getName);
  static NAN_GETTER(getLink);
  static NAN_GETTER(getArtists);
  static NAN_GETTER(getDuration);
  static NAN_GETTER(getAlbum);
  static NAN_GETTER(getStarred);
  static NAN_GETTER(getPopularity);
  static NAN_GETTER(isLoaded);
  static NAN_GETTER(getAvailability);
  static NAN_SETTER(setStarred);
  static Handle<FunctionTemplate> init();
};

#endif

<|start_filename|>src/objects/node/NodePlayer.h<|end_filename|>
#ifndef _NODE_PLAYER_H
#define _NODE_PLAYER_H

#include "NodeWrapped.h"
#include "../spotify/Player.h"

#include <v8.h>
#include <memory>

using namespace v8;

class NodePlayer : public NodeWrapped<NodePlayer> {
private:
  std::shared_ptr<Player> player;
  NodePlayer(const NodePlayer& other);
public:
  NodePlayer(std::shared_ptr<Player> player);
  ~NodePlayer();
  static NAN_METHOD(stop);
  static NAN_METHOD(pause);
  static NAN_METHOD(resume);
  static NAN_METHOD(play);
  static NAN_GETTER(getCurrentSecond);
  static NAN_METHOD(seek);
  static NAN_METHOD(on);
  static NAN_METHOD(off);
  static void init();
};

#endif


<|start_filename|>src/callbacks/PlaylistContainerCallbacksHolder.h<|end_filename|>
#ifndef _PLAYLIST_CONTAINER_CALLBACKS_H
#define _PLAYLIST_CONTAINER_CALLBACKS_H

#include <libspotify/api.h>
#include <nan.h>

class PlaylistContainerCallbacksHolder {
private:
  sp_playlistcontainer* playlistContainer;
  sp_playlistcontainer_callbacks* playlistContainerCallbacks;
  Nan::ObjectWrap* userdata;
public:
  PlaylistContainerCallbacksHolder(sp_playlistcontainer* pc, Nan::ObjectWrap* userdata);
  ~PlaylistContainerCallbacksHolder();

  //libspotify callback functions
  static void playlistAdded(sp_playlistcontainer* pc, sp_playlist* spPlaylist, int position, void* userdata);
  static void playlistRemoved(sp_playlistcontainer *pc, sp_playlist *playlist, int position, void *userdata);
  static void playlistMoved(sp_playlistcontainer *pc, sp_playlist *playlist, int position, int new_position, void *userdata);

  Nan::Callback playlistAddedCallback;
  Nan::Callback playlistRemovedCallback;
  Nan::Callback playlistMovedCallback;

  void setCallbacks();
  void unsetCallbacks();
};

#endif

<|start_filename|>src/objects/node/NodeAlbum.h<|end_filename|>
#ifndef _NODE_ALBUM_H
#define _NODE_ALBUM_H

#include "V8Browseable.h"
#include "../spotify/Album.h"

#include <nan.h>
#include <memory>

using namespace v8;

class NodeAlbum : public V8Browseable<NodeAlbum> {
friend class AlbumBrowseCallbacks;
private:
  std::unique_ptr<Album> album;
public:
  NodeAlbum(std::unique_ptr<Album> album);
  ~NodeAlbum();
  static void init();
  static NAN_GETTER(getName);
  static NAN_GETTER(getLink);
  static NAN_METHOD(getCoverLink);
  static NAN_METHOD(getCoverBase64);
  static NAN_METHOD(browse);
  static NAN_GETTER(getTracks);
  static NAN_GETTER(getCopyrights);
  static NAN_GETTER(getReview);
  static NAN_GETTER(getArtist);
  static NAN_GETTER(isLoaded);
};

#endif


<|start_filename|>src/objects/node/NodeWrapped.h<|end_filename|>
#ifndef _NODE_WRAPPED_H
#define _NODE_WRAPPED_H

#include <string>
#include <map>

#include <uv.h>
#include <node.h>
#include <nan.h>

/**
 * A class used as a base class for wrapping objects to node objects.
 **/
template <class T>
class NodeWrapped : public Nan::ObjectWrap {
public:
  ~NodeWrapped() {}
  
  virtual v8::Local<v8::Object> createInstance() {
    v8::Local<v8::Object> object = getConstructor()->NewInstance();
    this->Wrap(object);
    return object;
  }

  static v8::Local<v8::Function> getConstructor() {
    return Nan::New(constructorTemplate)->GetFunction();
  }
protected:
  static Nan::Persistent<v8::FunctionTemplate> constructorTemplate;

  /**
   * Basic init method for a wrapped node object.
   */
  static v8::Local<v8::FunctionTemplate> init(const char* className) {
    Nan::EscapableHandleScope scope;
    v8::Local<v8::FunctionTemplate> constructorTemplate = Nan::New<v8::FunctionTemplate>();
    constructorTemplate->SetClassName(Nan::New<v8::String>(className).ToLocalChecked());
    constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
    return scope.Escape(constructorTemplate);
  }
};

//The constructor template must be static per template instance not for all NodeWrapped subclasses.
template <class T> Nan::Persistent<v8::FunctionTemplate> NodeWrapped<T>::constructorTemplate;
#endif


<|start_filename|>src/objects/node/V8Browseable.h<|end_filename|>
#ifndef _V8_BROWSEABLE_H
#define _V8_BROWSEABLE_H

#include "NodeWrapped.h"

#include <nan.h>

template<class T>
class V8Browseable : public NodeWrapped<T> {
public:
  void callBrowseComplete() {
    Nan::HandleScope scope;
    v8::Local<v8::Value> argv[2] = {Nan::Undefined(), this->handle()};
    browseCompleteCallback.Call(2, argv);
  }
protected:
  void makePersistent() {
    persistentHandle.Reset(this->handle());
  }
  Nan::Callback browseCompleteCallback;
private:
  Nan::Persistent<v8::Object> persistentHandle;
};

#endif


<|start_filename|>src/objects/node/NodeSearch.cc<|end_filename|>
#include "NodeSearch.h"
#include "NodeTrack.h"
#include "NodeAlbum.h"
#include "NodeArtist.h"
#include "NodePlaylist.h"
#include "../../Application.h"

extern Application* application;

NodeSearch::NodeSearch(const char* _searchQuery) : searchQuery(_searchQuery), trackOffset(0), albumOffset(0), artistOffset(0), playlistOffset(0),
  trackLimit(10), albumLimit(10), artistLimit(10), playlistLimit(10) {

}

NodeSearch::NodeSearch(const char* _searchQuery, int offset) : searchQuery(_searchQuery), trackOffset(offset), albumOffset(offset), artistOffset(offset), playlistOffset(offset),
  trackLimit(10), albumLimit(10), artistLimit(10), playlistLimit(10) {

}

NodeSearch::NodeSearch(const char* _searchQuery, int offset, int limit) : searchQuery(_searchQuery), trackOffset(offset), albumOffset(offset), artistOffset(offset), playlistOffset(offset),
  trackLimit(limit), albumLimit(limit), artistLimit(limit), playlistLimit(limit) {

}

NAN_METHOD(NodeSearch::execute) {
  if(info.Length() < 1) {//TODO: how to check if it is a function? ->IsFunction() does not work, it does not recoginze functions.
    Nan::ThrowError("execute needs a callback function as its argument.");
    return;
  }
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->makePersistent();
  nodeSearch->browseCompleteCallback.SetFunction(info[0].As<Function>());
  nodeSearch->search = std::unique_ptr<Search>(new Search());
  nodeSearch->search->nodeObject = nodeSearch;
  nodeSearch->search->execute(nodeSearch->searchQuery, nodeSearch->trackOffset, nodeSearch->trackLimit,
    nodeSearch->albumOffset, nodeSearch->albumLimit,
    nodeSearch->artistOffset, nodeSearch->artistLimit,
    nodeSearch->playlistLimit, nodeSearch->playlistLimit);
  nodeSearch->setupAdditionalMethods();
  info.GetReturnValue().SetUndefined();
}

/**
 * Adds adiitional properties to the V8 object.
 * These will call libspotify functions and should first be available when the search has been executed.
 **/
void NodeSearch::setupAdditionalMethods() {
  Local<Object> nodeObject = this->handle();
  Nan::SetAccessor(nodeObject, Nan::New<String>("didYouMean").ToLocalChecked(), didYouMean);
  Nan::SetAccessor(nodeObject, Nan::New<String>("link").ToLocalChecked(), getLink);
  Nan::SetMethod(nodeObject, "getTrack", getTrack);
  Nan::SetMethod(nodeObject, "getAlbum", getAlbum);
  Nan::SetMethod(nodeObject, "getPlaylist", getPlaylist);
  Nan::SetAccessor(nodeObject, Nan::New<String>("totalTracks").ToLocalChecked(), getTotalTracks);
  Nan::SetAccessor(nodeObject, Nan::New<String>("numTracks").ToLocalChecked(), getNumTracks);
  Nan::SetAccessor(nodeObject, Nan::New<String>("totalAlbums").ToLocalChecked(), getTotalAlbums);
  Nan::SetAccessor(nodeObject, Nan::New<String>("numAlbums").ToLocalChecked(), getNumAlbums);
  Nan::SetAccessor(nodeObject, Nan::New<String>("totalArtists").ToLocalChecked(), getTotalArtists);
  Nan::SetAccessor(nodeObject, Nan::New<String>("numArtists").ToLocalChecked(), getNumArtists);
  Nan::SetAccessor(nodeObject, Nan::New<String>("totalPlaylists").ToLocalChecked(), getTotalPlaylists);
  Nan::SetAccessor(nodeObject, Nan::New<String>("numPlaylists").ToLocalChecked(), getNumPlaylists);
}

NAN_GETTER(NodeSearch::getTrackOffset) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->trackOffset));
}

NAN_SETTER(NodeSearch::setTrackOffset) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->trackOffset = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getAlbumOffset) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->albumOffset));
}

NAN_SETTER(NodeSearch::setAlbumOffset) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->albumOffset = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getArtistOffset) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->artistOffset));
}

NAN_SETTER(NodeSearch::setArtistOffset) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->artistOffset = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getPlaylistOffset) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->playlistOffset));
}

NAN_SETTER(NodeSearch::setPlaylistOffset) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->playlistOffset = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getTrackLimit) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->trackLimit));
}

NAN_SETTER(NodeSearch::setTrackLimit) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->trackLimit = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getAlbumLimit) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->albumLimit));
}

NAN_SETTER(NodeSearch::setAlbumLimit) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->albumLimit = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getArtistLimit) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->artistLimit));
}

NAN_SETTER(NodeSearch::setArtistLimit) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->artistLimit = value->ToInteger()->Value();
}

NAN_GETTER(NodeSearch::getPlaylistLimit) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->playlistLimit));
}

NAN_SETTER(NodeSearch::setPlaylistLimit) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  nodeSearch->playlistLimit = value->ToInteger()->Value();
}

NAN_METHOD(NodeSearch::New) {
  NodeSearch* search;
  Nan::Utf8String searchQuery(info[0]->ToString());
  if(info.Length() == 1) {
    search = new NodeSearch(*searchQuery);
  } else if(info.Length() == 2) {
    int offset = info[1]->ToInteger()->Value();
    search = new NodeSearch(*searchQuery, offset);
  } else if(info.Length() == 3) {
    int offset = info[1]->ToInteger()->Value();
    int limit = info[2]->ToInteger()->Value();
    search = new NodeSearch(*searchQuery, offset, limit);
  } else {
    Nan::ThrowError("Please provide an object to the node-spotify initializer function");
    return;
  }
  search->Wrap(info.This());
  info.GetReturnValue().Set(info.This());
}

NAN_GETTER(NodeSearch::didYouMean) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeSearch->search->didYouMeanText().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeSearch::getLink) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeSearch->search->link().c_str()).ToLocalChecked());
}

NAN_METHOD(NodeSearch::getTrack) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  if(info.Length() < 1 || !info[0]->IsNumber()) {
    Nan::ThrowError("getTrack needs a number as its first argument.");
    return;
  }

  int position = info[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numTracks() || position < 0) {
    Nan::ThrowError("Track index out of bounds");
    return;
  }

  NodeTrack* nodeTrack = new NodeTrack(nodeSearch->search->getTrack(position));
  info.GetReturnValue().Set(nodeTrack->createInstance());
}

NAN_METHOD(NodeSearch::getAlbum) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  if(info.Length() < 1 || !info[0]->IsNumber()) {
    Nan::ThrowError("getAlbum needs a number as its first argument.");
    return;
  }

  int position = info[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numAlbums() || position < 0) {
    Nan::ThrowError("Album index out of bounds");
    return;
  }

  NodeAlbum* nodeAlbum = new NodeAlbum(nodeSearch->search->getAlbum(position));
  info.GetReturnValue().Set(nodeAlbum->createInstance());
}

NAN_METHOD(NodeSearch::getArtist) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  if(info.Length() < 1 || !info[0]->IsNumber()) {
    Nan::ThrowError("getArtist needs a number as its first argument.");
    return;
  }

  int position = info[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numArtists() || position < 0) {
    Nan::ThrowError("Artist index out of bounds");
    return;
  }

  NodeArtist* nodeArtist = new NodeArtist(nodeSearch->search->getArtist(position));
  info.GetReturnValue().Set(nodeArtist->createInstance());
}

NAN_METHOD(NodeSearch::getPlaylist) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  if(info.Length() < 1 || !info[0]->IsNumber()) {
    Nan::ThrowError("getPlaylist needs a number as its first argument.");
    return;
  }

  int position = info[0]->ToNumber()->IntegerValue();
  if(position >= nodeSearch->search->numPlaylists() || position < 0) {
    Nan::ThrowError("Playlist index out of bounds");
    return;
  }

  NodePlaylist* nodePlaylist = new NodePlaylist(nodeSearch->search->getPlaylist(position));
  info.GetReturnValue().Set(nodePlaylist->createInstance());
}

NAN_GETTER(NodeSearch::getTotalTracks) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->search->totalTracks()));
}

NAN_GETTER(NodeSearch::getNumTracks) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->search->numTracks()));
}

NAN_GETTER(NodeSearch::getTotalAlbums) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->search->totalAlbums()));
}

NAN_GETTER(NodeSearch::getNumAlbums) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->search->numAlbums()));
}

NAN_GETTER(NodeSearch::getTotalArtists) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->search->totalArtists()));
}

NAN_GETTER(NodeSearch::getNumArtists) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->search->numArtists()));
}

NAN_GETTER(NodeSearch::getTotalPlaylists) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->search->totalPlaylists()));
}

NAN_GETTER(NodeSearch::getNumPlaylists) {
  NodeSearch* nodeSearch = Nan::ObjectWrap::Unwrap<NodeSearch>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodeSearch->search->numPlaylists()));
}

void NodeSearch::init() {
  Local<FunctionTemplate> constructorTemplate = Nan::New<FunctionTemplate>(New);
  constructorTemplate->SetClassName(Nan::New<String>("Search").ToLocalChecked());
  constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
  Nan::SetPrototypeMethod(constructorTemplate, "execute", execute);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("trackOffset").ToLocalChecked(), getTrackOffset, setTrackOffset);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("trackLimit").ToLocalChecked(), getTrackLimit, setTrackLimit);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("albumOffset").ToLocalChecked(), getAlbumOffset, setAlbumOffset);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("albumLimit").ToLocalChecked(), getAlbumLimit, setAlbumLimit);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("artistOffset").ToLocalChecked(), getArtistOffset, setArtistOffset);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("artistLimit").ToLocalChecked(), getArtistLimit, setArtistLimit);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("playlistOffset").ToLocalChecked(), getPlaylistOffset, setPlaylistOffset);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("playlistLimit").ToLocalChecked(), getPlaylistLimit, setPlaylistLimit);
  NodeSearch::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>src/callbacks/PlaylistCallbacksHolder.cc<|end_filename|>
#include "PlaylistCallbacksHolder.h"
#include "../objects/node/NodeTrackExtended.h"
#include "../objects/node/NodeUser.h"
#include "../objects/spotify/TrackExtended.h"
#include "../objects/spotify/Playlist.h"
#include "../objects/spotify/User.h"

#include <memory>

PlaylistCallbacksHolder::PlaylistCallbacksHolder(Nan::ObjectWrap* _userdata, sp_playlist* _playlist) : userdata(_userdata), playlist(_playlist) {
  playlistCallbacks = new sp_playlist_callbacks();
}

PlaylistCallbacksHolder::~PlaylistCallbacksHolder() {
  sp_playlist_remove_callbacks(playlist, playlistCallbacks, this);
  delete playlistCallbacks;
}

void PlaylistCallbacksHolder::playlistRenamed(sp_playlist* spPlaylist, void* userdata) {
  auto holder = static_cast<PlaylistCallbacksHolder*>(userdata);
  Local<Value> argv[] = { Nan::Undefined(), holder->userdata->handle() };
  holder->playlistRenamedCallback.Call(2, argv);
}

void PlaylistCallbacksHolder::tracksAdded(sp_playlist* spPlaylist, sp_track *const *tracks, int num_tracks, int position, void *userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistCallbacksHolder*>(userdata);
  Local<Array> nodeTracks = Nan::New<Array>(num_tracks);
  for(int i = 0; i < num_tracks; i++) {
    NodeTrack* nodeTrackExtended = new NodeTrackExtended(std::make_shared<TrackExtended>(tracks[i], spPlaylist, position + i));
    nodeTracks->Set(Nan::New<Number>(i), nodeTrackExtended->createInstance());
  }
  Local<Value> argv[] = { Nan::Undefined(), holder->userdata->handle(), nodeTracks, Nan::New<Number>(position) };
  holder->tracksAddedCallback.Call(4, argv);
}

void PlaylistCallbacksHolder::tracksMoved(sp_playlist* spPlaylist, const int* tracks, int num_tracks, int new_position, void *userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistCallbacksHolder*>(userdata);
  Local<Array> movedTrackIndices = Nan::New<Array>(num_tracks);
  for(int i = 0; i < num_tracks; i++) {
    movedTrackIndices->Set(Nan::New<Number>(i), Nan::New<Number>(tracks[i]));
  }
  Local<Value> argv[] = { Nan::Undefined(), holder->userdata->handle(), movedTrackIndices, Nan::New<Number>(new_position) };
  holder->tracksMovedCallback.Call(4, argv);
}

void PlaylistCallbacksHolder::tracksRemoved(sp_playlist* spPlaylist, const int *tracks, int num_tracks, void *userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistCallbacksHolder*>(userdata);
  Local<Array> removedTrackIndexes = Nan::New<Array>(num_tracks);
  for(int i = 0; i < num_tracks; i++) {
    removedTrackIndexes->Set(Nan::New<Number>(i), Nan::New<Number>(tracks[i]));
  }
  Local<Value> argv[] = { Nan::Undefined(), holder->userdata->handle(), removedTrackIndexes };
  holder->tracksRemovedCallback.Call(3, argv);
}

void PlaylistCallbacksHolder::trackCreatedChanged(sp_playlist* spPlaylist, int position, sp_user* spUser, int when, void* userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistCallbacksHolder*>(userdata);
  double date = (double)when * 1000;
  NodeUser* nodeUser = new NodeUser(std::unique_ptr<User>(new User(spUser)));
  Local<Value> argv[] = { Nan::Undefined(), holder->userdata->handle(), Nan::New<Integer>(position), nodeUser->handle(), Nan::New<Date>(date).ToLocalChecked() };
  holder->trackCreatedChangedCallback.Call(5, argv);
}

void PlaylistCallbacksHolder::trackSeenChanged(sp_playlist* spPlaylist, int position, bool seen, void* userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistCallbacksHolder*>(userdata);
  Local<Value> argv[] = { Nan::Undefined(), holder->userdata->handle(), Nan::New<Integer>(position), Nan::New<Boolean>(seen) };
  holder->trackSeenChangedCallback.Call(4, argv);
}

void PlaylistCallbacksHolder::trackMessageChanged(sp_playlist* spPlaylist, int position, const char* message, void* userdata) {
  Nan::HandleScope scope;
  auto holder = static_cast<PlaylistCallbacksHolder*>(userdata);
  Local<Value> argv[] = { Nan::Undefined(), holder->userdata->handle(), Nan::New<Integer>(position), Nan::New<String>(message).ToLocalChecked() };
  holder->trackMessageChangedCallback.Call(4, argv);
}

void PlaylistCallbacksHolder::setCallbacks() {
  sp_playlist_remove_callbacks(playlist, playlistCallbacks, this);
  
  if(!playlistRenamedCallback.IsEmpty()) {
    playlistCallbacks->playlist_renamed = &PlaylistCallbacksHolder::playlistRenamed;  
  }
  if(!tracksAddedCallback.IsEmpty()) {
    playlistCallbacks->tracks_added = &PlaylistCallbacksHolder::tracksAdded;  
  }
  if(!tracksMovedCallback.IsEmpty()) {
    playlistCallbacks->tracks_moved = &PlaylistCallbacksHolder::tracksMoved;  
  }
  if(!tracksRemovedCallback.IsEmpty()) {
    playlistCallbacks->tracks_removed = &PlaylistCallbacksHolder::tracksRemoved;  
  }
  if(!trackCreatedChangedCallback.IsEmpty()) {
    playlistCallbacks->track_created_changed = &PlaylistCallbacksHolder::trackCreatedChanged;
  }
  if(!trackSeenChangedCallback.IsEmpty()) {
    playlistCallbacks->track_seen_changed = &PlaylistCallbacksHolder::trackSeenChanged;
  }
  if(!trackMessageChangedCallback.IsEmpty()) {
    playlistCallbacks->track_message_changed = &PlaylistCallbacksHolder::trackMessageChanged;
  }
  sp_playlist_add_callbacks(playlist, playlistCallbacks, this);
}

void PlaylistCallbacksHolder::unsetCallbacks() {
  sp_playlist_remove_callbacks(playlist, playlistCallbacks, this);
}


<|start_filename|>src/audio/audio.c<|end_filename|>
/*
 * Copyright (c) 2010 Spotify Ltd
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 *
 * Audio helper functions.
 *
 * This file is part of the libspotify examples suite.
 */

#include "audio.h"
#include <stdlib.h>

int audio_stopThread;

#ifdef NODE_SPOTIFY_NATIVE_SOUND
static uv_thread_t audioThread;
#endif

audio_fifo_data_t* audio_get(audio_fifo_t* audioFifo) {
  uv_mutex_lock(&audioFifo->audioQueueMutex);

  audio_fifo_data_t* audioData = TAILQ_FIRST(&audioFifo->queue);
  if(audioData) {
    TAILQ_REMOVE(&audioFifo->queue, audioData, link);
    audioFifo->samplesInQueue -= audioData->numberOfSamples;
  }

  uv_mutex_unlock(&audioFifo->audioQueueMutex);
  return audioData;
}

void audio_fifo_flush(audio_fifo_t* audioFifo) {
  audio_fifo_data_t *audioData;
  uv_mutex_lock(&audioFifo->audioQueueMutex);

  while((audioData = TAILQ_FIRST(&audioFifo->queue))) {
	  TAILQ_REMOVE(&audioFifo->queue, audioData, link);
	  free(audioData);
  }

  audioFifo->samplesInQueue = 0;
  uv_mutex_unlock(&audioFifo->audioQueueMutex);
}

/**
Initialize the audio queue and the locking mutex
**/
void audio_init(audio_fifo_t* audioFifo) {
  TAILQ_INIT(&audioFifo->queue);
  audioFifo->samplesInQueue = 0;
  uv_mutex_init(&audioFifo->audioQueueMutex);
}

void audio_stop(audio_fifo_t *audioFifo) {
  audio_fifo_flush(audioFifo);
  uv_mutex_destroy(&audioFifo->audioQueueMutex);
}

#ifdef NODE_SPOTIFY_NATIVE_SOUND
/**
Gets the audio data from the queue. If there is none, waits for the condition variable to trigger.
**/
audio_fifo_data_t* audio_get_native(audio_fifo_t* audioFifo) {
  audio_fifo_data_t* audioData;
  uv_mutex_lock(&audioFifo->audioQueueMutex);

  while( !audio_stopThread && !(audioData = TAILQ_FIRST(&audioFifo->queue)) ) {
    uv_cond_wait(&audioFifo->audioCondition, &audioFifo->audioQueueMutex);
  }

  if(audio_stopThread) {
    uv_mutex_unlock(&audioFifo->audioQueueMutex);
    return NULL;
  }

  TAILQ_REMOVE(&audioFifo->queue, audioData, link);
  audioFifo->samplesInQueue -= audioData->numberOfSamples;

  uv_mutex_unlock(&audioFifo->audioQueueMutex);
  return audioData;
}

void audio_init_native(audio_fifo_t* audioFifo) {
  audio_stopThread = 0;
  uv_cond_init(&audioFifo->audioCondition);
  uv_thread_create(&audioThread, audio_start, audioFifo);
}

void audio_stop_native(audio_fifo_t* audioFifo) {
  audio_stopThread  = 1;
  //Notify audio_get_native to return in case it is waiting for data
  uv_mutex_lock(&audioFifo->audioQueueMutex);
  uv_cond_signal(&audioFifo->audioCondition);
  uv_mutex_unlock(&audioFifo->audioQueueMutex);
  audio_fifo_flush(audioFifo);
  uv_thread_join(&audioThread);
  uv_cond_destroy(&audioFifo->audioCondition);
}
#endif


<|start_filename|>src/objects/node/NodeAlbum.cc<|end_filename|>
#include "NodeAlbum.h"
#include "NodeTrack.h"
#include "NodeArtist.h"
#include "../spotify/Track.h"

NodeAlbum::NodeAlbum(std::unique_ptr<Album> _album) : album(std::move(_album)) {
  album->nodeObject = this;
};

NodeAlbum::~NodeAlbum() {
  if(album->nodeObject == this) {
    album->nodeObject = nullptr;
  }
}

NAN_GETTER(NodeAlbum::getName) {
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeAlbum->album->name().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeAlbum::getLink) {
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeAlbum->album->link().c_str()).ToLocalChecked());
}

NAN_METHOD(NodeAlbum::getCoverLink) {
  sp_image_size imageSize = SP_IMAGE_SIZE_NORMAL;
  if(info.Length() > 0 && info[0]->IsNumber()) {
    imageSize = static_cast<sp_image_size>(info[0]->ToNumber()->IntegerValue());
  }
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeAlbum->album->coverLink(imageSize).c_str()).ToLocalChecked());
}

NAN_METHOD(NodeAlbum::getCoverBase64) {
  sp_image_size imageSize = SP_IMAGE_SIZE_NORMAL;
  if(info.Length() > 0 && info[0]->IsNumber()) {
    imageSize = static_cast<sp_image_size>(info[0]->ToNumber()->IntegerValue());
  }
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeAlbum->album->coverBase64(imageSize).c_str()).ToLocalChecked());
}

NAN_METHOD(NodeAlbum::browse) {
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  if(nodeAlbum->album->albumBrowse == nullptr) {
    nodeAlbum->makePersistent();
    nodeAlbum->browseCompleteCallback.SetFunction(info[0].As<Function>());

    //Mutate the V8 object.
    Local<Object> nodeAlbumV8 = nodeAlbum->handle();
    Nan::SetAccessor(nodeAlbumV8, Nan::New<String>("tracks").ToLocalChecked(), getTracks);
    Nan::SetAccessor(nodeAlbumV8, Nan::New<String>("review").ToLocalChecked(), getReview);
    Nan::SetAccessor(nodeAlbumV8, Nan::New<String>("copyrights").ToLocalChecked(), getCopyrights);
    Nan::SetAccessor(nodeAlbumV8, Nan::New<String>("artist").ToLocalChecked(), getArtist);

    nodeAlbum->album->browse();
  } else {
    nodeAlbum->callBrowseComplete();
  }
  info.GetReturnValue().SetUndefined();
}

NAN_GETTER(NodeAlbum::getTracks) {
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  std::vector<std::shared_ptr<Track>> tracks = nodeAlbum->album->tracks();
  Local<Array> nodeTracks = Nan::New<Array>(tracks.size());
  for(int i = 0; i < (int)tracks.size(); i++) {
    NodeTrack* nodeTrack = new NodeTrack(tracks[i]);
    nodeTracks->Set(Nan::New<Number>(i), nodeTrack->createInstance());
  }
  info.GetReturnValue().Set(nodeTracks);
}

NAN_GETTER(NodeAlbum::getReview) {
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeAlbum->album->review().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeAlbum::getCopyrights) {
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  std::vector<std::string> copyrights = nodeAlbum->album->copyrights();
  Local<Array> nodeCopyrights = Nan::New<Array>(copyrights.size());
  for(int i = 0; i < (int)copyrights.size(); i++) {
    nodeCopyrights->Set(Nan::New<Number>(i), Nan::New<String>(copyrights[i].c_str()).ToLocalChecked());
  }
  info.GetReturnValue().Set(nodeCopyrights);
}

NAN_GETTER(NodeAlbum::getArtist) {
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  NodeArtist* nodeArtist = new NodeArtist(nodeAlbum->album->artist());
  info.GetReturnValue().Set(nodeArtist->createInstance());
}

NAN_GETTER(NodeAlbum::isLoaded) {
  NodeAlbum* nodeAlbum = Nan::ObjectWrap::Unwrap<NodeAlbum>(info.This());
  info.GetReturnValue().Set(Nan::New<Boolean>(nodeAlbum->album->isLoaded()));
}

void NodeAlbum::init() {
  Local<FunctionTemplate> constructorTemplate = NodeWrapped::init("Album");
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("name").ToLocalChecked(), getName);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("link").ToLocalChecked(), getLink);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("isLoaded").ToLocalChecked(), isLoaded);
  Nan::SetPrototypeMethod(constructorTemplate, "getCoverBase64", getCoverBase64);
  Nan::SetPrototypeMethod(constructorTemplate, "getCoverLink", getCoverLink);
  Nan::SetPrototypeMethod(constructorTemplate, "browse", browse);
  NodeAlbum::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>package.json<|end_filename|>
{
  "name": "node-spotify",
  "version": "0.8.2",
  "description": "A wrapper for libspotify",
  "keywords": [
    "spotify",
    "music"
  ],
  "homepage": "http://www.node-spotify.com",
  "bugs": {
    "url": "https://github.com/FrontierPsychiatrist/node-spotify/issues",
    "email": "<EMAIL>"
  },
  "license": "MIT",
  "author": "<NAME> <<EMAIL>> (http://www.node-spotify.com)",
  "main": "lib/spotify.js",
  "repository": {
    "type": "git",
    "url": "https://github.com/FrontierPsychiatrist/node-spotify"
  },
  "devDependencies": {
    "aws-sdk": "~2.3"
  },
  "dependencies": {
    "node-pre-gyp": "0.6.x",
    "nan": "2.3.5"
  },
  "scripts": {
    "preinstall": "npm install node-pre-gyp",
    "install": "node-pre-gyp install --fallback-to-build"
  },
  "binary": {
    "module_name": "nodespotify",
    "module_path": "./lib/binding",
    "host": "https://node-spotify.s3-eu-central-1.amazonaws.com",
    "remote_path": "./{name}/{version}"
  }
}


<|start_filename|>test/test.js<|end_filename|>
var baseTest = require('./basetest.js');
var spotify = baseTest.spotify;

baseTest.executeTest(tests);

function tests() {
  console.log('Starting tests');
  /* Playlists */
  var playlistContainer = spotify.playlistContainer;
  var playlists = playlistContainer.getPlaylists();
  console.log('Playlists: ' + playlists.length)
  var starredPlaylist = spotify.sessionUser.starredPlaylist;
  console.log('Starred playlist accessible');
  var firstPlaylist = playlists[0];
  console.log('Playlistname: ' + firstPlaylist.name);
  console.log('Playlistlink: ' + firstPlaylist.link);

  /* Tracks */
  var starredTracks = starredPlaylist.getTracks();
  console.log('Starred tracks: ' + starredTracks.length);
  var firstStarredTrack = starredTracks[0];
  console.log('Trackname: ' + firstStarredTrack.name);
  console.log('Track is starred: ' + firstStarredTrack.starred);
  console.log('Track link: ' + firstStarredTrack.link);
  console.log('Track availability: ' + firstStarredTrack.availability);
  /* Album */ 
  var album = firstStarredTrack.album;
  console.log('Albumname: ' + album.name);
  console.log('Album link: ' + album.link);

  /* Artists */
  var artists = firstStarredTrack.artists;
  console.log('Artists: ' + artists.length);
  var firstArtist = artists[0];
  console.log('Artistname: ' + firstArtist.name);
  console.log('Artist link: ' + firstArtist.link);

  spotify.logout(function () {
    process.exit();
  });
}


<|start_filename|>src/exceptions.h<|end_filename|>
#ifndef _NODESPOTIFY_EXCEPTIONS_H
#define _NODESPOTIFY_EXCEPTIONS_H

#define CREATE_EXCEPTION_WITH_MESSAGE(name) class name : public ExceptionWithMessage {\
  public:\
    name(const char* message) : ExceptionWithMessage(message) {};\
  };

#include <exception>

class ExceptionWithMessage {
public:
  ExceptionWithMessage(const char* _message) : message(_message) {}
  std::string message;
};
class FileException : public std::exception {};
class TrackNotPlayableException : public std::exception {};
class PlaylistCreationException : public std::exception {};
class PlaylistNotDeleteableException : public std::exception {};
class TracksNotRemoveableException : public std::exception {};
#ifndef NODE_SPOTIFY_NATIVE_SOUND
class NoAudioHandlerException : public std::exception {};
#endif

CREATE_EXCEPTION_WITH_MESSAGE(PlaylistNotMoveableException)
CREATE_EXCEPTION_WITH_MESSAGE(TracksNotReorderableException)
CREATE_EXCEPTION_WITH_MESSAGE(SessionCreationException)
CREATE_EXCEPTION_WITH_MESSAGE(TracksNotAddedException)
CREATE_EXCEPTION_WITH_MESSAGE(SocialProviderScrobblingException)

#endif


<|start_filename|>src/objects/node/NodeTrackExtended.h<|end_filename|>
#ifndef _NODE_TRACK_EXTENDED_H
#define _NODE_TRACK_EXTENDED_H

#include "NodeTrack.h"
#include "../spotify/TrackExtended.h"

#include <nan.h>
#include <memory>

using namespace v8;

class NodeTrackExtended : public NodeTrack {
private:
  std::shared_ptr<TrackExtended> trackExtended;
public:
  NodeTrackExtended(std::shared_ptr<TrackExtended> trackExtended);
  static NAN_GETTER(getCreator);
  static NAN_GETTER(getSeen);
  static NAN_SETTER(setSeen);
  static NAN_GETTER(getCreateTime);
  static NAN_GETTER(getMessage);
  static void init();
  static Local<Function> getConstructor();
  Local<Object> createInstance();
protected:
  static Nan::Persistent<FunctionTemplate> constructorTemplate;
};

#endif


<|start_filename|>src/objects/node/NodePlaylist.cc<|end_filename|>
#include "NodePlaylist.h"
#include "../../exceptions.h"
#include "../spotify/Track.h"
#include "../spotify/TrackExtended.h"
#include "NodeTrack.h"
#include "NodeTrackExtended.h"
#include "NodeUser.h"

NodePlaylist::NodePlaylist(std::shared_ptr<Playlist> _playlist) : playlist(_playlist),
  playlistCallbacksHolder(this, _playlist->playlist) {
}

NodePlaylist::~NodePlaylist() {
  
}

NAN_SETTER(NodePlaylist::setName) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  Nan::Utf8String newName(value);
  nodePlaylist->playlist->name(*newName);
}

NAN_GETTER(NodePlaylist::getName) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodePlaylist->playlist->name().c_str()).ToLocalChecked());
}

NAN_SETTER(NodePlaylist::setCollaborative) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  nodePlaylist->playlist->setCollaborative(value->ToBoolean()->Value());
}

NAN_GETTER(NodePlaylist::getCollaborative) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  info.GetReturnValue().Set(Nan::New<Boolean>(nodePlaylist->playlist->isCollaborative()));
}

NAN_GETTER(NodePlaylist::getLink) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodePlaylist->playlist->link().c_str()).ToLocalChecked());
}

NAN_GETTER(NodePlaylist::getDescription) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodePlaylist->playlist->description().c_str()).ToLocalChecked());
}

NAN_GETTER(NodePlaylist::getNumTracks) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodePlaylist->playlist->numTracks()));
}

NAN_METHOD(NodePlaylist::getTrack) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  if(info.Length() < 1 || !info[0]->IsNumber()) {
    Nan::ThrowError("getTrack needs a number as its first argument.");
    return;
  }
  int position = info[0]->ToNumber()->IntegerValue();
  if(position >= nodePlaylist->playlist->numTracks() || position < 0) {
    Nan::ThrowError("Track index out of bounds");
    return;
  }
  std::shared_ptr<TrackExtended> track = nodePlaylist->playlist->getTrack(position);
  NodeTrackExtended* nodeTrack = new NodeTrackExtended(track);
  info.GetReturnValue().Set(nodeTrack->createInstance());
}

NAN_METHOD(NodePlaylist::addTracks) {
  if(info.Length() < 2 || !info[0]->IsArray() || !info[1]->IsNumber()) {
    Nan::ThrowError("addTracks needs an array and a number as its arguments.");
    return;
  }
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  Handle<Array> trackArray = Handle<Array>::Cast(info[0]);
  std::vector<std::shared_ptr<Track>> tracks(trackArray->Length());
  for(unsigned int i = 0; i < trackArray->Length(); i++) {
    Local<Object> trackObject = trackArray->Get(i)->ToObject();
    NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(trackObject);
    tracks[i] = nodeTrack->track;
  }
  int position = info[1]->ToNumber()->IntegerValue();
  try {
    nodePlaylist->playlist->addTracks(tracks, position);
  } catch(const TracksNotAddedException& e) {
    Nan::ThrowError(e.message.c_str());
    return;
  }

  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodePlaylist::removeTracks) {
  if(info.Length() < 1 || !info[0]->IsArray()) {
    Nan::ThrowError("removeTracks needs an array as its first argument.");
    return;
  }
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  Handle<Array> trackPositionsArray = Handle<Array>::Cast(info[0]);
  int trackPositions[trackPositionsArray->Length()];
  for(unsigned int i = 0; i < trackPositionsArray->Length(); i++) {
    trackPositions[i] = trackPositionsArray->Get(i)->ToNumber()->IntegerValue();
  }
  try {
    nodePlaylist->playlist->removeTracks(trackPositions, trackPositionsArray->Length());
  } catch(const TracksNotRemoveableException& e) {
    Nan::ThrowError("Tracks not removeable, permission denied.");
    return;
  }

  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodePlaylist::reorderTracks) {
  if(info.Length() < 2 || !info[0]->IsArray() || !info[1]->IsNumber()) {
    Nan::ThrowError("reorderTracks needs an array and a numer as its arguments.");
    return;
  }
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  Handle<Array> trackPositionsArray = Handle<Array>::Cast(info[0]);
  int trackPositions[trackPositionsArray->Length()];
  int newPosition = info[1]->ToNumber()->IntegerValue();
  for(unsigned int i = 0; i < trackPositionsArray->Length(); i++) {
    trackPositions[i] = trackPositionsArray->Get(i)->ToNumber()->IntegerValue();
  }
  try {
    nodePlaylist->playlist->reorderTracks(trackPositions, trackPositionsArray->Length(), newPosition);
  } catch(const TracksNotReorderableException& e) {
    Nan::ThrowError(e.message.c_str());
    return;
  }

  info.GetReturnValue().SetUndefined();
}

NAN_GETTER(NodePlaylist::isLoaded) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  info.GetReturnValue().Set(Nan::New<Boolean>(nodePlaylist->playlist->isLoaded()));
}

NAN_GETTER(NodePlaylist::getOwner) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  Local<Value> owner;
  if(nodePlaylist->playlist->owner()) {
    owner = (new NodeUser(nodePlaylist->playlist->owner()))->createInstance();
  }
  info.GetReturnValue().Set(owner);
}

/**
  Set all callbacks for this playlist. Replaces all old callbacks.
**/
NAN_METHOD(NodePlaylist::on) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  if(info.Length() < 1 || !info[0]->IsObject()) {
    Nan::ThrowError("on needs an object as its first argument.");
    return;
  }
  Handle<Object> callbacks = info[0]->ToObject();
  Handle<String> playlistRenamedKey = Nan::New<String>("playlistRenamed").ToLocalChecked();
  if(callbacks->Has(playlistRenamedKey)) {
    nodePlaylist->playlistCallbacksHolder.playlistRenamedCallback.SetFunction(callbacks->Get(playlistRenamedKey).As<Function>());
  }

  Handle<String> tracksMovedKey = Nan::New<String>("tracksMoved").ToLocalChecked();
  if(callbacks->Has(tracksMovedKey)) {
    nodePlaylist->playlistCallbacksHolder.tracksAddedCallback.SetFunction(callbacks->Get(tracksMovedKey).As<Function>());
  }

  Handle<String> tracksAddedKey = Nan::New<String>("tracksAdded").ToLocalChecked();
  if(callbacks->Has(tracksAddedKey)) {
    nodePlaylist->playlistCallbacksHolder.tracksMovedCallback.SetFunction(callbacks->Get(tracksAddedKey).As<Function>());
  }

  Handle<String> tracksRemovedKey = Nan::New<String>("tracksRemoved").ToLocalChecked();
  if(callbacks->Has(tracksRemovedKey)) {
    nodePlaylist->playlistCallbacksHolder.tracksRemovedCallback.SetFunction(callbacks->Get(tracksRemovedKey).As<Function>());
  }

  Handle<String> trackCreatedChangedKey = Nan::New<String>("trackCreatedChanged").ToLocalChecked();
  if(callbacks->Has(trackCreatedChangedKey)) {
    nodePlaylist->playlistCallbacksHolder.trackCreatedChangedCallback.SetFunction(callbacks->Get(trackCreatedChangedKey).As<Function>());
  }

  Handle<String> trackSeenChangedKey = Nan::New<String>("trackSeenChanged").ToLocalChecked();
  if(callbacks->Has(trackSeenChangedKey)) {
    nodePlaylist->playlistCallbacksHolder.trackSeenChangedCallback.SetFunction(callbacks->Get(trackSeenChangedKey).As<Function>());
  }

  Handle<String> trackMessageChangedKey = Nan::New<String>("trackMessageChanged").ToLocalChecked();
  if(callbacks->Has(trackMessageChangedKey)) {
    nodePlaylist->playlistCallbacksHolder.trackMessageChangedCallback.SetFunction(callbacks->Get(trackMessageChangedKey).As<Function>());
  }
  nodePlaylist->playlistCallbacksHolder.setCallbacks();
  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodePlaylist::off) {
  NodePlaylist* nodePlaylist = Nan::ObjectWrap::Unwrap<NodePlaylist>(info.This());
  nodePlaylist->playlistCallbacksHolder.unsetCallbacks();
  info.GetReturnValue().SetUndefined();
}

void NodePlaylist::init() {
  Local<FunctionTemplate> constructorTemplate = Nan::New<FunctionTemplate>();
  constructorTemplate->SetClassName(Nan::New<String>("Playlist").ToLocalChecked());
  constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
  Nan::SetPrototypeMethod(constructorTemplate, "on", on);
  Nan::SetPrototypeMethod(constructorTemplate, "off", off);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("name").ToLocalChecked(), getName, setName);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("link").ToLocalChecked(), getLink);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("collaborative").ToLocalChecked(), getCollaborative, setCollaborative);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("description").ToLocalChecked(), getDescription);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("owner").ToLocalChecked(), getOwner);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("isLoaded").ToLocalChecked(), isLoaded);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("numTracks").ToLocalChecked(), getNumTracks);
  Nan::SetPrototypeMethod(constructorTemplate, "getTrack", getTrack);
  Nan::SetPrototypeMethod(constructorTemplate, "addTracks", addTracks);
  Nan::SetPrototypeMethod(constructorTemplate, "removeTracks", removeTracks);
  Nan::SetPrototypeMethod(constructorTemplate, "reorderTracks", reorderTracks);

  NodePlaylist::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>test/search.js<|end_filename|>
var baseTest = require('./basetest.js');
var assert = require('assert');
var spotify = baseTest.spotify;

baseTest.executeTest(test);

function test() {
  console.log('Starting tests');
  var search = new spotify.Search('godspeed');
  search.albumLimit = 10;
  search.artistLimit = 10;
  search.trackLimit = 10;
  search.playlistLimit = 10;
  search.execute(searchComplete);
}

function searchComplete(err, search) {
  console.log('Search executed');
  assert(search.numTracks != 0);
  assert(search.numAlbums != 0);
  assert(search.numArtists != 0);
  assert(search.totalTracks != 0);
  assert(search.totalArtists != 0);
  assert(search.totalAlbums != 0);
  assert(search.totalPlaylists != 0);
  console.log('Total number of tracks:' + search.totalTracks);
  console.log('Total number of artists:' + search.totalArtists);
  console.log('Total number of albums:' + search.totalAlbums);
  console.log('Total number of playlists:' + search.totalPlaylists);
  console.log(search.didYouMean);
  spotify.logout(function () {
    process.exit();
  });
}


<|start_filename|>src/callbacks/SessionCallbacks.h<|end_filename|>
#ifndef _SPOTIFY_SERVICE_SESSION_CALLBACKS_H
#define _SPOTIFY_SERVICE_SESSION_CALLBACKS_H

#include <libspotify/api.h>
#include <nan.h>

class SessionCallbacks {
public:
  static void notifyMainThread(sp_session* session);
  static void loggedIn(sp_session* session, sp_error error);
  static void loggedOut(sp_session* session);
  static void playTokenLost(sp_session* session);
  static void end_of_track(sp_session* session);
  static void init();
  static void metadata_updated(sp_session* session);
  static void start_playback(sp_session* session);
  static void stop_playback(sp_session* session);
  static void rootPlaylistContainerLoaded(sp_playlistcontainer* sp, void* userdata);
  static Nan::Callback loginCallback;
  static Nan::Callback logoutCallback;
  static Nan::Callback metadataUpdatedCallback;
  static Nan::Callback endOfTrackCallback;
  static Nan::Callback playTokenLostCallback;
};

#endif


<|start_filename|>src/audio/NativeAudioHandler.h<|end_filename|>
#ifndef NATIVEAUDIOHANDLER_H
#define NATIVEAUDIOHANDLER_H

#include "AudioHandler.h"

/**
 * @brief The NativeAudioHandler class
 * Handles audio in a native audio system like ALSA or OpenAL.
 */
class NativeAudioHandler : public AudioHandler {
public:
  NativeAudioHandler();
  ~NativeAudioHandler();
protected:
  void afterMusicDelivery(const sp_audioformat* format);
  bool dataNeeded();
};

#endif // NATIVEAUDIOHANDLER_H


<|start_filename|>src/audio/audio.h<|end_filename|>
/*
 * Copyright (c) 2006-2009 Spotify Ltd
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 *
 * Audio output driver.
 *
 * This file is part of the libspotify examples suite.
 */
#ifndef _JUKEBOX_AUDIO_H_
#define _JUKEBOX_AUDIO_H_

#include <uv.h>
#include <stdint.h>
#include "queue.h"

typedef struct audio_fifo_data {
  TAILQ_ENTRY(audio_fifo_data) link;
  int channels;
  int sampleRate;
  int numberOfSamples;
  int16_t samples[0];
} audio_fifo_data_t;

typedef struct audio_fifo {
  TAILQ_HEAD(, audio_fifo_data) queue;
  int samplesInQueue;
  uv_mutex_t audioQueueMutex;
#ifdef NODE_SPOTIFY_NATIVE_SOUND
  uv_cond_t audioCondition;
#endif
} audio_fifo_t;

extern void audio_fifo_flush(audio_fifo_t *audioFifo);
#ifdef NODE_SPOTIFY_NATIVE_SOUND
/**
Initializes the native audio system and the condition variable.
**/
void audio_init_native(audio_fifo_t *audioFifo);
void audio_stop_native(audio_fifo_t* audioFifo);
audio_fifo_data_t* audio_get_native(audio_fifo_t* audioFifo);
void audio_start(void* aux);
#endif

/**
Gets audio data from the queue
**/
audio_fifo_data_t* audio_get(audio_fifo_t* audioFifo);
void audio_init(audio_fifo_t* audioFifo);
void audio_stop(audio_fifo_t* audioFifo);

#endif


<|start_filename|>src/audio/NativeAudioHandler.cc<|end_filename|>
#include "NativeAudioHandler.h"

NativeAudioHandler::NativeAudioHandler() {
  audio_init_native(&audioFifo);
}

NativeAudioHandler::~NativeAudioHandler() {
  audio_stop_native(&audioFifo);
}

void NativeAudioHandler::afterMusicDelivery(const sp_audioformat *format) {
  uv_cond_signal(&audioFifo.audioCondition);
}

bool NativeAudioHandler::dataNeeded() {
  return true;
}


<|start_filename|>src/objects/spotify/Search.h<|end_filename|>
#ifndef _SEARCH_RESULT_H
#define _SEARCH_RESULT_H

#include "Track.h"
#include "Album.h"
#include "Playlist.h"
#include "Artist.h"
#include "../node/V8Browseable.h"

#include <memory>
#include <string>

class NodeSearch;

class Search {
friend class NodeSearch;
friend class SearchCallbacks;
public:
  Search() {}
  Search(const Search& other);
  ~Search();
  std::shared_ptr<Track> getTrack(int position);
  std::unique_ptr<Album> getAlbum(int position);
  std::unique_ptr<Artist> getArtist(int position);
  std::shared_ptr<Playlist> getPlaylist(int position);
  void execute(std::string query, int trackOffset, int trackLimit,
    int albumOffset, int albumLimit,
    int artistOffset, int artistLimit,
    int playlistOffset, int playlistLimit);
  std::string link();
  std::string didYouMeanText();
  int numTracks();
  int totalTracks();
  int numAlbums();
  int totalAlbums();
  int numArtists();
  int totalArtists();
  int numPlaylists();
  int totalPlaylists();
private:
  sp_search* search;
  V8Browseable<NodeSearch>* nodeObject;
};

#endif


<|start_filename|>src/objects/node/NodePlaylist.h<|end_filename|>
#ifndef _NODE_PLAYLIST_H
#define _NODE_PLAYLIST_H

#include "NodeWrapped.h"
#include "../../callbacks/PlaylistCallbacksHolder.h"
#include "../spotify/Playlist.h"

#include <v8.h>
#include <map>
#include <string>
#include <memory>

using namespace v8;

class NodePlaylist : public NodeWrapped<NodePlaylist> {
private:
  std::shared_ptr<Playlist> playlist;
  PlaylistCallbacksHolder playlistCallbacksHolder;
public:
  NodePlaylist(std::shared_ptr<Playlist> playlist);
  ~NodePlaylist();
  static NAN_SETTER(setName);
  static NAN_GETTER(getName);
  static NAN_SETTER(setCollaborative);
  static NAN_GETTER(getCollaborative);
  static NAN_GETTER(getLink);
  static NAN_GETTER(getDescription);
  static NAN_GETTER(getNumTracks);
  static NAN_METHOD(getTrack);
  static NAN_METHOD(addTracks);
  static NAN_METHOD(removeTracks);
  static NAN_METHOD(reorderTracks);
  static NAN_GETTER(isLoaded);
  static NAN_GETTER(getOwner);
  static NAN_METHOD(on);
  static NAN_METHOD(off);
  static void init();
};

#endif


<|start_filename|>src/objects/spotify/Player.cc<|end_filename|>
#include "Player.h"
#include "../../exceptions.h"
#include "../../Application.h"

extern Application* application;

Player::Player() : currentSecond(0), isPaused(false), isLoading(false), loadingTrack(nullptr) {}

void Player::stop() {
  sp_session_player_unload(application->session);
}

void Player::pause() {
  sp_session_player_play(application->session, 0);
  application->audioHandler->setStopped(true);
  isPaused = true;
}

void Player::resume() {
 if(isPaused) {
    sp_session_player_play(application->session, 1);
    application->audioHandler->setStopped(false);
    isPaused = false;
  }
}

void Player::play(std::shared_ptr<Track> track) {
#ifndef NODE_SPOTIFY_NATIVE_SOUND
  //If node-spotify is compiled without native sound we have to check if the user registered a nodejs audio handler.
  if(!application->audioHandler) {
    throw NoAudioHandlerException();
  }
#endif
  application->audioHandler->setStopped(false);
  application->audioHandler->framesReceived = 0;
  application->audioHandler->currentSecond = 0;
  currentSecond = 0;
  sp_error error = sp_session_player_load(application->session, track->track);
  if(error == SP_ERROR_IS_LOADING) {
    isLoading = true;
    loadingTrack = track->track;
  } else if (error == SP_ERROR_TRACK_NOT_PLAYABLE) {
    throw TrackNotPlayableException();
  } else {
    sp_session_player_play(application->session, 1);
  }
}

void Player::retryPlay() {
  sp_error error = sp_track_error(loadingTrack);
  if(error != SP_ERROR_IS_LOADING) {
    sp_session_player_load(application->session, loadingTrack);
    sp_session_player_play(application->session, 1);
    isLoading = false;
    loadingTrack = nullptr;
  }
}

void Player::seek(int second) {
  sp_session_player_seek(application->session, second*1000);
  application->audioHandler->currentSecond = second;
}

void Player::setCurrentSecond(int second) {
  currentSecond = second;
}


<|start_filename|>src/callbacks/PlaylistCallbacksHolder.h<|end_filename|>
#ifndef _SPOTIFY_SERVICE_PLAYLIST_CALLBACKS_HOLDER_H
#define _SPOTIFY_SERVICE_PLAYLIST_CALLBACKS_HOLDER_H

#include <libspotify/api.h>
#include <nan.h>

using namespace v8;

class PlaylistCallbacksHolder {
private:
  Nan::ObjectWrap* userdata;
  sp_playlist* playlist;
  sp_playlist_callbacks* playlistCallbacks;
public:
  PlaylistCallbacksHolder(Nan::ObjectWrap* userdata, sp_playlist* playlist);
  ~PlaylistCallbacksHolder();

  //libspotify callback functions.
  static void playlistRenamed(sp_playlist* spPlaylist, void* userdata);
  static void tracksAdded(sp_playlist* playlist, sp_track *const *tracks, int num_tracks, int position, void *userdata);
  static void tracksMoved(sp_playlist* playlist, const int* tracks, int num_tracks, int new_position, void *userdata);
  static void tracksRemoved(sp_playlist* spPlaylist, const int *tracks, int num_tracks, void *userdata);
  static void trackCreatedChanged(sp_playlist* spPlaylist, int position, sp_user* spUser, int when, void* userdata);
  static void trackSeenChanged(sp_playlist* spPlaylist, int position, bool seen, void* userdata);
  static void trackMessageChanged(sp_playlist* spPlaylist, int position, const char* message, void* userdata);
  
  Nan::Callback playlistRenamedCallback;
  Nan::Callback tracksAddedCallback;
  Nan::Callback tracksMovedCallback;
  Nan::Callback tracksRemovedCallback;
  Nan::Callback trackCreatedChangedCallback;
  Nan::Callback trackSeenChangedCallback;
  Nan::Callback trackMessageChangedCallback;
  /**
    Register the callbacks with libspotify. Will first remove old registered callbacks.
  **/
  void setCallbacks();
  /**
    Unregister all callbacks.
  **/
  void unsetCallbacks();
};

#endif


<|start_filename|>src/objects/node/NodeArtist.cc<|end_filename|>
#include "NodeArtist.h"
#include "NodeTrack.h"
#include "NodeAlbum.h"

NodeArtist::NodeArtist(std::unique_ptr<Artist> _artist) : artist(std::move(_artist)) {
  artist->nodeObject = this;
}

NodeArtist::~NodeArtist() {
  if(artist->nodeObject == this) {
    artist->nodeObject = nullptr;
  }
}

NAN_GETTER(NodeArtist::getName) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeArtist->artist->name().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeArtist::getLink) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeArtist->artist->link().c_str()).ToLocalChecked());
}

NAN_METHOD(NodeArtist::browse) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  if(nodeArtist->artist->artistBrowse == nullptr) {
    nodeArtist->makePersistent();
    sp_artistbrowse_type artistbrowseType = static_cast<sp_artistbrowse_type>(info[0]->ToNumber()->IntegerValue());
    nodeArtist->browseCompleteCallback.SetFunction(info[1].As<Function>());

    //Mutate the V8 object.
    Local<Object> nodeArtistV8 = nodeArtist->handle();
    Nan::SetAccessor(nodeArtistV8, Nan::New<String>("tracks").ToLocalChecked(), getTracks);
    Nan::SetAccessor(nodeArtistV8, Nan::New<String>("tophitTracks").ToLocalChecked(), getTophitTracks);
    Nan::SetAccessor(nodeArtistV8, Nan::New<String>("albums").ToLocalChecked(), getAlbums);
    Nan::SetAccessor(nodeArtistV8, Nan::New<String>("similarArtists").ToLocalChecked(), getSimilarArtists);
    Nan::SetAccessor(nodeArtistV8, Nan::New<String>("biography").ToLocalChecked(), getBiography);
    //TODO: portraits

    nodeArtist->artist->browse(artistbrowseType);
  } else {
    nodeArtist->callBrowseComplete();
  }
  info.GetReturnValue().SetUndefined();
}

NAN_GETTER(NodeArtist::getTracks) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  std::vector<std::shared_ptr<Track>> tracks = nodeArtist->artist->tracks();
  Local<Array> nodeTracks = Nan::New<Array>(tracks.size());
  for(int i = 0; i < (int)tracks.size(); i++) {
    NodeTrack* nodeTrack = new NodeTrack(tracks[i]);
    nodeTracks->Set(Nan::New<Number>(i), nodeTrack->createInstance());
  }
  info.GetReturnValue().Set(nodeTracks);
}

NAN_GETTER(NodeArtist::getTophitTracks) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  std::vector<std::shared_ptr<Track>> tophitTracks = nodeArtist->artist->tophitTracks();
  Local<Array> nodeTophitTracks = Nan::New<Array>(tophitTracks.size());
  for(int i = 0; i < (int)tophitTracks.size(); i++) {
    NodeTrack* nodeTrack = new NodeTrack(tophitTracks[i]);
    nodeTophitTracks->Set(Nan::New<Number>(i), nodeTrack->createInstance());
  }
  info.GetReturnValue().Set(nodeTophitTracks);
}

NAN_GETTER(NodeArtist::getAlbums) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  std::vector<std::unique_ptr<Album>> albums = nodeArtist->artist->albums();
  Local<Array> nodeAlbums = Nan::New<Array>(albums.size());
  for(int i = 0; i < (int)albums.size(); i++) {
    NodeAlbum* nodeAlbum = new NodeAlbum(std::move(albums[i]));
    nodeAlbums->Set(Nan::New<Number>(i), nodeAlbum->createInstance());
  }
  info.GetReturnValue().Set(nodeAlbums);
}

NAN_GETTER(NodeArtist::getSimilarArtists) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  std::vector<std::unique_ptr<Artist>> similarArtists = nodeArtist->artist->similarArtists();
  Local<Array> nodeSimilarArtists = Nan::New<Array>(similarArtists.size());
  for(int i = 0; i < (int)similarArtists.size(); i++) {
    NodeArtist* nodeArtist = new NodeArtist(std::move(similarArtists[i]));
    nodeSimilarArtists->Set(Nan::New<Number>(i), nodeArtist->createInstance());
  }
  info.GetReturnValue().Set(nodeSimilarArtists);
}

NAN_GETTER(NodeArtist::getBiography) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  std::string biography = nodeArtist->artist->biography();
  info.GetReturnValue().Set(Nan::New<String>(biography.c_str()).ToLocalChecked());
}

NAN_GETTER(NodeArtist::isLoaded) {
  NodeArtist* nodeArtist = Nan::ObjectWrap::Unwrap<NodeArtist>(info.This());
  info.GetReturnValue().Set(Nan::New<Boolean>(nodeArtist->artist->isLoaded()));
}

void NodeArtist::init() {
  Local<FunctionTemplate> constructorTemplate = NodeWrapped::init("Artist");
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("name").ToLocalChecked(), getName);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("link").ToLocalChecked(), getLink);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("isLoaded").ToLocalChecked(), isLoaded);
  Nan::SetPrototypeMethod(constructorTemplate, "browse", browse);
  NodeArtist::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>src/audio/AudioHandler.h<|end_filename|>
#ifndef AUDIOHANDLER_H
#define AUDIOHANDLER_H

extern "C" {
  #include "audio.h"
}

#include <libspotify/api.h>

/**
 * @brief The AudioHandler class
 * Abstract base class for handling audio in node-spotify. Provides the basic music delivery callback
 * that pushes music data into a queue and a audio buffer stats callback.
 */
class AudioHandler {
friend class Player; // to set current second and frames received on stop
friend class Spotify; // access the callback methods
public:
  AudioHandler();
  virtual ~AudioHandler();
  virtual void setStopped(bool stopped);
protected:
  audio_fifo_t audioFifo;
  int framesReceived;
  int currentSecond;
  /**
   * @brief afterMusicDelivery Method that will be called after audio data has been written into the queue.
   * @param format audio format delivered together with the raw music data by libspotify.
   */
  virtual void afterMusicDelivery(const sp_audioformat* format) = 0;
  /**
   * @brief dataNeeded Method that will be called before anything happens to the audio data. If it returns false,
   * afterMusicDelivery will return 0 and thus notify libspotify it hasn't consumed any frames.
   * @return
   */
  virtual bool dataNeeded() = 0;
private:
  /**
   * @brief AudioHandler::musicDelivery Callack for libspotify. Write the music data into the queue and call the abstract afterMusic
   * delivery method.
   * @param session libspotify session
   * @param format audio format
   * @param frames raw pcm data
   * @param num_frames number of frames in the pcm data
   * @return The number of frames consumed.
   */
  static int musicDelivery(sp_session* session, const sp_audioformat* format, const void* frames, int num_frames);
  static void getAudioBufferStats(sp_session* session, sp_audio_buffer_stats* stats);
};

#endif // AUDIOHANDLER_H


<|start_filename|>src/objects/node/NodeUser.cc<|end_filename|>
#include "NodeUser.h"
#include "NodePlaylistContainer.h"
#include "NodePlaylist.h"

NodeUser::NodeUser(std::unique_ptr<User> _user) : user(std::move(_user)) {}

NodeUser::~NodeUser() {}

NAN_GETTER(NodeUser::getLink) {
  NodeUser* nodeUser = Nan::ObjectWrap::Unwrap<NodeUser>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeUser->user->link().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeUser::getCanonicalName) {
  NodeUser* nodeUser = Nan::ObjectWrap::Unwrap<NodeUser>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeUser->user->canonicalName().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeUser::getDisplayName) {
  NodeUser* nodeUser = Nan::ObjectWrap::Unwrap<NodeUser>(info.This());
  info.GetReturnValue().Set(Nan::New<String>(nodeUser->user->displayName().c_str()).ToLocalChecked());
}

NAN_GETTER(NodeUser::isLoaded) {
  NodeUser* nodeUser = Nan::ObjectWrap::Unwrap<NodeUser>(info.This());
  info.GetReturnValue().Set(Nan::New<Boolean>(nodeUser->user->isLoaded()));
}

NAN_GETTER(NodeUser::getPublishedPlaylistsContainer) {
  NodeUser* nodeUser = Nan::ObjectWrap::Unwrap<NodeUser>(info.This());
  auto playlistContainer = nodeUser->user->publishedPlaylists();
  NodePlaylistContainer* nodePlaylistContainer = new NodePlaylistContainer(playlistContainer);
  info.GetReturnValue().Set(nodePlaylistContainer->createInstance());
}

NAN_GETTER(NodeUser::getStarredPlaylist) {
  NodeUser* nodeUser = Nan::ObjectWrap::Unwrap<NodeUser>(info.This());
  auto playlist = nodeUser->user->starredPlaylist();
  NodePlaylist* nodePlaylist = new NodePlaylist(playlist);
  info.GetReturnValue().Set(nodePlaylist->createInstance());
}

void NodeUser::init() {
  Local<FunctionTemplate> constructorTemplate = NodeWrapped::init("User");
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("canonicalName").ToLocalChecked(), getCanonicalName);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("link").ToLocalChecked(), getLink);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("displayName").ToLocalChecked(), getDisplayName);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("isLoaded").ToLocalChecked(), isLoaded);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("playlistContainer").ToLocalChecked(), getPublishedPlaylistsContainer);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("starredPlaylist").ToLocalChecked(), getStarredPlaylist);
  NodeUser::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>src/objects/node/NodePlayer.cc<|end_filename|>
#include "NodePlayer.h"
#include "NodeTrack.h"
#include "../../callbacks/SessionCallbacks.h"
#include "../../exceptions.h"

NodePlayer::NodePlayer(std::shared_ptr<Player> _player) : player(_player) {}

NodePlayer::~NodePlayer() {

}

NodePlayer::NodePlayer(const NodePlayer& other) {

}

NAN_METHOD(NodePlayer::pause) {
  NodePlayer* nodePlayer = Nan::ObjectWrap::Unwrap<NodePlayer>(info.This());
  nodePlayer->player->pause();
  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodePlayer::stop) {
  NodePlayer* nodePlayer = Nan::ObjectWrap::Unwrap<NodePlayer>(info.This());
  nodePlayer->player->stop();
  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodePlayer::resume) {
  NodePlayer* nodePlayer = Nan::ObjectWrap::Unwrap<NodePlayer>(info.This());
  nodePlayer->player->resume();
  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodePlayer::play) {
  if(info.Length() < 1) {
    Nan::ThrowError("play needs a track as its first argument.");
    return;
  }
  NodePlayer* nodePlayer = Nan::ObjectWrap::Unwrap<NodePlayer>(info.This());
  NodeTrack* nodeTrack = Nan::ObjectWrap::Unwrap<NodeTrack>(info[0]->ToObject());
  try {
    nodePlayer->player->play(nodeTrack->track);
  } catch (const TrackNotPlayableException& e) {
    Nan::ThrowError("Track not playable");
    return;
  }
#ifndef NODE_SPOTIFY_NATIVE_SOUND
  catch (const NoAudioHandlerException& e) {
    Nan::ThrowError("No audio handler registered. Use spotify.useNodejsAudio().");
    return;
  }
#endif

  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodePlayer::seek) {
  if(info.Length() < 1 || !info[0]->IsNumber()) {
    Nan::ThrowError("seek needs an integer as its first argument.");
    return;
  }
  NodePlayer* nodePlayer = Nan::ObjectWrap::Unwrap<NodePlayer>(info.This());
  int second = info[0]->ToInteger()->Value();
  nodePlayer->player->seek(second);
  info.GetReturnValue().SetUndefined();
}

NAN_GETTER(NodePlayer::getCurrentSecond) {
  NodePlayer* nodePlayer = Nan::ObjectWrap::Unwrap<NodePlayer>(info.This());
  info.GetReturnValue().Set(Nan::New<Integer>(nodePlayer->player->currentSecond));
}

NAN_METHOD(NodePlayer::on) {
  if(info.Length() < 1 || !info[0]->IsObject()) {
    Nan::ThrowError("on needs an object as its first argument.");
    return;
  }
  Handle<Object> callbacks = info[0]->ToObject();
  Handle<String> endOfTrackKey = Nan::New<String>("endOfTrack").ToLocalChecked();
  if(callbacks->Has(endOfTrackKey)) {
    SessionCallbacks::endOfTrackCallback.SetFunction(callbacks->Get(endOfTrackKey).As<Function>());
  }
  info.GetReturnValue().SetUndefined();
}

NAN_METHOD(NodePlayer::off) {
  //SessionCallbacks::endOfTrackCallback = Nan::Callback();
  info.GetReturnValue().SetUndefined();
}

void NodePlayer::init() {
  Local<FunctionTemplate> constructorTemplate = Nan::New<FunctionTemplate>();
  constructorTemplate->SetClassName(Nan::New<String>("Player").ToLocalChecked());
  constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
  Nan::SetPrototypeMethod(constructorTemplate, "on", on);
  Nan::SetPrototypeMethod(constructorTemplate, "off", off);

  Nan::SetPrototypeMethod(constructorTemplate, "play", play);
  Nan::SetPrototypeMethod(constructorTemplate, "pause", pause);
  Nan::SetPrototypeMethod(constructorTemplate, "resume", resume);
  Nan::SetPrototypeMethod(constructorTemplate, "stop", stop);
  Nan::SetPrototypeMethod(constructorTemplate, "seek", seek);
  Nan::SetAccessor(constructorTemplate->InstanceTemplate(), Nan::New<String>("currentSecond").ToLocalChecked(), &getCurrentSecond);
  NodePlayer::constructorTemplate.Reset(constructorTemplate);
}


<|start_filename|>src/objects/spotify/Track.h<|end_filename|>
#ifndef _TRACK_H
#define _TRACK_H

#include "Artist.h"
#include "Album.h"

#include <libspotify/api.h>
#include <string>
#include <vector>
#include <memory>

class Album;
class Artist;

class Track {
friend class Player;
friend class Playlist;
friend class NodeTrack;
public:
  Track(sp_track* _track);
  Track(const Track& other) : track(other.track) {
      sp_track_add_ref(track);
    };
  virtual ~Track() {
    sp_track_release(track);
  };

  std::string name();
  std::string link();
  std::vector<std::unique_ptr<Artist>> artists();
  std::unique_ptr<Album> album();
  int duration();
  bool starred();
  void setStarred(bool starred);
  int popularity();
  bool isLoaded();
  int getAvailability();
private:
  sp_track* track;
};

#endif


<|start_filename|>src/objects/node/NodeUser.h<|end_filename|>
#ifndef _NODE_USER_H
#define _NODE_USER_H

#include "NodeWrapped.h"
#include "../spotify/User.h"

#include <nan.h>
#include <memory>

using namespace v8;

class NodeUser : public NodeWrapped<NodeUser> {
private:
  std::unique_ptr<User> user;
public:
  NodeUser(std::unique_ptr<User> user);
  ~NodeUser();
  static NAN_GETTER(getCanonicalName);
  static NAN_GETTER(getDisplayName);
  static NAN_GETTER(isLoaded);
  static NAN_GETTER(getLink);
  static NAN_GETTER(getPublishedPlaylistsContainer);
  static NAN_GETTER(getStarredPlaylist);
  static void init();
};

#endif


<|start_filename|>src/audio/AudioHandler.cc<|end_filename|>
#include "../Application.h"
#include "AudioHandler.h"
#include "../objects/spotify/Spotify.h"

#include <stdlib.h>

#ifdef OS_LINUX //For memcpy
#include <string.h>
#endif

extern Application* application;

AudioHandler::AudioHandler() {
  audio_init(&audioFifo);
}

AudioHandler::~AudioHandler() {
  audio_stop(&audioFifo);
}

int AudioHandler::musicDelivery(sp_session *session, const sp_audioformat *format, const void *frames, int num_frames) {
  if(num_frames == 0 || !application->audioHandler->dataNeeded()) {
    return 0;
  }

  audio_fifo_t* audioFifo = &application->audioHandler->audioFifo;
  uv_mutex_lock(&audioFifo->audioQueueMutex);

  //If there is more than one second worth of samples in the queue don't buffer more
  if(audioFifo->samplesInQueue > format->sample_rate) {
    uv_mutex_unlock(&audioFifo->audioQueueMutex);
    return 0;
  }

  size_t size = num_frames * sizeof(int16_t) * format->channels;
  audio_fifo_data_t* audioData = (audio_fifo_data_t*)malloc(sizeof(*audioData) + size);
  memcpy(audioData->samples, frames, size);

  audioData->numberOfSamples = num_frames;
  audioData->sampleRate = format->sample_rate;
  audioData->channels = format->channels;

  TAILQ_INSERT_TAIL(&audioFifo->queue, audioData, link);
  audioFifo->samplesInQueue += num_frames;

  application->audioHandler->framesReceived += num_frames;
  if( application->audioHandler->framesReceived / audioData->sampleRate > 0) {
    application->audioHandler->currentSecond++;
    application->audioHandler->framesReceived = application->audioHandler->framesReceived - audioData->sampleRate;
    application->player->setCurrentSecond(application->audioHandler->currentSecond);
  }

  application->audioHandler->afterMusicDelivery(format);

  uv_mutex_unlock(&audioFifo->audioQueueMutex);

  return num_frames;
}

void AudioHandler::getAudioBufferStats(sp_session* session, sp_audio_buffer_stats* stats) {
  //TODO:this crashes when switching the audio handler in between playback because the mutex gets destroyed.
  audio_fifo_t *audioFifo = &application->audioHandler->audioFifo;
  uv_mutex_lock(&audioFifo->audioQueueMutex);
  stats->samples = audioFifo->samplesInQueue;
  stats->stutter = 0;
  uv_mutex_unlock(&audioFifo->audioQueueMutex);
}

void AudioHandler::setStopped(bool stopped) {
  if(stopped == false) {
    audio_fifo_flush(&audioFifo);
  }
}


<|start_filename|>test/bug-101.js<|end_filename|>
var stream = require('stream');
var ss = new stream.PassThrough();

var baseTest = require('./basetest.js');
var spotify = baseTest.spotify;
spotify.useNodejsAudio(function(err, buffer) {
  if(err) throw err;
  ss.write(buffer);
  return true;
});

baseTest.executeTest(tests);

function tests() {
  var track = spotify.sessionUser.starredPlaylist.getTrack(0);
  console.log('playing track ' +  track.name + ' for 2 seconds');
  spotify.player.play(track);

  setTimeout(function() {
    console.log('logging out');
    spotify.logout(function () {
      process.exit();
    });
  }, 1000);
}
