libechonest-2.3.1/cmake/FindLibEchonest.cmake000644 001750 001750 00000001766 12465467260 021726 0ustar00stefanstefan000000 000000 # - Try to find libechonest # # ECHONEST_FOUND - system has libechonest # ECHONEST_INCLUDE_DIRS - the libechonest include directories # ECHONEST_LIBRARIES - link these to use libechonest # # (c) Dominik Schmidt # # Dependencies find_package(Qt4 4.6.0 COMPONENTS QtCore QtNetwork REQUIRED) # Include dir find_path(ECHONEST_INCLUDE_DIR NAMES echonest/echonest_export.h PATHS ${KDE4_INCLUDE_DIR} ) # Finally the library itself find_library(ECHONEST_LIBRARY NAMES echonest PATHS ${KDE4_LIB_DIR} ) if(ECHONEST_INCLUDE_DIR AND ECHONEST_LIBRARY) set(ECHONEST_FOUND TRUE) message(STATUS "Found libechonest: ${ECHONEST_INCLUDE_DIR}, ${ECHONEST_LIBRARY}") else(ECHONEST_INCLUDE_DIR AND ECHONEST_LIBRARY) set(ECHONEST_FOUND FALSE) if (ECHONEST_FIND_REQUIRED) message(FATAL_ERROR "Could NOT find required package LibEchonest") endif(ECHONEST_FIND_REQUIRED) endif(ECHONEST_INCLUDE_DIR AND ECHONEST_LIBRARY) mark_as_advanced(ECHONEST_INCLUDE_DIR ECHONEST_LIBRARY) libechonest-2.3.1/src/CatalogUpdateEntry_p.h000644 001750 001750 00000006133 12465467260 021653 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef CATALOG_UPDATE_ENTRY_H #define CATALOG_UPDATE_ENTRY_H #include "Util.h" #include #include #include class CatalogUpdateEntryData : public QSharedData { public: CatalogUpdateEntryData() : action( Echonest::CatalogTypes::Update ), track_number( -1 ), disc_number( -1 ), favorite( false ), banned( false ), play_count( -1 ), skip_count( -1 ), rating( -1 ), favoriteSet( false ), bannedSet( false ) {} CatalogUpdateEntryData( const CatalogUpdateEntryData& other ) : QSharedData( other ) { item_id = other.item_id; action = other.action; fp_code = other.fp_code; song_id = other.song_id; song_name = other.song_name; artist_id = other.artist_id; artist_name = other.artist_name; release = other.release; genre = other.genre; track_number = other.track_number; disc_number = other.disc_number; url = other.url; favorite = other.favorite; banned = other.banned; play_count = other.play_count; skip_count = other.skip_count; rating = other.rating; favoriteSet = other.favoriteSet; bannedSet = other.banned; } Echonest::CatalogTypes::Action action; QByteArray item_id; QByteArray fp_code; QByteArray song_id; QString song_name; QByteArray artist_id; QString artist_name; QString release; QString genre; int track_number; int disc_number; QString url; bool favorite; bool banned; int play_count; int skip_count; int rating; // internal bool favoriteSet; bool bannedSet; }; #endif libechonest-2.3.1/TODO000644 001750 001750 00000000341 12465467260 015320 0ustar00stefanstefan000000 000000 TODO list ======== * Implement DynamicPlaylist::parseInfo * Add parsing of catalog ticket items. NOTE: I am unable to get any ticket items in my catalog/status calls, so skipping this for now * Implement Catalog::parseDelete libechonest-2.3.1/tests/SongTest.h000644 001750 001750 00000003273 12465467260 017720 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_SONG_TEST_H #define ECHONEST_SONG_TEST_H #include class SongTest : public QObject { Q_OBJECT private slots: void initTestCase(); void testSearch1(); void testSearch2(); void testSearch3(); void testProfile(); void testIdentify(); void testIdentifyWithData(); void testSearchSongType(); }; #endif libechonest-2.3.1/src/AudioSummary.cpp000644 001750 001750 00000017534 12465467260 020556 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "AudioSummary.h" #include "AudioSummary_p.h" #include "Config.h" #include "Parsing_p.h" #include Echonest::AudioSummary::AudioSummary() : d( new AudioSummaryData ) { } Echonest::AudioSummary::AudioSummary(const Echonest::AudioSummary& other) :d( other.d ) { } Echonest::AudioSummary::~AudioSummary() {} QDebug Echonest::operator<<(QDebug d, const Echonest::AudioSummary& summary) { // d << summary return d.maybeSpace(); } Echonest::AudioSummary& Echonest::AudioSummary::operator=(const Echonest::AudioSummary& audio) { d = audio.d; return *this; } int Echonest::AudioSummary::analysisStatus() const { return d->status; } void Echonest::AudioSummary::setAnalysisStatus(int status) { d->status = status; } qreal Echonest::AudioSummary::analysisTime() const { return d->analysis_time; } void Echonest::AudioSummary::setAnalysisTime(qreal time) { d->analysis_time = time; } QString Echonest::AudioSummary::analyzerVersion() const { return d->analyzer_version; } void Echonest::AudioSummary::setAnalyzerVersion(QString version) { d->analyzer_version = version; } Echonest::BarList Echonest::AudioSummary::bars() const { return d->bars; } void Echonest::AudioSummary::setBars(const Echonest::BarList& bars) { d->bars = bars; } Echonest::BeatList Echonest::AudioSummary::beats() const { return d->beats; } void Echonest::AudioSummary::setBeats(const Echonest::BeatList& beats) { d->beats = beats; } QString Echonest::AudioSummary::detailedStatus() const { return d->detailed_status; } void Echonest::AudioSummary::setDetailedStatus( const QString& status ) { d->detailed_status = status; } qreal Echonest::AudioSummary::duration() const { return d->duration; } void Echonest::AudioSummary::setDuration(qreal duration) { d->duration = duration; } qreal Echonest::AudioSummary::endOfFadeIn() const { return d->end_of_fade_in; } void Echonest::AudioSummary::setEndOfFadeIn(qreal time) { d->end_of_fade_in = time; } QNetworkReply* Echonest::AudioSummary::fetchFullAnalysis() const { return Echonest::Config::instance()->nam()->get( QNetworkRequest( QUrl( d->analysis_url ) ) ); } int Echonest::AudioSummary::key() const { return d->key; } void Echonest::AudioSummary::setKey(int key) { d->key = key; } qreal Echonest::AudioSummary::keyConfidence() const { return d->key_confidence; } void Echonest::AudioSummary::setKeyConfidence(qreal confidence) { d->key_confidence = confidence; } qreal Echonest::AudioSummary::loudness() const { return d->loudness; } void Echonest::AudioSummary::setLoudness(qreal loudness) { d->loudness = loudness; } qreal Echonest::AudioSummary::modeConfidence() const { return d->mode_confidence; } void Echonest::AudioSummary::setModeConfidence(qreal confidence) { d->mode_confidence = confidence; } qint64 Echonest::AudioSummary::numSamples() const { return d->num_samples; } void Echonest::AudioSummary::setNumSamples(qint64 num) { d->num_samples = num; } void Echonest::AudioSummary::parseFullAnalysis( QNetworkReply* reply ) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); Echonest::Parser::parseDetailedAudioSummary( reply, *this ); reply->deleteLater(); } QString Echonest::AudioSummary::sampleMD5() const { return d->sample_md5; } void Echonest::AudioSummary::setSampleMD5(const QString& md5) { d->sample_md5 = md5; } qreal Echonest::AudioSummary::sampleRate() const { return d->samplerate; } void Echonest::AudioSummary::setSampleRate(qreal sampleRate) { d->samplerate = sampleRate; } Echonest::SectionList Echonest::AudioSummary::sections() const { return d->sections; } void Echonest::AudioSummary::setSections(const Echonest::SectionList& sections) { d->sections = sections; } Echonest::SegmentList Echonest::AudioSummary::segments() const { return d->segments; } void Echonest::AudioSummary::setSegments(const Echonest::SegmentList& segments) { d->segments = segments; } void Echonest::AudioSummary::setStartOfFadeOut(qreal time) { d->start_of_fade_out = time; } Echonest::TatumList Echonest::AudioSummary::tatums() const { return d->tatums; } void Echonest::AudioSummary::setTatums(const Echonest::TatumList& tatums) { d->tatums = tatums; } qreal Echonest::AudioSummary::startOfFadeOut() const { return d->start_of_fade_out; } qreal Echonest::AudioSummary::tempo() const { return d->tempo; } void Echonest::AudioSummary::setTempo(qreal tempo) { d->tempo = tempo; } qreal Echonest::AudioSummary::tempoConfidence() const { return d->tempo_confidence; } void Echonest::AudioSummary::setTempoConfidence(qreal confidence) { d->tempo_confidence = confidence; } int Echonest::AudioSummary::timeSignature() const { return d->time_signature; } void Echonest::AudioSummary::setTimeSignature(int timeSignature) { d->time_signature = timeSignature; } qreal Echonest::AudioSummary::timeSignatureConfidence() const { return d->time_signature_confidence; } void Echonest::AudioSummary::setTimeSignatureConfidence(qreal confidence) { d->time_signature_confidence = confidence; } void Echonest::AudioSummary::setTimestamp(qreal timestamp) { d->timestamp = timestamp; } qreal Echonest::AudioSummary::timestamp() const { return d->timestamp; } int Echonest::AudioSummary::mode() const { return d->mode; } void Echonest::AudioSummary::setAnalysisUrl(const QUrl& analysisUrl) { d->analysis_url = analysisUrl; } void Echonest::AudioSummary::setMode(int mode) { d->mode = mode; } qreal Echonest::AudioSummary::danceability() const { return d->danceability; } void Echonest::AudioSummary::setDanceability(qreal dance) { d->danceability = dance; } qreal Echonest::AudioSummary::energy() const { return d->energy; } void Echonest::AudioSummary::setEnergy(qreal energy) { d->energy = energy; } qreal Echonest::AudioSummary::acousticness() const { return d->acousticness; } void Echonest::AudioSummary::setAcousticness(qreal acousticness) { d->acousticness = acousticness; } qreal Echonest::AudioSummary::speechiness() const { return d->speechiness; } void Echonest::AudioSummary::setSpeechiness(qreal speechiness) { d->speechiness = speechiness; } qreal Echonest::AudioSummary::liveness() const { return d->liveness; } void Echonest::AudioSummary::setLiveness(qreal liveness) { d->liveness = liveness; } qreal Echonest::AudioSummary::valence() const { return d->valence; } void Echonest::AudioSummary::setValence(qreal valence) { d->valence = valence; } libechonest-2.3.1/src/Playlist.cpp000644 001750 001750 00000057671 12465467260 017746 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Playlist.h" #include "Playlist_p.h" #include "Parsing_p.h" #include Echonest::DynamicPlaylist::DynamicPlaylist() : d( new DynamicPlaylistData ) { } Echonest::DynamicPlaylist::DynamicPlaylist(const Echonest::DynamicPlaylist& other) : d( other.d ) { } Echonest::DynamicPlaylist::~DynamicPlaylist() { } Echonest::DynamicPlaylist& Echonest::DynamicPlaylist::operator=(const Echonest::DynamicPlaylist& playlist) { d = playlist.d; return *this; } QNetworkReply* Echonest::DynamicPlaylist::create(const Echonest::DynamicPlaylist::PlaylistParams& params) const { // params are the same, if user passes in format parsing will throw, but it should be expected.. return generateInternal( params, "dynamic/create" ); } void Echonest::DynamicPlaylist::parseCreate(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QByteArray data = reply->readAll(); // qDebug() << data; QXmlStreamReader xml( data ); Echonest::Parser::readStatus( xml ); d->sessionId = Echonest::Parser::parsePlaylistSessionId( xml ); Q_ASSERT( !d->sessionId.isEmpty() ); } QNetworkReply* Echonest::DynamicPlaylist::restart(const Echonest::DynamicPlaylist::PlaylistParams& params) const { return generateInternal( params, "dynamic/restart" ); } QByteArray Echonest::DynamicPlaylist::sessionId() const { return d->sessionId; } void Echonest::DynamicPlaylist::setSessionId(const QByteArray& id) { d->sessionId = id; } Echonest::Song Echonest::DynamicPlaylist::currentSong() const { return d->currentSong; } void Echonest::DynamicPlaylist::setCurrentSong(const Echonest::Song& song) { d->currentSong = song; } QNetworkReply* Echonest::DynamicPlaylist::next( int results, int lookahead ) const { QUrl url = Echonest::baseGetQuery( "playlist/dynamic", "next" ); urlAddQueryItem( url, QLatin1String( "session_id" ), QString::fromLatin1( d->sessionId ) ); urlAddQueryItem( url, QLatin1String( "results" ), QString::number( results ) ); urlAddQueryItem( url, QLatin1String( "lookahead" ), QString::number( lookahead ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::DynamicPlaylist::fetchInfo() const { QUrl url = Echonest::baseGetQuery( "playlist/dynamic", "info" ); urlAddQueryItem( url, QLatin1String( "session_id" ), QString::fromLatin1( d->sessionId ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } Echonest::DynamicPlaylist::FetchPair Echonest::DynamicPlaylist::parseNext(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); // const QByteArray data = reply->readAll(); // qDebug() << data; QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); Echonest::SongList lookahead = Echonest::Parser::parseDynamicLookahead( xml ); Echonest::SongList results = Echonest::Parser::parseSongList( xml ); reply->deleteLater(); return qMakePair(results, lookahead); } QNetworkReply* Echonest::DynamicPlaylist::feedback(const Echonest::DynamicPlaylist::DynamicFeedback& feedback) const { QUrl url = Echonest::baseGetQuery( "playlist/dynamic", "feedback" ); urlAddQueryItem( url, QLatin1String( "session_id" ), QString::fromLatin1( d->sessionId ) ); foreach( const Echonest::DynamicPlaylist::DynamicFeedbackParamData& param, feedback ) { urlAddQueryItem( url, QString::fromLatin1( dynamicFeedbackToString(param.first) ), QString::fromLatin1( param.second ) ); } return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } void Echonest::DynamicPlaylist::parseFeedback(QNetworkReply* reply) const throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); reply->deleteLater(); } QNetworkReply* Echonest::DynamicPlaylist::steer(const Echonest::DynamicPlaylist::PlaylistParams& steerParams) const { QUrl url = Echonest::baseGetQuery( "playlist/dynamic", "steer" ); urlAddQueryItem( url, QLatin1String( "session_id" ), QString::fromLatin1( d->sessionId ) ); foreach( const Echonest::DynamicPlaylist::PlaylistParamData& param, steerParams ) { // HACK ARG min/max functions for steering are min_foo_bar, but params for static/initial seeds are foo_min_bar. can't reuse :( QByteArray str; switch ( param.first ) { case Echonest::DynamicPlaylist::ArtistMinFamiliarity: str = "min_artist_familiarity"; break; case Echonest::DynamicPlaylist::ArtistMaxFamiliarity: str = "max_artist_familiarity"; break; case Echonest::DynamicPlaylist::ArtistMinHotttnesss: str = "min_artist_hotttnesss"; break; case Echonest::DynamicPlaylist::ArtistMaxHotttnesss: str = "max_artist_hotttnesss"; break; case Echonest::DynamicPlaylist::SongMinHotttnesss: str = "min_song_hotttnesss"; break; case Echonest::DynamicPlaylist::SongMaxHotttnesss: str = "max_song_hotttnesss"; break; case Echonest::DynamicPlaylist::MinEnergy: str = "min_energy"; break; case Echonest::DynamicPlaylist::MaxEnergy: str = "max_energy"; break; case Echonest::DynamicPlaylist::MinDanceability: str = "min_danceability"; break; case Echonest::DynamicPlaylist::MaxDanceability: str = "max_danceability"; break; case Echonest::DynamicPlaylist::MinLoudness: str = "min_loudness"; break; case Echonest::DynamicPlaylist::MaxLoudness: str = "max_loudness"; break; case Echonest::DynamicPlaylist::MinTempo: str = "min_tempo"; break; case Echonest::DynamicPlaylist::MaxTempo: str = "max_tempo"; break; default: str = playlistParamToString( param.first ); } urlAddQueryItem( url, QString::fromLatin1( str ), param.second.toString()); } return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } void Echonest::DynamicPlaylist::parseSteer(QNetworkReply* reply) const throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); reply->deleteLater(); } Echonest::SessionInfo Echonest::DynamicPlaylist::parseInfo(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); reply->deleteLater(); return Echonest::Parser::parseSessionInfo( xml ); } QNetworkReply* Echonest::DynamicPlaylist::deleteSession() const { QUrl url = Echonest::baseGetQuery( "playlist/dynamic", "delete" ); urlAddQueryItem( url, QLatin1String( "session_id" ), QString::fromLatin1( d->sessionId ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } void Echonest::DynamicPlaylist::parseDeleteSession(QNetworkReply* reply) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); d->sessionId.clear(); reply->deleteLater(); } QNetworkReply* Echonest::DynamicPlaylist::staticPlaylist(const Echonest::DynamicPlaylist::PlaylistParams& params) { return Echonest::DynamicPlaylist::generateInternal( params, "static" ); } Echonest::SongList Echonest::DynamicPlaylist::parseStaticPlaylist(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); Echonest::SongList songs = Echonest::Parser::parseSongList( xml ); reply->deleteLater(); return songs; } QByteArray Echonest::DynamicPlaylist::parseXSPFPlaylist(QNetworkReply* reply) throw( Echonest::ParseError ) { QByteArray data = reply->readAll(); Echonest::Parser::checkForErrors( reply ); reply->deleteLater(); return data; } QNetworkReply* Echonest::DynamicPlaylist::generateInternal(const Echonest::DynamicPlaylist::PlaylistParams& params, const QByteArray& type) { QUrl url = Echonest::baseGetQuery( "playlist", type ); Echonest::DynamicPlaylist::PlaylistParams::const_iterator iter = params.constBegin(); for( ; iter < params.constEnd(); ++iter ) { if( iter->first == Format ) // If it's a format, we have to remove the xml format we automatically specify urlRemoveQueryItem( url, QString::fromLatin1( "format" ) ); if( iter->first == Type ) { // convert type enum to string switch( static_cast( iter->second.toInt() ) ) { case ArtistType: urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QLatin1String( "artist" ) ); break; case ArtistRadioType: urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QLatin1String( "artist-radio" ) ); break; case ArtistDescriptionType: urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QLatin1String( "artist-description" ) ); break; case CatalogType: urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QLatin1String( "catalog" ) ); break; case CatalogRadioType: urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QLatin1String( "catalog-radio" ) ); break; case SongRadioType: urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QLatin1String( "song-radio" ) ); break; case GenreRadioType: urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QLatin1String( "genre-radio" ) ); break; } } else if( iter->first == Sort ) { urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QString::fromLatin1( playlistSortToString( static_cast( iter->second.toInt() ) ) ) ); } else if( iter->first == Pick ) { urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QString::fromLatin1( playlistArtistPickToString( static_cast( iter->second.toInt() ) ) ) ); } else if( iter->first == SongInformation ) { Echonest::Song::addQueryInformation( url, Echonest::SongInformation( iter->second.value< Echonest::SongInformation >() ) ); } else if ( iter->first == GenrePreset ) { urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QString::fromLatin1( playlistGenrePresetToString( static_cast( iter->second.toInt() ) ) ) ); } else { urlAddQueryItem( url, QString::fromLatin1( playlistParamToString( iter->first ) ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( iter->second.toString() ) ) ); } } return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QByteArray Echonest::DynamicPlaylist::playlistParamToString(Echonest::DynamicPlaylist::PlaylistParam param) { switch( param ) { case Echonest::DynamicPlaylist::Type : return "type"; case Echonest::DynamicPlaylist::Format : return "format"; case Echonest::DynamicPlaylist::Pick: return "artist_pick"; case Echonest::DynamicPlaylist::Variety : return "variety"; case Echonest::DynamicPlaylist::ArtistId : return "artist_id"; case Echonest::DynamicPlaylist::Artist : return "artist"; case Echonest::DynamicPlaylist::ArtistSeedCatalog : return "artist_seed_catalog"; case Echonest::DynamicPlaylist::SourceCatalog : return "seed_catalog"; case Echonest::DynamicPlaylist::SongId : return "song_id"; case Echonest::DynamicPlaylist::Description : return "description"; case Echonest::DynamicPlaylist::Results : return "results"; case Echonest::DynamicPlaylist::MaxTempo : return "max_tempo"; case Echonest::DynamicPlaylist::MinTempo : return "min_tempo"; case Echonest::DynamicPlaylist::MaxDuration : return "max_duration"; case Echonest::DynamicPlaylist::MinDuration : return "min_duration"; case Echonest::DynamicPlaylist::MaxLoudness : return "max_loudness"; case Echonest::DynamicPlaylist::MinLoudness : return "min_loudness"; case Echonest::DynamicPlaylist::ArtistMaxFamiliarity : return "artist_max_familiarity"; case Echonest::DynamicPlaylist::ArtistMinFamiliarity : return "artist_min_familiarity"; case Echonest::DynamicPlaylist::MinDanceability : return "min_danceability"; case Echonest::DynamicPlaylist::MaxDanceability : return "max_danceability"; case Echonest::DynamicPlaylist::MinEnergy : return "min_energy"; case Echonest::DynamicPlaylist::MaxEnergy : return "max_energy"; case Echonest::DynamicPlaylist::ArtistMaxHotttnesss : return "artist_max_hotttnesss"; case Echonest::DynamicPlaylist::ArtistMinHotttnesss : return "artist_min_hotttnesss"; case Echonest::DynamicPlaylist::SongMaxHotttnesss : return "song_max_hotttnesss"; case Echonest::DynamicPlaylist::SongMinHotttnesss : return "song_min_hotttnesss"; case Echonest::DynamicPlaylist::ArtistMinLongitude : return "min_longitude"; case Echonest::DynamicPlaylist::ArtistMaxLongitude : return "max_longitude"; case Echonest::DynamicPlaylist::ArtistMinLatitude : return "min_latitude"; case Echonest::DynamicPlaylist::ArtistMaxLatitude : return "max_latitude"; case Echonest::DynamicPlaylist::Mode : return "mode"; case Echonest::DynamicPlaylist::Key : return "key"; case Echonest::DynamicPlaylist::SongInformation: return "bucket"; case Echonest::DynamicPlaylist::Sort : return "sort"; case Echonest::DynamicPlaylist::Limit : return "limit"; case Echonest::DynamicPlaylist::Audio : return "audio"; case Echonest::DynamicPlaylist::DMCA : return "dmca"; case Echonest::DynamicPlaylist::ChainXSPF : return "chain_xspf"; case Echonest::DynamicPlaylist::Mood : return "mood"; case Echonest::DynamicPlaylist::Style : return "style"; case Echonest::DynamicPlaylist::Adventurousness : return "adventurousness"; case Echonest::DynamicPlaylist::MoreLikeThis : return "more_like_this"; case Echonest::DynamicPlaylist::LessLikeThis : return "less_like_this"; case Echonest::DynamicPlaylist::TargetTempo : return "target_tempo"; case Echonest::DynamicPlaylist::TargetDanceability : return "target_danceability"; case Echonest::DynamicPlaylist::TargetEnergy : return "target_energy"; case Echonest::DynamicPlaylist::TargetLoudness : return "target_loudness"; case Echonest::DynamicPlaylist::TargetArtistFamiliarity : return "target_artist_familiarity"; case Echonest::DynamicPlaylist::TargetArtistHotttnesss : return "target_artist_hotttnesss"; case Echonest::DynamicPlaylist::TargetSongHotttnesss : return "target_song_hotttnesss"; case Echonest::DynamicPlaylist::SongType : return "song_type"; case Echonest::DynamicPlaylist::Genre : return "genre"; case Echonest::DynamicPlaylist::ArtistStartYearBefore: return "artist_start_year_before"; case Echonest::DynamicPlaylist::ArtistStartYearAfter: return "artist_start_year_after"; case Echonest::DynamicPlaylist::ArtistEndYearBefore: return "artist_end_year_before"; case Echonest::DynamicPlaylist::ArtistEndYearAfter: return "artist_end_year_after"; case Echonest::DynamicPlaylist::MaxAcousticness: return "max_accousticness"; case Echonest::DynamicPlaylist::MinAcousticness: return "min_accousticness"; case Echonest::DynamicPlaylist::MaxSpeechiness: return "max_speechiness"; case Echonest::DynamicPlaylist::MinSpeechiness: return "min_speechiness"; case Echonest::DynamicPlaylist::MaxLiveness: return "max_liveness"; case Echonest::DynamicPlaylist::MinLiveness: return "min_liveness"; case Echonest::DynamicPlaylist::MaxValence: return "max_valence"; case Echonest::DynamicPlaylist::MinValence: return "min_valence"; case Echonest::DynamicPlaylist::Distribution: return "distribution"; case Echonest::DynamicPlaylist::GenrePreset: return "genre_preset"; } return QByteArray(); } QByteArray Echonest::DynamicPlaylist::playlistArtistPickToString(Echonest::DynamicPlaylist::ArtistPick pick) { switch( pick ) { case PickSongHotttnesssAscending: return "song_hotttnesss-asc"; case PickTempoAscending: return "tempo-asc"; case PickDurationAscending: return "duration-asc"; case PickLoudnessAscending: return "loudness-asc"; case PickModeAscending: return "mode-asc"; case PickKeyAscending: return "key-asc"; case PickSongHotttnesssDescending: return "song_hotttnesss-desc"; case PickTempoDescending: return "tempo-desc"; case PickDurationDescending: return "duration-desc"; case PickLoudnessDescending: return "loudness-desc"; case PickModeDescending: return "mode-desc"; case PickKeyDescending: return "key-desc"; } return QByteArray(); } QByteArray Echonest::DynamicPlaylist::playlistSortToString(Echonest::DynamicPlaylist::SortingType sorting) { switch( sorting ) { case SortTempoAscending: return "tempo-asc"; case SortTempoDescending: return "tempo-desc"; case SortDurationAscending: return "duration-asc"; case SortDurationDescending: return "duration-desc"; case SortLoudnessAscending: return "loudness-asc"; case SortLoudnessDescending: return "loudness-desc"; case SortArtistFamiliarityAscending: return "artist_familiarity-asc"; case SortArtistFamiliarityDescending: return "artist_familiarity-desc"; case SortArtistHotttnessAscending: return "artist_hotttnesss-asc"; case SortArtistHotttnessDescending: return "artist_hotttnesss-desc"; case SortSongHotttnesssAscending: return "song_hotttnesss-asc"; case SortSongHotttnesssDescending: return "song_hotttnesss-desc"; case SortLatitudeAscending: return "latitude-asc"; case SortLatitudeDescending: return "latitude-desc"; case SortLongitudeAscending: return "longitude-asc"; case SortLongitudeDescending: return "longitude-desc"; case SortModeAscending: return "mode-asc"; case SortModeDescending: return "mode-desc"; case SortKeyAscending: return "key-asc"; case SortKeyDescending: return "key-desc"; case SortEnergyAscending: return "energy-asc"; case SortEnergyDescending: return "energy-desc"; case SortDanceabilityAscending: return "danceability-asc"; case SortDanceabilityDescending: return "danceability-desc"; case SortAcousticnessAscending: return "acousticness-asc"; case SortAcousticnessDescending: return "acousticness-desc"; case SortSpeechinessAscending: return "speechiness-asc"; case SortSpeechinessDescending: return "speechiness-desc"; case SortLivenessAscending: return "liveness-asc"; case SortLivenessDescending: return "liveness-desc"; case SortValenceAscending: return "valence-asc"; case SortValenceDescending: return "valence-desc"; } return QByteArray(); } QByteArray Echonest::DynamicPlaylist::dynamicFeedbackToString(Echonest::DynamicPlaylist::DynamicFeedbackParam param) { switch( param ) { case BanArtist: return "ban_artist"; case FavoriteArtist: return "favorite_artist"; case BanSong: return "ban_song"; case FavoriteSong: return "favorite_song"; case SkipSong: return "skip_song"; case PlaySong: return "play_song"; case UnplaySong: return "unplay_song"; case RateSong: return "rate_song"; default: Q_ASSERT(false); return ""; } } QByteArray Echonest::DynamicPlaylist::playlistGenrePresetToString(Echonest::DynamicPlaylist::GenrePresetParam param) { switch( param ) { case CoreBest: return "core-best"; case CoreShuffled: return "core-shuffled"; case EmergingBest: return "emerging-best"; case EmerginShuffled: return "emerging-shuffled"; default: Q_ASSERT(false); return ""; } } QDebug Echonest::operator<<(QDebug d, const Echonest::DynamicPlaylist& playlist) { d << QString::fromLatin1( "DynamicPlaylist(%1, %2)" ).arg( QLatin1String( playlist.sessionId() ), playlist.currentSong().toString() ); return d.maybeSpace(); } libechonest-2.3.1/src/Parsing.cpp000644 001750 001750 00000176073 12465467260 017546 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Parsing_p.h" #include "Artist.h" #include "CatalogItem_p.h" #include "Util.h" #include "Genre.h" #include "Catalog.h" #include "CatalogArtist.h" #include "qjsonwrapper/Json.h" #include #include #include void Echonest::Parser::checkForErrors( QNetworkReply* reply ) throw( Echonest::ParseError ) { if( !reply ) throw ParseError( Echonest::UnknownError ); // TODO sometimes this returns false when it shouldn't be? what's going on.. // if( !reply->isFinished() ) // throw ParseError( Echonest::UnfinishedQuery ); // if( reply->error() != QNetworkReply::NoError && reply->error() != QNetworkReply::UnknownContentError ) { // let UnknownContentError through so we parse it in readStatus with the proper error message qDebug() << reply->errorString(); ParseError err( Echonest::NetworkError ); err.setNetworkError( reply->error() ); throw err; } } void Echonest::Parser::readStatus( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.readNextStartElement() ) { // sanity checks if( xml.atEnd() || xml.name() != QLatin1String( "response" ) ) throw ParseError( UnknownParseError ); if( xml.readNextStartElement() ) { if( xml.atEnd() || xml.name() != QLatin1String( "status" ) ) throw ParseError( UnknownParseError ); xml.readNextStartElement(); double version = xml.readElementText().toDouble(); // TODO use version for something? Q_UNUSED(version); xml.readNextStartElement(); Echonest::ErrorType code = static_cast< Echonest::ErrorType >( xml.readElementText().toInt() ); xml.readNextStartElement(); QString msg = xml.readElementText(); xml.readNextStartElement(); if( code != Echonest::NoError ) { qDebug() << "Parse Error:" << code << msg; throw ParseError( code, msg ); } xml.readNext(); } } else { throw ParseError( UnknownParseError ); } } QVector< Echonest::Song > Echonest::Parser::parseSongList( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { QVector< Echonest::Song > songs; xml.readNext(); while( !( xml.name() == QLatin1String( "songs" ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { // parse a song songs.append( parseSong( xml ) ); } return songs; } Echonest::Song Echonest::Parser::parseSong( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "song" ) ) throw ParseError( Echonest::UnknownParseError ); Echonest::Song song; while( !( xml.name() == QLatin1String( "song" ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { if( xml.name() == QLatin1String( "id" ) && xml.isStartElement() ) song.setId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "title" ) && xml.isStartElement() ) song.setTitle( xml.readElementText() ); else if( xml.name() == QLatin1String( "artist_id" ) && xml.isStartElement() ) song.setArtistId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "artist_name" ) && xml.isStartElement() ) song.setArtistName( xml.readElementText() ); else if( xml.name() == QLatin1String( "release" ) && xml.isStartElement() ) song.setRelease( xml.readElementText() ); else if( xml.name() == QLatin1String( "song_hotttnesss" ) && xml.isStartElement() ) song.setHotttnesss( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "artist_hotttnesss" ) && xml.isStartElement() ) song.setArtistHotttnesss( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "artist_familiarity" ) && xml.isStartElement() ) song.setArtistFamiliarity( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "tracks" ) && xml.isStartElement() ) { song.setTracks( parseSongTrackBucket( xml ) ); } else if( xml.name() == QLatin1String( "artist_location" ) && xml.isStartElement() ) { song.setArtistLocation( parseSongArtistLocation( xml ) ); } else if( xml.name() == QLatin1String( "audio_summary" ) && xml.isStartElement() ) { song.setAudioSummary( parseAudioSummary( xml ) ); } else if( xml.name() == QLatin1String( "song_type" ) && xml.isStartElement() ) { song.addSongType( xml.readElementText() ); } xml.readNext(); } xml.readNext(); // skip past the last return song; } Echonest::ArtistLocation Echonest::Parser::parseSongArtistLocation( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "artist_location" ) ) { throw ParseError( Echonest::UnknownParseError ); } /** * while( !( xml.name() == "location" && xml.tokenType() == QXmlStreamReader::EndElement ) ) { x ml.readNex*tStartElement(); if( xml.name() == "location" ) song.setArtistLocation( xml.readElementText() ); } xml.readNext(); **/ Echonest::ArtistLocation location; while( !( xml.name() == QLatin1String( "artist_location" ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { if( xml.name() == QLatin1String( "latitude" ) && xml.isStartElement() ) { location.latitude = xml.readElementText().toDouble(); } else if( xml.name() == QLatin1String( "longitude" ) && xml.isStartElement() ) { location.longitude = xml.readElementText().toDouble(); } else if( xml.name() == QLatin1String( "location" ) && xml.isStartElement() ) { location.location = xml.readElementText(); } xml.readNext(); } return location; } Echonest::Tracks Echonest::Parser::parseSongTrackBucket( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "tracks" ) ) { throw ParseError( Echonest::UnknownParseError ); } Echonest::Tracks tracks; while( !( xml.name() == QLatin1String( "tracks" ) && xml.tokenType() == QXmlStreamReader::EndElement ) && ( xml.name() != QLatin1String( "track" ) || !xml.isEndElement() ) ) { if( xml.name() == QLatin1String( "track" ) && xml.isStartElement() ) { Echonest::Track track = parseTrack( xml ); tracks.append( track ); } else xml.readNext(); } return tracks; } Echonest::Tracks Echonest::Parser::parseCatalogSongTracks( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "tracks" ) ) { throw ParseError( Echonest::UnknownParseError ); } Echonest::Tracks tracks; while( !( xml.name() == QLatin1String( "tracks" ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { if( xml.name() == QLatin1String( "track" ) && xml.isStartElement() ) { tracks.append( Echonest::Track( xml.readElementText().toLatin1() ) ); } xml.readNext(); } return tracks; } Echonest::Track Echonest::Parser::parseTrack( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "track" ) ) { throw ParseError( Echonest::UnknownParseError ); } Echonest::Track track; while( !( xml.name() == QLatin1String( "track" ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { if( xml.name() == QLatin1String( "id" ) && xml.isStartElement() ) track.setId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "title" ) && xml.isStartElement() ) track.setTitle( xml.readElementText() ); else if( xml.name() == QLatin1String( "artist" ) && xml.isStartElement() ) track.setArtist( xml.readElementText() ); else if( xml.name() == QLatin1String( "status" ) && xml.isStartElement() ) track.setStatus( Echonest::statusToEnum( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "analyzer_version" ) && xml.isStartElement() ) track.setAnalyzerVersion( xml.readElementText() ); else if( xml.name() == QLatin1String( "release" ) && xml.isStartElement() ) track.setRelease( xml.readElementText() ); else if( xml.name() == QLatin1String( "song_id" ) && xml.isStartElement() ) track.setSong( Echonest::Song( xml.readElementText().toLatin1() ) ); else if( xml.name() == QLatin1String( "audio_md5" ) && xml.isStartElement() ) track.setAudioMD5( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "bitrate" ) && xml.isStartElement() ) track.setBitrate( xml.readElementText().toInt() ); else if( xml.name() == QLatin1String( "samplerate" ) && xml.isStartElement() ) track.setSamplerate( xml.readElementText().toInt() ); else if( xml.name() == QLatin1String( "md5" ) && xml.isStartElement() ) track.setMD5( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "catalog" ) && xml.isStartElement() ) track.setCatalog( xml.readElementText() ); else if( xml.name() == QLatin1String( "foreign_id" ) && xml.isStartElement() ) track.setForeignId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "release_image" ) && xml.isStartElement() ) track.setReleaseImage( QUrl( xml.readElementText(), QUrl::TolerantMode ) ); else if( xml.name() == QLatin1String( "preview_url" ) && xml.isStartElement() ) track.setPreviewUrl( QUrl( xml.readElementText(), QUrl::TolerantMode ) ); else if( xml.name() == QLatin1String( "audio_summary" ) && xml.isStartElement() ) { track.setAudioSummary( parseAudioSummary( xml ) ); continue; } xml.readNext(); } xml.readNext(); // skip past the last return track; } Echonest::AudioSummary Echonest::Parser::parseAudioSummary( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "audio_summary" ) ) { throw ParseError( Echonest::UnknownParseError ); } Echonest::AudioSummary summary; while( !( xml.name() == QLatin1String( "audio_summary" ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { if( xml.name() == QLatin1String( "key" ) && xml.isStartElement() ) summary.setKey( xml.readElementText().toInt() ); else if( xml.name() == QLatin1String( "analysis_url" ) && xml.isStartElement() ) summary.setAnalysisUrl( QUrl::fromEncoded( xml.readElementText().toUtf8(), QUrl::TolerantMode ) ); else if( xml.name() == QLatin1String( "tempo" ) && xml.isStartElement() ) summary.setTempo( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "mode" ) && xml.isStartElement() ) summary.setMode( xml.readElementText().toInt() ); else if( xml.name() == QLatin1String( "time_signature" ) && xml.isStartElement() ) summary.setTimeSignature( xml.readElementText().toInt() ); else if( xml.name() == QLatin1String( "duration" ) && xml.isStartElement() ) summary.setDuration( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "loudness" ) && xml.isStartElement() ) summary.setLoudness( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "danceability" ) && xml.isStartElement() ) summary.setDanceability( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "energy" ) && xml.isStartElement() ) summary.setEnergy( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "acousticness" ) && xml.isStartElement() ) summary.setAcousticness( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "speechiness" ) && xml.isStartElement() ) summary.setSpeechiness( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "liveness" ) && xml.isStartElement() ) summary.setLiveness( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "valence" ) && xml.isStartElement() ) summary.setValence( xml.readElementText().toDouble() ); xml.readNext(); } return summary; } // extract confidence, duration, start out of a list of them. same code for bars, beats, sections, tatums template< class T > inline QVector< T > extractTripleTuple( const QVariantList& list ) { QVector< T > tList; tList.reserve( list.size() ); for( QVariantList::const_iterator iter = list.constBegin(); iter != list.constEnd(); ++iter ) { T t; QVariantMap tMap = iter->toMap(); t.confidence = tMap.value( QLatin1String( "confidence" ), -1 ).toReal(); t.duration = tMap.value( QLatin1String( "duration" ), -1 ).toReal(); t.start = tMap.value( QLatin1String( "start" ), -1 ).toReal(); tList.append( t ); } // qDebug() << "Parsed simple list:" << tList.size(); return tList; } void Echonest::Parser::parseDetailedAudioSummary( QNetworkReply* reply, Echonest::AudioSummary& summary ) throw( ParseError ) { bool ok; QByteArray jsonData = reply->readAll(); QVariant data = QJsonWrapper::parseJson( jsonData, &ok ); if( !ok ) { qWarning() << "Failed to parse JSON data!" << jsonData; throw ParseError( Echonest::UnknownParseError ); } QVariantMap mainMap = data.toMap(); if( mainMap.contains( QLatin1String( "meta" ) ) ) { QVariantMap metaMap = mainMap.value( QLatin1String( "meta" ) ).toMap(); summary.setAnalysisTime( metaMap.value( QLatin1String( "analysis_time" ), -1 ).toReal() ); summary.setAnalysisStatus( metaMap.value( QLatin1String( "status_code" ) ).toInt() ); summary.setDetailedStatus( metaMap.value( QLatin1String( "detailed_status" ) ).toString() ); summary.setAnalyzerVersion( metaMap.value( QLatin1String( "analyzer_version" ) ).toString() ); summary.setTimestamp( metaMap.value( QLatin1String( "analysis_time" ), -1 ).toReal() ); } if( mainMap.contains( QLatin1String( "bars" ) ) ) { QVariantList barList = mainMap.value( QLatin1String( "bars" ) ).toList(); summary.setBars( extractTripleTuple( barList ) ); } if( mainMap.contains( QLatin1String( "beats" ) ) ) { QVariantList beatList = mainMap.value( QLatin1String( "beats" ) ).toList(); summary.setBeats( extractTripleTuple( beatList ) ); } if( mainMap.contains( QLatin1String( "sections" ) ) ) { QVariantList sectionList = mainMap.value( QLatin1String( "sections" ) ).toList(); summary.setSections( extractTripleTuple( sectionList ) ); } if( mainMap.contains( QLatin1String( "segments" ) ) ) { QVariantList segmentList = mainMap.value( QLatin1String( "segments" ) ).toList(); Echonest::SegmentList segments; segments.reserve( segmentList.size() ); for( QVariantList::const_iterator iter = segmentList.constBegin(); iter != segmentList.constEnd(); ++iter ) { Echonest::Segment segment; QVariantMap segmentMap = iter->toMap(); segment.confidence = segmentMap.value( QLatin1String( "confidence" ), -1 ).toReal(); segment.duration = segmentMap.value( QLatin1String( "duration" ), -1 ).toReal(); segment.loudness_max = segmentMap.value( QLatin1String( "loudness_max" ), -1 ).toReal(); segment.loudness_max_time = segmentMap.value( QLatin1String( "loudness_max_time" ), -1 ).toReal(); segment.loudness_start = segmentMap.value( QLatin1String( "loudness_start" ), -1 ).toReal(); // pitches QVariantList pitchesList = segmentMap.value( QLatin1String( "pitches" ) ).toList(); QVector< qreal > pitches; pitches.reserve( pitchesList.size() ); for( QVariantList::const_iterator piter = pitchesList.constBegin(); piter != pitchesList.constEnd(); ++piter ) pitches.append( piter->toReal() ); segment.pitches = pitches; segment.start = segmentMap.value( QLatin1String( "start" ), -1 ).toReal(); // timbre QVariantList timbreList = segmentMap.value( QLatin1String( "timbre" ) ).toList(); QVector< qreal > timbres; timbres.reserve( timbreList.size() ); for( QVariantList::const_iterator titer = timbreList.constBegin(); titer != timbreList.constEnd(); ++titer ) timbres.append( titer->toReal() ); segment.timbre = timbres; segments.append( segment ); } summary.setSegments( segments ); } if( mainMap.contains( QLatin1String( "tatums" ) ) ) { QVariantList tatumList = mainMap.value( QLatin1String( "tatums" ) ).toList(); summary.setTatums( extractTripleTuple( tatumList ) ); } if( mainMap.contains( QLatin1String( "track" ) ) ) { QVariantMap trackMap = mainMap.value( QLatin1String( "track" ) ).toMap(); summary.setSampleRate( trackMap.value( QLatin1String( "analysis_sample_rate" ), -1 ).toReal() ); summary.setEndOfFadeIn( trackMap.value( QLatin1String( "end_of_fade_in" ), -1 ).toReal() ); summary.setKeyConfidence( trackMap.value( QLatin1String( "key_confidence" ), -1 ).toReal() ); summary.setModeConfidence( trackMap.value( QLatin1String( "mode_confidence" ), -1 ).toReal() ); summary.setNumSamples( trackMap.value( QLatin1String( "num_samples" ), -1 ).toLongLong() ); summary.setSampleMD5( trackMap.value( QLatin1String( "sample_md5" ) ).toString() ); summary.setStartOfFadeOut( trackMap.value( QLatin1String( "start_of_fade_out" ), -1 ).toReal() ); summary.setTempoConfidence( trackMap.value( QLatin1String( "tempo_confidence" ), -1 ).toReal() ); summary.setTimeSignatureConfidence( trackMap.value( QLatin1String( "time_signature_confidence" ), -1 ).toReal() ); } } Echonest::Artists Echonest::Parser::parseArtists( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { // we expect to be in an start element if( xml.atEnd() || xml.name() != QLatin1String( "artists" ) || !xml.isStartElement() ) throw ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); Echonest::Artists artists; while( !xml.atEnd() && ( xml.name() != QLatin1String( "artists" ) || !xml.isEndElement() ) ) { if( xml.atEnd() || xml.name() != QLatin1String( "artist" ) || !xml.isStartElement() ) throw Echonest::ParseError( Echonest::UnknownParseError ); Echonest::Artist artist; while( !xml.atEnd() && ( xml.name() != QLatin1String( "artist" ) || !xml.isEndElement() ) ) { parseArtistInfo( xml, artist ); xml.readNextStartElement(); } artists.append( artist ); xml.readNext(); } return artists; } int Echonest::Parser::parseArtistInfoOrProfile( QXmlStreamReader& xml , Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.name() == QLatin1String( "start" ) ) { // this is an individual info query, so lets read it xml.readNextStartElement(); xml.readNext(); int results = -1; if( xml.name() == QLatin1String( "total" ) ) { results = xml.readElementText().toInt(); xml.readNextStartElement(); } parseArtistInfo( xml, artist ); return results; } else if( xml.name() == QLatin1String( "songs" ) ) { parseArtistSong( xml, artist ); } else if( xml.name() == QLatin1String( "urls" ) ) { // urls also has no start/total parseUrls( xml, artist ); } else { // this is either a profile query, or a familiarity or hotttness query, so save all the data we find while( !( xml.name() == QLatin1String( "artist" ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { parseArtistInfo( xml, artist ); xml.readNextStartElement(); } } return 0; } void Echonest::Parser::parseArtistInfo( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { // parse each sort of artist information if( xml.name() == QLatin1String( "audio" ) ) { parseAudio( xml, artist ); } else if( xml.name() == QLatin1String( "biographies" ) ) { parseBiographies( xml, artist ); } else if( xml.name() == QLatin1String( "familiarity" ) ) { artist.setFamiliarity( xml.readElementText().toDouble() ); } else if( xml.name() == QLatin1String( "hotttnesss" ) ) { artist.setHotttnesss( xml.readElementText().toDouble() ); } else if( xml.name() == QLatin1String( "images" ) ) { parseImages( xml, artist ); } else if( xml.name() == QLatin1String( "news" ) && xml.isStartElement() ) { parseNewsOrBlogs( xml, artist, true ); } else if( xml.name() == QLatin1String( "blogs" ) ) { parseNewsOrBlogs( xml, artist, false ); } else if( xml.name() == QLatin1String( "reviews" ) ) { parseReviews( xml, artist ); } else if( xml.name() == QLatin1String( "terms" ) ) { parseTerms( xml, artist ); } else if( xml.name() == QLatin1String( "urls" ) ) { parseUrls( xml, artist ); } else if( xml.name() == QLatin1String( "songs" ) ) { parseArtistSong( xml, artist ); } else if( xml.name() == QLatin1String( "video" ) ) { parseVideos( xml, artist ); } else if( xml.name() == QLatin1String( "foreign_ids" ) ) { parseForeignArtistIds( xml, artist ); } else if( xml.name() == QLatin1String( "name" ) ) { artist.setName( xml.readElementText() ); } else if( xml.name() == QLatin1String( "id" ) ) { artist.setId( xml.readElementText().toLatin1() ); } else if( xml.name() == QLatin1String( "genres" ) ) { parseArtistGenres( xml, artist ); } else if ( xml.name() == QLatin1String( "twitter" ) ) { artist.setTwitter( xml.readElementText() ); } } // parse each type of artist attribute void Echonest::Parser::parseAudio( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "audio" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); Echonest::AudioList audioList; while( !xml.atEnd() && ( xml.name() != QLatin1String( "audio" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ) { Echonest::AudioFile audio; do { xml.readNext(); if( xml.name() == QLatin1String( "title" ) ) audio.setTitle( xml.readElementText() ); else if( xml.name() == QLatin1String( "url" ) ) audio.setUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "artist" ) ) audio.setArtist( xml.readElementText() ); else if( xml.name() == QLatin1String( "date" ) ) audio.setDate( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); else if( xml.name() == QLatin1String( "length" ) ) audio.setLength( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "link" ) ) audio.setLink( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "release" ) ) audio.setRelease( xml.readElementText() ); else if( xml.name() == QLatin1String( "id" ) ) audio.setId( xml.readElementText().toLatin1() ); } while( !xml.atEnd() && ( xml.name() != QLatin1String( "audio" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ); audioList.append( audio ); xml.readNext(); } artist.setAudio( audioList ); } void Echonest::Parser::parseBiographies( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "biographies" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); Echonest::BiographyList bios; while( !xml.atEnd() && ( xml.name() != QLatin1String( "biographies" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ) { Echonest::Biography bio; do { xml.readNext(); if( xml.name() == QLatin1String( "text" ) ) bio.setText( xml.readElementText() ); else if( xml.name() == QLatin1String( "site" ) ) bio.setSite( xml.readElementText() ); else if( xml.name() == QLatin1String( "url" ) ) bio.setUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "license" ) ) bio.setLicense( parseLicense( xml) ); } while( !xml.atEnd() && ( xml.name() != QLatin1String( "biography" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ); bios.append( bio ); xml.readNext(); } artist.setBiographies( bios ); } void Echonest::Parser::parseImages( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "images" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); Echonest::ArtistImageList imgs; while( !xml.atEnd() && ( xml.name() != QLatin1String( "images" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ) { Echonest::ArtistImage img; do { xml.readNext(); if( xml.name() == QLatin1String( "url" ) ) img.setUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "license" ) ) img.setLicense( parseLicense( xml) ); } while( !xml.atEnd() && ( xml.name() != QLatin1String( "image" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ); imgs.append( img ); xml.readNext(); } artist.setImages( imgs ); } void Echonest::Parser::parseNewsOrBlogs( QXmlStreamReader& xml, Echonest::Artist& artist, bool news ) throw( Echonest::ParseError ) { if( news && ( xml.atEnd() || xml.name() != QLatin1String( "news" ) || xml.tokenType() != QXmlStreamReader::StartElement ) ) throw Echonest::ParseError( Echonest::UnknownParseError ); else if( !news && ( xml.atEnd() || xml.name() != QLatin1String( "blogs" ) || xml.tokenType() != QXmlStreamReader::StartElement ) ) throw Echonest::ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); Echonest::NewsList newsList; while( !( ( xml.name() == QLatin1String( "news" ) || xml.name() == QLatin1String( "blogs" ) ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { Echonest::NewsArticle news; do { xml.readNextStartElement(); if( xml.name() == QLatin1String( "name" ) ) news.setName( xml.readElementText() ); else if( xml.name() == QLatin1String( "url" ) ) news.setUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "summary" ) ) news.setSummary( xml.readElementText() ); else if( xml.name() == QLatin1String( "date_found" ) ) news.setDateFound( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); else if( xml.name() == QLatin1String( "id" ) ) news.setId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "date_posted" ) ) news.setDatePosted( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); } while( !( ( xml.name() == QLatin1String( "news" ) || xml.name() == QLatin1String( "blog" ) ) && xml.tokenType() == QXmlStreamReader::EndElement ) ); newsList.append( news ); xml.readNext(); } if( news ) artist.setNews( newsList ); else artist.setBlogs( newsList ); } void Echonest::Parser::parseReviews( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "reviews" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); Echonest::ReviewList reviews; while( !xml.atEnd() && ( xml.name() != QLatin1String( "reviews" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ) { Echonest::Review review; do { xml.readNextStartElement(); if( xml.name() == QLatin1String( "url" ) ) review.setUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "name" ) ) review.setName( xml.readElementText() ); else if( xml.name() == QLatin1String( "summary" ) ) review.setSummary( xml.readElementText() ); else if( xml.name() == QLatin1String( "date_found" ) ) review.setDateFound( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); else if( xml.name() == QLatin1String( "image" ) ) review.setImageUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "release" ) ) review.setRelease( xml.readElementText() ); else if( xml.name() == QLatin1String( "id" ) ) review.setId( xml.readElementText().toLatin1() ); } while( !xml.atEnd() && ( xml.name() != QLatin1String( "review" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ); reviews.append( review ); xml.readNext(); } artist.setReviews( reviews ); } void Echonest::Parser::parseArtistSong( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "songs" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); Echonest::SongList songs; while( !xml.atEnd() && ( xml.name() != QLatin1String( "songs" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ) { if( xml.name() == QLatin1String( "song" ) && xml.isStartElement() ) { Echonest::Song song; while( !xml.atEnd() && ( xml.name() != QLatin1String( "song" ) || !xml.isEndElement() ) ) { if( xml.name() == QLatin1String( "id" ) && xml.isStartElement() ) song.setId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "title" ) && xml.isStartElement() ) song.setTitle( xml.readElementText() ); xml.readNextStartElement(); } songs.append( song ); } xml.readNext(); } artist.setSongs( songs ); } void Echonest::Parser::parseTerms( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "terms" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); artist.setTerms( parseTopTermList( xml ) ); } Echonest::Artists Echonest::Parser::parseArtistSuggestList( QXmlStreamReader& xml ) throw( ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "artists" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); Echonest::Artists artists; xml.readNextStartElement(); while( xml.name() != QLatin1String( "artists" ) || !xml.isEndElement() ) { QString name; QByteArray id; while( xml.name() != QLatin1String( "artist" ) || !xml.isEndElement() ) { if( xml.name() == QLatin1String( "name" ) && xml.isStartElement() ) name = xml.readElementText(); else if( xml.name() == QLatin1String( "id" ) && xml.isStartElement() ) id = xml.readElementText().toLatin1(); xml.readNext(); } artists << Echonest::Artist( id, name ); xml.readNext(); } return artists; } void Echonest::Parser::parseUrls( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "urls" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); // xml.readNextStartElement(); while( !xml.atEnd() && ( xml.name() != QLatin1String( "urls" ) || !xml.isEndElement() ) ) { if( xml.name() == QLatin1String( "lastfm_url" ) ) artist.setLastFmUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "aolmusic_url" ) ) artist.setAolMusicUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "myspace_url" ) ) artist.setMyspaceUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "amazon_url" ) ) artist.setAmazonUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "itunes_url" ) ) artist.setItunesUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "mb_url" ) ) artist.setMusicbrainzUrl( QUrl( xml.readElementText() ) ); xml.readNextStartElement(); } xml.readNextStartElement(); } void Echonest::Parser::parseVideos( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "video" )|| xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); Echonest::VideoList videos; while( xml.name() == QLatin1String( "video" ) && xml.isStartElement() ) { Echonest::Video video; while( !xml.atEnd() && ( xml.name() != QLatin1String( "video" ) || !xml.isEndElement() ) ) { if( xml.name() == QLatin1String( "title" ) ) video.setTitle( xml.readElementText() ); else if( xml.name() == QLatin1String( "url" ) ) video.setUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "site" ) ) video.setSite( xml.readElementText() ); else if( xml.name() == QLatin1String( "date_found" ) ) video.setDateFound( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); else if( xml.name() == QLatin1String( "image_url" ) ) video.setImageUrl( QUrl( xml.readElementText() ) ); else if( xml.name() == QLatin1String( "id" ) ) video.setId( xml.readElementText().toLatin1() ); xml.readNextStartElement(); } videos.append( video ); xml.readNextStartElement(); } artist.setVideos( videos ); } Echonest::TermList Echonest::Parser::parseTopTermList( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "terms" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); Echonest::TermList terms; while( xml.name() == QLatin1String( "terms" ) && xml.isStartElement() ) { Echonest::Term term; // qDebug() << "Parsing term outer item:" << xml.name() << xml.isStartElement(); while( !xml.atEnd() && ( xml.name() != QLatin1String( "terms" ) || !xml.isEndElement() ) ) { if( xml.name() == QLatin1String( "frequency" ) ) term.setFrequency( xml.readElementText().toDouble() ); else if( xml.name() == QLatin1String( "name" ) ) term.setName( xml.readElementText() ); else if( xml.name() == QLatin1String( "weight" ) ) term.setWeight( xml.readElementText().toDouble() ); xml.readNextStartElement(); } terms.append( term ); // qDebug() << "Parsing exernal term item:" << xml.name() << xml.isStartElement(); xml.readNext(); } // qDebug() << " done Parsing terms:" << xml.name() << xml.isStartElement(); return terms; } QVector< QString > Echonest::Parser::parseTermList( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "terms" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); QVector< QString > terms; while( xml.name() != QLatin1String( "response" ) || !xml.isEndElement() ) { if( xml.name() == QLatin1String( "name" ) && xml.isStartElement() ) terms.append( xml.readElementText() ); xml.readNextStartElement(); } return terms; } QVector< QString > Echonest::Parser::parseGenreListStrings( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { xml.readNextStartElement(); xml.readNextStartElement(); if( xml.atEnd() || xml.name() != QLatin1String( "genres" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); QVector< QString > genres; while( xml.name() != QLatin1String( "response" ) || !xml.isEndElement() ) { if( xml.name() == QLatin1String( "name" ) && xml.isStartElement() ) genres.append( xml.readElementText() ); xml.readNextStartElement(); } return genres; } Echonest::Genres Echonest::Parser::parseGenres( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if ( xml.name() == QLatin1String( "start" ) ) xml.readNextStartElement(); if ( xml.name() == QLatin1String( "start" ) ) xml.readNextStartElement(); // skip if( xml.atEnd() || xml.name() != QLatin1String( "genres" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); Genres genres; while( !(xml.isEndElement() && xml.name() == QLatin1String( "genres" ) ) /*xml.name() != QLatin1String( "response" ) || !xml.isEndElement() ||*/ ){ if (!xml.isStartElement()) xml.readNextStartElement(); if( xml.name() == QLatin1String( "genre" ) && xml.isStartElement() ) { Genre g = parseGenre( xml ); genres.append( g ); } xml.readNext(); } return genres; } Echonest::Genre Echonest::Parser::parseGenre( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { Genre g; while ( !( xml.isEndElement() && xml.name() == QLatin1String( "genre" ) ) ) { if ( xml.name() == QLatin1String( "name" ) && xml.isStartElement() ) g.setName( xml.readElementText() ); else if ( xml.name() == QLatin1String( "description" ) ) g.setDescription( xml.readElementText() ); else if ( xml.name() == QLatin1String( "urls" ) ) { xml.readNextStartElement(); if ( xml.name() == QLatin1String( "urls" ) ) { xml.readNextStartElement(); if ( xml.name() == QLatin1String( "wikipedia_url" ) ) g.setWikipediaUrl( xml.readElementText() ); } } xml.readNextStartElement(); } return g; } void Echonest::Parser::parseArtistGenres( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { Genres genres = parseGenres( xml ); artist.setGenres( genres ); } void Echonest::Parser::parseForeignArtistIds( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "foreign_ids" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); Echonest::ForeignIds ids; while( xml.name() != QLatin1String( "foreign_ids" ) || !xml.isEndElement() ) { xml.readNext(); xml.readNext(); // get past the enclosing , or else we'll think it's the internal one. Echonest::ForeignId id; while( xml.name() != QLatin1String( "foreign_id" ) || !xml.isEndElement() ) { if( xml.name() == QLatin1String( "catalog" ) && xml.isStartElement() ) id.catalog = xml.readElementText(); else if( xml.name() == QLatin1String( "foreign_id" ) && xml.isStartElement() ) id.foreign_id = xml.readElementText(); xml.readNext(); } ids.append( id ); xml.readNext(); } artist.setForeignIds( ids ); } Echonest::License Echonest::Parser::parseLicense( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "license" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); Echonest::License license; while( !xml.atEnd() && ( xml.name() != QLatin1String( "license" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ) { if( xml.name() == QLatin1String( "type" ) ) license.type = xml.readElementText(); else if( xml.name() == QLatin1String( "attribution" ) ) license.attribution = xml.readElementText(); else if( xml.name() == QLatin1String( "url" ) ) license.url = QUrl( xml.readElementText() ); xml.readNext(); } xml.readNextStartElement(); return license; } QByteArray Echonest::Parser::parsePlaylistSessionId( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "session_id" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); QByteArray sessionId = xml.readElementText().toLatin1(); xml.readNext(); //read to next start element return sessionId; } Echonest::SongList Echonest::Parser::parseDynamicLookahead( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); Echonest::SongList lookahead; // Might not be any if ( xml.name() != QLatin1String( "lookahead" ) ) return lookahead; while( !xml.atEnd() && ( xml.name() == QLatin1String( "lookahead" ) && xml.tokenType() == QXmlStreamReader::StartElement ) ) { // Read each lookahead track Echonest::Song song; while( !xml.atEnd() && ( xml.name() != QLatin1String( "lookahead" ) || xml.tokenType() != QXmlStreamReader::EndElement ) ) { if( xml.name() == QLatin1String( "id" ) && xml.isStartElement() ) song.setId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "title" ) && xml.isStartElement() ) song.setTitle( xml.readElementText() ); else if( xml.name() == QLatin1String( "artist_id" ) && xml.isStartElement() ) song.setArtistId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "artist_name" ) && xml.isStartElement() ) song.setArtistName( xml.readElementText() ); xml.readNext(); } if ( !( song.id().isEmpty() && song.title().isEmpty() && song.artistId().isEmpty() && song.artistName().isEmpty() ) ) lookahead.append(song); xml.readNext(); } return lookahead; } // Catalogs parseCatalogList( QXmlStreamReader& xml ) throw( ParseError ); Echonest::Catalogs Echonest::Parser::parseCatalogList( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); int total = -1; while( xml.name() != QLatin1String( "response" ) && ( xml.name() != QLatin1String( "catalogs" ) || !xml.isStartElement() ) ) { if( xml.name() == QLatin1String( "total" ) && xml.isStartElement() ) total = xml.readElementText().toInt(); xml.readNextStartElement(); } Echonest::Catalogs catalogs; if( xml.name() != QLatin1String( "catalogs" ) ) { // none return catalogs; } catalogs.reserve( total ); // now we're pointing at the first catalog while( xml.name() != QLatin1String( "response" ) || !xml.isEndElement() ) catalogs.append( Echonest::Parser::parseCatalog( xml ) ); return catalogs; } Echonest::Catalog Echonest::Parser::parseCatalog( QXmlStreamReader& xml, bool justOne ) throw( Echonest::ParseError ) { QString cName = justOne ? QLatin1String( "catalog" ) : QLatin1String( "catalogs" ); if( xml.atEnd() || xml.name() != cName || !xml.isStartElement() ) throw Echonest::ParseError( Echonest::UnknownParseError ); xml.readNextStartElement(); Echonest::Catalog catalog; while( xml.name() != cName || !xml.isEndElement() ) { if( xml.name() == QLatin1String( "total" ) && xml.isStartElement() ) catalog.setTotal( xml.readElementText().toInt() ); else if( xml.name() == QLatin1String( "type" ) && xml.isStartElement() ) catalog.setType( Echonest::literalToCatalogType( xml.readElementText().toLatin1() ) ); else if( xml.name() == QLatin1String( "id" ) && xml.isStartElement() ) catalog.setId( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "name" ) && xml.isStartElement() ) catalog.setName( xml.readElementText() ); else if( xml.name() == QLatin1String( "items" ) && xml.isStartElement() ) { QList items = parseCatalogItems( xml ); if( items.isEmpty() ) { xml.readNextStartElement(); continue; } if( items[ 0 ]->type() == Echonest::CatalogTypes::Artist ) { saveArtistList( catalog, items ); } else if( items[ 0 ]->type() == Echonest::CatalogTypes::Song ) { saveSongList( catalog, items ); } } xml.readNextStartElement(); } xml.readNext(); return catalog; } QList Echonest::Parser::parseCatalogItems( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "items" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); QList items; while( xml.name() == QLatin1String( "items" ) && xml.isStartElement() ) { // OK, the mixture of the crappy Catalog API and strongly typed c++ makes this ugly. We don't know if this is an artist or song until we reach the artist_id or song_id. // so, we'll keep two copies till the end, where we throw away one. :( Echonest::CatalogArtist* artist = new Echonest::CatalogArtist; Echonest::CatalogSong* song = new CatalogSong; while( xml.name() != QLatin1String( "items" ) || !xml.isEndElement() ) { // OK, we have to check for all possible song+artist types :'( if( xml.name() == QLatin1String( "rating" ) && xml.isStartElement() ) { /// mixed and artist items artist->setRating( xml.readElementText().toInt() ); song->setRating( artist->rating() ); } else if( xml.name() == QLatin1String( "request" ) && xml.isStartElement() ) { parseCatalogRequestItem( xml, *artist, *song ); } else if( xml.name() == QLatin1String( "artist_name" ) && xml.isStartElement() ) { artist->setName( xml.readElementText() ); song->setArtistName( artist->name() ); } else if( xml.name() == QLatin1String( "reviews" ) && xml.isStartElement() ) { parseReviews( xml, *artist ); } else if( xml.name() == QLatin1String( "terms" ) && xml.isStartElement() ) { parseTerms( xml, *artist ); continue; } else if( xml.name() == QLatin1String( "biographies" ) && xml.isStartElement() ) { parseBiographies( xml, *artist ); } else if( xml.name() == QLatin1String( "familiarity" ) && xml.isStartElement() ) { artist->setFamiliarity( xml.readElementText().toDouble() ); song->setArtistFamiliarity( artist->familiarity() ); } else if( xml.name() == QLatin1String( "blogs" ) && xml.isStartElement() ) { parseNewsOrBlogs( xml, *artist, false ); } else if( xml.name() == QLatin1String( "hotttnesss" ) && xml.isStartElement() ) { artist->setHotttnesss( xml.readElementText().toDouble() ); song->setArtistHotttnesss( artist->hotttnesss() ); } else if( xml.name() == QLatin1String( "video" ) && xml.isStartElement() ) { parseVideos( xml, *artist ); } else if( xml.name() == QLatin1String( "urls" ) && xml.isStartElement() ) { parseUrls( xml, *artist ); } else if( xml.name() == QLatin1String( "news" ) && xml.isStartElement() ) { parseNewsOrBlogs( xml, *artist ); } else if( xml.name() == QLatin1String( "images" ) && xml.isStartElement() ) { parseImages( xml, *artist ); } else if( xml.name() == QLatin1String( "date_added" ) && xml.isStartElement() ) { artist->setDateAdded( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); song->setDateAdded( artist->dateAdded() ); } else if( xml.name() == QLatin1String( "artist_id" ) && xml.isStartElement() ) { artist->setId( xml.readElementText().toLatin1() ); song->setArtistId( artist->id() ); } else if( xml.name() == QLatin1String( "audio" ) && xml.isStartElement() ) { parseAudio( xml, *artist ); } else if( xml.name() == QLatin1String( "foreign_id" ) && xml.isStartElement() ) { artist->setForeignId( xml.readElementText().toLatin1() ); song->setForeignId( artist->foreignId() ); } else if( xml.name() == QLatin1String( "song_id" ) && xml.isStartElement() ) { /// song-specific entries song->setId( xml.readElementText().toLatin1() ); } else if( xml.name() == QLatin1String( "song_name" ) && xml.isStartElement() ) { song->setTitle( xml.readElementText() ); } else if( xml.name() == QLatin1String( "tracks" ) && xml.isStartElement() ) { song->setTracks( parseCatalogSongTracks( xml ) ); } else if( xml.name() == QLatin1String( "play_count" ) && xml.isStartElement() ) { static_cast(song)->setPlayCount( xml.readElementText().toInt() ); } else if( xml.name() == QLatin1String( "artist_hotttnesss" ) && xml.isStartElement() ) { song->setArtistHotttnesss( xml.readElementText().toDouble() ); } else if( xml.name() == QLatin1String( "artist_location" ) && xml.isStartElement() ) { // TODO } else if( xml.name() == QLatin1String( "song_hotttnesss" ) && xml.isStartElement() ) { song->setHotttnesss( xml.readElementText().toDouble() ); } else if( xml.name() == QLatin1String( "artist_familiarity" ) && xml.isStartElement() ) { song->setArtistFamiliarity( xml.readElementText().toDouble() ); } else if( xml.name() == QLatin1String( "audio_summary" ) && xml.isStartElement() ) { song->setAudioSummary( parseAudioSummary( xml ) ); } xml.readNextStartElement(); } if( !song->id().isEmpty() ) { // No song id, so it's an artist. // qDebug() << "Adding a song"; items << song; delete artist; } else if( !artist->id().isEmpty() ) { // qDebug() << "Adding an artist"; items << artist; delete song; } else { // dunno what this is really. lets use the song one for now // qDebug() << "Adding an EMPTY"; items << song; delete artist; } xml.readNext(); } return items; } void Echonest::Parser::parseCatalogRequestItem( QXmlStreamReader& xml, Echonest::CatalogArtist& artist, Echonest::CatalogSong& song) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "request" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); Echonest::CatalogUpdateEntry request; while( xml.name() != QLatin1String( "request" ) || !xml.isEndElement() ) { if( xml.name() == QLatin1String( "item_id" ) ) { request.setItemId( xml.readElementText().toLatin1() ); } else if( xml.name() == QLatin1String( "artist_name" ) ) { request.setArtistName( xml.readElementText() ); } else if( xml.name() == QLatin1String( "song_name" ) ) { request.setSongName( xml.readElementText() ); } else if( xml.name() == QLatin1String( "fp_code" ) ) { request.setFingerprint( xml.readElementText().toLatin1() ); } else if( xml.name() == QLatin1String( "song_id" ) ) { request.setSongId( xml.readElementText().toLatin1() ); } else if( xml.name() == QLatin1String( "artist_id" ) ) { request.setArtistId( xml.readElementText().toLatin1() ); } else if( xml.name() == QLatin1String( "release" ) ) { request.setRelease( xml.readElementText() ); } else if( xml.name() == QLatin1String( "genre" ) ) { request.setGenre( xml.readElementText() ); } xml.readNext(); } artist.setRequest( request ); song.setRequest( request ); } void Echonest::Parser::saveArtistList( Echonest::Catalog& catalog, QList& artists) { // will copy artists into the catalog, and delete the origin Echonest::CatalogArtists ca; foreach( const Echonest::CatalogItem* item, artists ) { ca.append( CatalogArtist( *static_cast( item ) ) ); } qDeleteAll( artists ); catalog.setArtists( ca ); } void Echonest::Parser::saveSongList( Echonest::Catalog& catalog, QList& songs) { // will copy songs into the catalog, and delete the origin Echonest::CatalogSongs ca; foreach( const Echonest::CatalogItem* item, songs ) { ca.append( CatalogSong( *static_cast( item ) ) ); } qDeleteAll( songs ); catalog.setSongs( ca ); } Echonest::CatalogStatus Echonest::Parser::parseCatalogStatus( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { Echonest::CatalogStatus status; while( xml.name() != QLatin1String( "response" ) || !xml.isEndElement() ) { if( xml.name() == QLatin1String( "ticket_status" ) && xml.isStartElement() ) status.status = Echonest::literalToCatalogStatus( xml.readElementText().toLatin1() ); else if( xml.name() == QLatin1String( "details" ) && xml.isStartElement() ) status.details = xml.readElementText(); else if( xml.name() == QLatin1String( "items_updated" ) && xml.isStartElement() ) status.items_updated = xml.readElementText().toInt(); else if( xml.name() == QLatin1String( "update_info" ) && xml.isStartElement() ) status.items = parseTicketUpdateInfo( xml ); // else if( xml.name() == "percent_complete" && xml.isStartElement() ) // status.percent_complete = xml.readElementText().toInt(); xml.readNext(); } return status; } Echonest::CatalogStatusItem Echonest::Parser::parseTicketUpdateInfo( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { // if( xml.atEnd() || xml.name() != "ticket_status" || xml.tokenType() != QXmlStreamReader::StartElement ) // throw Echonest::ParseError( Echonest::UnknownParseError ); // TODO return Echonest::CatalogStatusItem(); } QByteArray Echonest::Parser::parseCatalogTicket( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "ticket" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); QByteArray ticket= xml.readElementText().toLatin1(); return ticket; } Echonest::Catalog Echonest::Parser::parseNewCatalog( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); QString name; QByteArray id; Echonest::CatalogTypes::Type type = Echonest::CatalogTypes::Artist; qDebug() << "Parsing new catalog..."; while( xml.name() != QLatin1String( "response" ) || !xml.isEndElement() ) { qDebug() << "Parsing at:" << xml.name().toString(); if( xml.name() == QLatin1String( "name" ) && xml.isStartElement() ) name = xml.readElementText(); else if( xml.name() == QLatin1String( "id" ) && xml.isStartElement() ) id = xml.readElementText().toLatin1(); else if( xml.name() == QLatin1String( "type" ) && xml.isStartElement() ) type = Echonest::literalToCatalogType( xml.readElementText().toLatin1() ); xml.readNextStartElement(); qDebug() << "Parsing next at:" << xml.name().toString(); } Echonest::Catalog c = Echonest::Catalog( id ); c.setName( name ); c.setType( type ); return c; } Echonest::SessionInfo Echonest::Parser::parseSessionInfo( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { Echonest::SessionInfo info; while( xml.name() != QLatin1String( "response" ) || !xml.isEndElement() ) { // qDebug() << "Parsing part of session info:" << xml.name() << xml.isStartElement(); // TODO /** Sample output: http://files.lfranchi.com/echonest_dynamic_info.xml if( xml.name() == "terms" && xml.isStartElement() ) { info.terms = parseTopTermList( xml ); continue; } else if( xml.name() == "rules" && xml.isStartElement() ) { info.rules = parseRulesList( xml ); continue; } else if( xml.name() == "session_id" && xml.isStartElement() ) info.session_id = xml.readElementText().toLatin1(); else if( xml.name() == "seeds" && xml.isStartElement() ) info.seeds.append( Artist( xml.readElementText().toLatin1() ) ); else if( xml.name() == "banned_artists" && xml.isStartElement() ) info.banned_artists.append( Artist( xml.readElementText().toLatin1() ) ); else if( xml.name() == "seed_songs" && xml.isStartElement() ) info.seed_songs.append( Song( xml.readElementText().toLatin1() ) ); else if( xml.name() == "seed_catalog" && xml.isStartElement() ) info.seed_catalogs.append( Catalog( xml.readElementText().toLatin1() ) ); else if( xml.name() == "playlist_type" && xml.isStartElement() ) info.playlist_type = xml.readElementText(); else if( xml.name() == "skipped_songs" && xml.isStartElement() ) { info.skipped_songs = parseSessionSongItem( xml, QLatin1String( "skipped_songs" ) ); continue; } else if( xml.name() == "banned_songs" && xml.isStartElement() ) { info.banned_songs = parseSessionSongItem( xml, QLatin1String( "banned_songs" ) ); continue; } else if( xml.name() == "rated_songs" && xml.isStartElement() ) { info.rated_songs = parseSessionSongItem( xml, QLatin1String( "rated_songs" ) ); continue; } else if( xml.name() == "history" && xml.isStartElement() ) { info.history = parseSessionSongItem( xml, QLatin1String( "history" ) ); continue; }*/ xml.readNext(); } return info; } QVector< QString > Echonest::Parser::parseRulesList( QXmlStreamReader& xml ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != QLatin1String( "rules" ) || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); QVector< QString > rules; while( xml.name() == QLatin1String( "rules" ) && xml.isStartElement() ) { // qDebug() << "Parsing start of rules:" << xml.name() << xml.isStartElement(); xml.readNextStartElement(); rules.append( xml.readElementText() ); xml.readNext(); xml.readNext(); // qDebug() << "Parsing end of rules:" << xml.name() << xml.isStartElement(); } return rules; } /* * TODO port to API v2 QVector< Echonest::SessionItem > Echonest::Parser::parseSessionSongItem( QXmlStreamReader& xml, const QString& type ) throw( Echonest::ParseError ) { if( xml.atEnd() || xml.name() != type || xml.tokenType() != QXmlStreamReader::StartElement ) throw Echonest::ParseError( Echonest::UnknownParseError ); QVector< Echonest::SessionItem > items; while( xml.name() == type && xml.isStartElement() ) { // qDebug() << "Parsing exernal item:" << xml.name() << xml.isStartElement(); Echonest::SessionItem item; while( !xml.atEnd() && ( xml.name() != type || !xml.isEndElement() ) ) { // qDebug() << "Parsing internal item:" << xml.name() << xml.isStartElement(); if( xml.name() == "served_time" ) item.served_time = xml.readElementText().toDouble(); else if( xml.name() == "artist_id" ) item.artist_id = xml.readElementText().toLatin1(); else if( xml.name() == "id" ) item.id = xml.readElementText().toLatin1(); else if( xml.name() == "artist_name" ) item.artist_name= xml.readElementText(); else if( xml.name() == "title" ) item.title = xml.readElementText(); else if( xml.name() == "rating" ) item.rating = xml.readElementText().toInt(); xml.readNextStartElement(); } items.append( item ); xml.readNext(); } return items; }*/ libechonest-2.3.1/src/Artist_p.h000644 001750 001750 00000006667 12465467260 017376 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_ARTIST_P_H #define ECHONEST_ARTIST_P_H #include "Song.h" #include "ArtistTypes.h" #include "CommonTypes.h" #include #include #include namespace Echonest { class Artist; } class ArtistData : public QSharedData { public: ArtistData() : familiarity( -1 ), hotttnesss( -1 ) {} ArtistData( const QByteArray& id, const QString& name ) : id( id ), name( name ), familiarity( -1 ), hotttnesss( -1 ) {} ArtistData(const ArtistData& other) : QSharedData( other ) { id = other.id; name = other.name; audio = other.audio; biographies = other.biographies; blogs = other.blogs; familiarity = other.familiarity; hotttnesss = other.hotttnesss; images = other.images; news = other.news; reviews = other.reviews; songs = other.songs; similar = other.similar; terms = other.terms; videos = other.videos; twitter = other.twitter; lastfm_url = other.lastfm_url; aolmusic_url = other.aolmusic_url; myspace_url = other.myspace_url; amazon_url = other.amazon_url; itunes_url = other.itunes_url; mb_url = other.mb_url; foreign_ids = other.foreign_ids; genres = other.genres; } // The following exist in all valid Artist objects QByteArray id; QString name; //The following are populated on demand, and may not exist Echonest::AudioList audio; Echonest::BiographyList biographies; Echonest::BlogList blogs; qreal familiarity; qreal hotttnesss; Echonest::ArtistImageList images; Echonest::NewsList news; Echonest::ReviewList reviews; Echonest::SongList songs; QVector similar; Echonest::TermList terms; Echonest::VideoList videos; QString twitter; QUrl lastfm_url; QUrl aolmusic_url; QUrl myspace_url; QUrl amazon_url; QUrl itunes_url; QUrl mb_url; Echonest::ForeignIds foreign_ids; Echonest::Genres genres; }; #endif libechonest-2.3.1/src/Artist.h000644 001750 001750 00000037057 12465467260 017054 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_ARTIST_H #define ECHONEST_ARTIST_H #include "ArtistTypes.h" #include "Config.h" #include "echonest_export.h" #include "TypeInformation.h" #include "Song.h" #include "CommonTypes.h" #include #include #include class QNetworkReply; class ArtistData; class Term; namespace Echonest{ class Biography; class Catalog; /** * This encapsulates an Echo Nest artist---it always holds the basic info of artist id and * artist name, and can be queried for more data. * * It is also possible to fetch more information from a given artist name or ID by creating an Artist * object yourself and calling the fetch() functions directly. */ class ECHONEST_EXPORT Artist { public: enum TermSorting { Weight, Frequency } ; /** * The following are the various search parameters to the search() and similar() functions. * * Not all are acceptable for each API call, check the API documentation at * http://developer.echonest.com/docs/v4/artist.html#search for details. * * - id QVector< QByteArray > A list of the artist IDs to be searched (e.g. [ARH6W4X1187B99274F, musicbrainz:artist:a74b1b7f-71a5-4011-9441-d0b5e4122711 ,ARH6W4X1187B99274F^2]) * - name QVector< QString > A list of artist names to be searched (e.g. [Weezer, the beatles ,the beatles^0.5]) * - description QVector< QString > A list of descriptors [ alt-rock,-emo,harp^2 ] * - results 0 < results < 200, (Default=15) The number of results desired * - min_results 0 < results < 200, (Default=0) Indicates the minimum number of results to be returned regardless of constraints * - max_familiarity 0.0 < familiarity < 1.0 The maximum familiarity for returned artists * - min_familiarity 0.0 < familiarity < 1.0 The minimum familiarity for returned artists * - max_hotttnesss 0.0 < hotttnesss < 1.0 The maximum hotttnesss for returned artists * - min_hotttness 0.0 < hotttnesss < 1.0 The minimum hotttnesss for returned artists * - reverse [true, false] If true, return artists that are disimilar to the seeds * -sort QString How to sort the results. Options: familiarity-asc, hotttnesss-asc, familiarity-desc, hotttnesss-desc. * */ enum SearchParam { Id, Name, Results, Description, FuzzyMatch, MaxFamiliarity, MinFamiliarity, MaxHotttnesss, MinHotttnesss, Reverse, Sort, IdSpace, Mood }; typedef QPair< Echonest::Artist::SearchParam, QVariant > SearchParamEntry; typedef QVector< SearchParamEntry > SearchParams; Artist(); Artist( const QByteArray& id, const QString& name ); explicit Artist( const QString& name ); explicit Artist( const QByteArray& id ); Artist( const Artist& other ); Artist& operator=( const Artist& artist ); virtual ~Artist(); void init(); QByteArray id() const; void setId( const QByteArray& id ); QString name() const; void setName( const QString& name ); /** * The following require fetching from The Echo Nest, so return a QNetworkReply* * that is ready for parsing when the finished() signal is emitted. * * Call parseProfile() on the Artist object to populate it with the result of the * query. * */ /** * @Deprecated * A list of audio files on the web for this artist. */ AudioList audio() const; void setAudio( const AudioList& ); /** * A list of biographies for this artist. */ BiographyList biographies() const; void setBiographies( const BiographyList& ); /** * Blogs about this artist, around the web. */ BlogList blogs() const; void setBlogs( const BlogList& ); /** * How familiar this artist is. */ qreal familiarity() const; void setFamiliarity( qreal familiar ); /** * The hotttness of this artist. */ qreal hotttnesss() const; void setHotttnesss( qreal hotttnesss ); /** * Images related to this artist. */ ArtistImageList images() const; void setImages( const ArtistImageList& ); /** * News about this artist. */ NewsList news() const; void setNews( const NewsList& ); /** * Reviews of this artist */ ReviewList reviews() const; void setReviews( const ReviewList& ); /** * Echo Nest song objects belonging to this artist. */ SongList songs() const; void setSongs( const SongList& ); /** * Terms describing this artist. */ TermList terms() const; void setTerms( const TermList& ); /** * Twitter username */ QString twitter() const; void setTwitter( const QString& ); /** * Genres describing this artist. */ Genres genres() const; void setGenres( const Genres& ); /** * Urls pointing to this artists' basic information around the web. */ QUrl lastFmUrl() const; void setLastFmUrl( const QUrl& ); QUrl aolMusicUrl() const; void setAolMusicUrl( const QUrl& ); QUrl amazonUrl() const; void setAmazonUrl( const QUrl& ); QUrl itunesUrl() const; void setItunesUrl( const QUrl& ); QUrl myspaceUrl() const; void setMyspaceUrl( const QUrl& ); QUrl musicbrainzUrl() const; void setMusicbrainzUrl( const QUrl& url ); /** * Videos related to this artist. */ VideoList videos() const; void setVideos( const VideoList& ); /** * The list of foreign ids for this artist, if fetched. */ ForeignIds foreignIds() const; void setForeignIds( const ForeignIds& ids ); /** * @Deprecated * Fetch a list of audio documents found on the web that are related to this artist. * * @param numResults Limit how many results are returned * @param offset The offset of the results, if paging through results in increments. */ QNetworkReply* fetchAudio( int numResults = 0, int offset = -1 ) const; /** * Fetch a list of biographies for this artist from various places on the web. */ QNetworkReply* fetchBiographies( const QString& license = QString(), int numResults = 0, int offset = -1 ) const; /** * Fetch a list of blog articles relating to this artist. */ QNetworkReply* fetchBlogs( bool highRelevanceOnly = false, int numResults = 0, int offset = -1 ) const; /** * Fetch The Echo Nest's numerical estimate of how familiar this artist is to the world. */ QNetworkReply* fetchFamiliarity() const; /** * Fetch the numerical description of how hot this artist is. * * Currently the only supported type is 'normal' */ QNetworkReply* fetchHotttnesss( const QString& type = QLatin1String( "normal" ) ) const; /** * Fetch a list of images related to this artist. */ QNetworkReply* fetchImages( const QString& license = QString(), int numResults = 0, int offset = -1 ) const; /** * Fetch a list of news articles found on the web related to this artist. */ QNetworkReply* fetchNews( bool highRelevanceOnly = false, int numResults = 0, int offset = -1 ) const; /** * Fetch any number of pieces of artist information all at once. */ QNetworkReply* fetchProfile( ArtistInformation information = ArtistInformation() ) const; /** * Fetch reviews related to the artist. */ QNetworkReply* fetchReviews( int numResults = 0, int offset = -1 ) const; /** * Fetch a list of songs created by this artist. */ QNetworkReply* fetchSongs( int numResults = 0, int offset = -1 ) const; /** * Fetch a list of the most descriptive terms for this artist. */ QNetworkReply* fetchTerms( TermSorting sorting = Frequency ) const; /** * Fetch artist's twitter handle. */ QNetworkReply* fetchTwitter() const; /** * Fetch links to the artist's official site, MusicBrainz site, MySpace site, Wikipedia article, Amazon list, and iTunes page. */ QNetworkReply* fetchUrls() const; /** * Fetch a list of video documents found on the web related to an artist. */ QNetworkReply* fetchVideo( int numResults = 0, int offset = -1 ) const; /** * Parse a completed QNetworkReply* that has fetched more information about this artist. * This will update the artist object with the new values that have been fetched. * * @return The number of results available on the server. */ int parseProfile( QNetworkReply* ) throw( ParseError ); /** * Fetch a list of similar artists given one or more artists for comparison. * * Up to five artist names or ids can be included for the similarity search. * * * So they are passed as a list of [paramname, paramvalue] to be included in the query. * * Boosting: This method can take multiple seed artists. You an give a seed artist more or less weight by boosting the artist. A boost is an * affinity for a seed that gives it more or less weight when making calculations based on the argument. In case seeds are not meant to be equally * valued, the boost can help clarify where along a spectrum each argument falls. The boost is a positive floating point value, where 1 gives the normal * weight. It is signified by appending a caret and weight to the argument. * * See http://developer.echonest.com/docs/v4/artist.html#similar for boosting examples. * * Call parseSimilar() once the returned QNetworkReply* has emitted its finished() signal */ static QNetworkReply* fetchSimilar( const SearchParams& params, ArtistInformation information = ArtistInformation(), int numResults = 0, int offset = -1 ); /** * Search for artists. * * Warning: If limit is set to true, at least one idspace must also be provided. * * One of name or description is required, but only one can be used in a query at one time * */ static QNetworkReply* search( const SearchParams& params, ArtistInformation information = ArtistInformation(), bool limit = false ); /** * Fetch a list of the current top artists in terms of hotttnesss. * * Warning If limit is set to true, at least one idspace must also be provided in the bucket parameter. * */ static QNetworkReply* topHottt( ArtistInformation information = ArtistInformation(), int numResults = 0, int offset = -1, bool limit = false ); /** * Fetch a list of the top overall terms. */ static QNetworkReply* topTerms( int numResults = 15 ); /** * Suggest artists based on a partial name. * * \param results How many results to return, between 0 and 15. Default is 10 */ static QNetworkReply* suggest( const QString& name, int results = 10 ); /** * Returns a list of terms of the given type, for use in other calls. * * \param type Which type of term to return, at the moment only 'style' or 'mood' */ static QNetworkReply* listTerms( const QString& type = QLatin1String("style") ); /** * @Deprecated * Fetch a list of genres supported by echonest */ static QNetworkReply* fetchGenres(); /** * Parse the result of a fetchSimilar() call, which returns a list of artists similar to the * original pair. */ static Artists parseSimilar( QNetworkReply* ) throw( ParseError ); /** * Parse the result of an artist search. */ static Artists parseSearch( QNetworkReply* ) throw( ParseError ); /** * Parse the result of a top hottness query. */ static Artists parseTopHottt( QNetworkReply* ) throw( ParseError ); /** * Parse the result of a top terms query. */ static TermList parseTopTerms( QNetworkReply* ) throw( ParseError ); /** * Parse the result of a suggest query * * Returns a list of suggested artists */ static Artists parseSuggest( QNetworkReply* ) throw( ParseError ); /** * Parse the result of a termList query. Returns the list of terms delivered. */ static QVector< QString > parseTermList( QNetworkReply* ) throw( ParseError ); /** * @Deprecated * Parse the result of a genres query. Returns the list of genres delivered. */ static QVector< QString > parseGenreList( QNetworkReply* ) throw ( ParseError ); private: QUrl setupQuery( const QByteArray& methodName, int numResults = 0, int start = -1 ) const; static QByteArray searchParamToString( SearchParam param ); static void addQueryInformation( QUrl& url, ArtistInformation information ); QSharedDataPointer d; friend class Catalog; friend class Genre; }; ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Artist& artist); } // namespace Q_DECLARE_METATYPE( Echonest::Artist ) Q_DECLARE_METATYPE( Echonest::Artists ); #endif libechonest-2.3.1/src/CatalogItem_p.h000644 001750 001750 00000004040 12465467260 020300 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CATALOG_ITEM_P_H #define ECHONEST_CATALOG_ITEM_P_H #include "Song_p.h" #include #include #include #include #include "CatalogUpdateEntry.h" class CatalogItemData : public QSharedData { public: CatalogItemData() {} CatalogItemData(const CatalogItemData& other) : QSharedData( other ) { foreign_id = other.foreign_id; request = other.request; date_added = other.date_added; rating = other.rating; play_count = other.play_count; } ~CatalogItemData() {} Echonest::CatalogUpdateEntry request; QByteArray foreign_id; QDateTime date_added; int rating; int play_count; }; #endif libechonest-2.3.1/src/AudioSummary_p.h000644 001750 001750 00000010561 12465467260 020533 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Util.h" #include #include #include #include class AudioSummaryData : public QSharedData { public: AudioSummaryData() : key( -1 ), tempo( -1 ), mode( -1 ), time_signature( -1 ), duration( -1 ), loudness( -1 ), samplerate( -1 ), danceability( -1 ), energy( -1 ), acousticness( -1 ), speechiness( -1 ), liveness( -1 ), valence( -1 ), analysis_time( -1 ), status( -1 ), timestamp( -1 ), end_of_fade_in( -1 ), key_confidence( -1 ), mode_confidence( -1 ), num_samples( -1 ), start_of_fade_out( -1 ), tempo_confidence( -1 ), time_signature_confidence( -1 ) {} AudioSummaryData(const AudioSummaryData& other) : QSharedData( other ) { key = other.key; tempo = other.tempo; mode = other.mode; time_signature = other.time_signature; duration = other.duration; loudness = other.loudness; samplerate = other.samplerate; danceability = other.danceability; energy = other.energy; acousticness = other.acousticness; speechiness = other.speechiness; liveness = other.liveness; valence = other.valence; analysis_url = other.analysis_url; analysis_time = other.analysis_time; analyzer_version = other.analyzer_version; detailed_status = other.detailed_status; status = other.status; timestamp = other.timestamp; end_of_fade_in = other.end_of_fade_in; key_confidence = other.key_confidence; loudness = other.loudness; mode_confidence = other.mode_confidence; num_samples = other.num_samples; sample_md5 = other.sample_md5; start_of_fade_out = other.start_of_fade_out; tempo_confidence = other.tempo_confidence; time_signature = other.time_signature; time_signature_confidence = other.time_signature_confidence; bars = other.bars; beats = other.beats; sections = other.sections; tatums = other.tatums; segments = other.segments; } // basic data that always exists in an Audio Summary object int key; qreal tempo; int mode; int time_signature; qreal duration; qreal loudness; int samplerate; qreal danceability; qreal energy; qreal acousticness; qreal speechiness; qreal liveness; qreal valence; QUrl analysis_url; // used to fetch the following pieces of data // meta section qreal analysis_time; QString analyzer_version; QString detailed_status; int status; qreal timestamp; // track section qreal end_of_fade_in; qreal key_confidence; qreal mode_confidence; qint64 num_samples; QString sample_md5; qreal start_of_fade_out; qreal tempo_confidence; qreal time_signature_confidence; Echonest::BarList bars; Echonest::BeatList beats; Echonest::SectionList sections; Echonest::TatumList tatums; Echonest::SegmentList segments; }; libechonest-2.3.1/tests/test_data/000755 001750 001750 00000000000 12465467301 017740 5ustar00stefanstefan000000 000000 libechonest-2.3.1/src/TypeInformation.cpp000644 001750 001750 00000014742 12465467260 021264 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "TypeInformation.h" #include namespace Echonest { class ArtistInformationPrivate { public: ArtistInformationPrivate() : flags( ArtistInformation::NoInformation ) {} Echonest::ArtistInformation::ArtistInformationFlags flags; QStringList idSpaces; }; class SongInformationPrivate { public: SongInformationPrivate() : flags( SongInformation::NoInformation ) {} SongInformation::SongInformationFlags flags; QStringList idSpaces; }; class GenreInformationPrivate { public: GenreInformationPrivate() : flags( GenreInformation::NoInformation ) {} GenreInformation::GenreInformationFlags flags; }; } // namespace Echonest::ArtistInformation::ArtistInformation() : d_ptr( new Echonest::ArtistInformationPrivate ) { Q_D( ArtistInformation ); d->flags = Echonest::ArtistInformation::NoInformation; } Echonest::ArtistInformation::ArtistInformation( ArtistInformationFlags flags ) : d_ptr( new Echonest::ArtistInformationPrivate ) { Q_D( ArtistInformation ); d->flags = flags; } Echonest::ArtistInformation::ArtistInformation( ArtistInformation::ArtistInformationFlags flags, const QStringList& idSpaces ) : d_ptr( new Echonest::ArtistInformationPrivate ) { Q_D( Echonest::ArtistInformation ); d->flags = flags; d->idSpaces = idSpaces; } Echonest::ArtistInformation::ArtistInformation( const Echonest::ArtistInformation& other ) : d_ptr( new Echonest::ArtistInformationPrivate( *other.d_ptr ) ) { } Echonest::ArtistInformation::~ArtistInformation() { delete d_ptr; } Echonest::ArtistInformation& Echonest::ArtistInformation::operator=( const Echonest::ArtistInformation& typeInfo ) { d_ptr = new Echonest::ArtistInformationPrivate( *typeInfo.d_ptr ); return *this; } Echonest::ArtistInformation::ArtistInformationFlags Echonest::ArtistInformation::flags() const { Q_D( const Echonest::ArtistInformation ); return d->flags; } void Echonest::ArtistInformation::setArtistInformationFlags( ArtistInformationFlags flags) { Q_D( Echonest::ArtistInformation ); d->flags = flags; } QStringList Echonest::ArtistInformation::idSpaces() const { Q_D( const Echonest::ArtistInformation ); return d->idSpaces; } void Echonest::ArtistInformation::setIdSpaces(const QStringList& idspaces) { Q_D( Echonest::ArtistInformation ); d->idSpaces = idspaces; } Echonest::SongInformation::SongInformation() : d_ptr( new Echonest::SongInformationPrivate ) { Q_D( Echonest::SongInformation ); d->flags = Echonest::SongInformation::NoInformation; } Echonest::SongInformation::SongInformation( SongInformationFlags flags ) : d_ptr( new Echonest::SongInformationPrivate ) { Q_D( SongInformation ); d->flags = flags; } Echonest::SongInformation::SongInformation( SongInformation::SongInformationFlags flags, const QStringList& idSpaces ) : d_ptr( new Echonest::SongInformationPrivate ) { Q_D( Echonest::SongInformation ); d->flags = flags; d->idSpaces = idSpaces; } Echonest::SongInformation::SongInformation( const Echonest::SongInformation& other ) : d_ptr( new Echonest::SongInformationPrivate( *other.d_ptr ) ) { } Echonest::SongInformation::~SongInformation() { delete d_ptr; } Echonest::SongInformation& Echonest::SongInformation::operator=( const Echonest::SongInformation& info ) { d_ptr = new Echonest::SongInformationPrivate( *info.d_ptr ); return *this; } Echonest::SongInformation::SongInformationFlags Echonest::SongInformation::flags() const { Q_D( const Echonest::SongInformation ); return d->flags; } void Echonest::SongInformation::setSongInformationFlags( SongInformationFlags flags ) { Q_D( Echonest::SongInformation ); d->flags = flags; } QStringList Echonest::SongInformation::idSpaces() const { Q_D( const Echonest::SongInformation ); return d->idSpaces; } void Echonest::SongInformation::setIdSpaces(const QStringList& idspaces) { Q_D( Echonest::SongInformation ); d->idSpaces = idspaces; } Echonest::GenreInformation::GenreInformation() : d_ptr( new Echonest::GenreInformationPrivate ) { Q_D( Echonest::GenreInformation ); d->flags = Echonest::GenreInformation::NoInformation; } Echonest::GenreInformation::GenreInformation( GenreInformationFlags flags ) : d_ptr( new Echonest::GenreInformationPrivate ) { Q_D( GenreInformation ); d->flags = flags; } Echonest::GenreInformation::GenreInformation( const Echonest::GenreInformation& other ) : d_ptr( new Echonest::GenreInformationPrivate( *other.d_ptr ) ) { } Echonest::GenreInformation::~GenreInformation() { delete d_ptr; } Echonest::GenreInformation& Echonest::GenreInformation::operator=( const Echonest::GenreInformation& info ) { d_ptr = new Echonest::GenreInformationPrivate( *info.d_ptr ); return *this; } Echonest::GenreInformation::GenreInformationFlags Echonest::GenreInformation::flags() const { Q_D( const Echonest::GenreInformation ); return d->flags; } void Echonest::GenreInformation::setGenreInformationFlags( GenreInformationFlags flags ) { Q_D( Echonest::GenreInformation ); d->flags = flags; } libechonest-2.3.1/tests/TrackTest.h000644 001750 001750 00000005551 12465467260 020057 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * Copyright (c) 2011 Jeff Mitchell * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_TRACK_TEST_H #define ECHONEST_TRACK_TEST_H #include #include #include #include "Config.h" #include "Track.h" static QThread* s_mainThread; class TrackTest : public QObject { Q_OBJECT private slots: void initTestCase(); void testAnalyzeFromMD5(); void testAnalyzerFromId(); void testProfileFromMD5(); void testProfileFromId(); void testThreads(); private: void testUploadLocalFile(); // slow and uploads an mp3, only enable on demand void verifyTrack1( const Echonest::Track& track ); void verifyTrack2( const Echonest::Track& track ); }; class TrackTestThreadObject : public QObject { Q_OBJECT public: TrackTestThreadObject() { } virtual ~TrackTestThreadObject() { } public slots: void go() { Echonest::Config::instance()->setNetworkAccessManager( new QNetworkAccessManager() ); QByteArray id = "TROICNF12B048DE990"; QNetworkReply* reply = Echonest::Track::analyzeTrackId( id, true ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVERIFY( reply->error() == QNetworkReply::NoError ); qDebug() << "main thread is " << s_mainThread << " and current thread is " << QThread::currentThread(); QVERIFY( QThread::currentThread() != s_mainThread ); QThread::currentThread()->quit(); } }; #endif libechonest-2.3.1/src/AudioSummary.h000644 001750 001750 00000020415 12465467260 020213 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_AUDIOSUMMARY_H #define ECHONEST_AUDIOSUMMARY_H #include "echonest_export.h" #include #include #include "Util.h" #include "Util.h" #include "Config.h" class QNetworkReply; class QNetworkReply; class AudioSummaryData; namespace Echonest{ /** * This encapsulates the audio summary of an Echo Nest track or song. * * It has two batches of data: the more generic acoustic information about the * song is always populated, and additional detailed information about the song * such as bars, beats, segments, tatus, and sections, can be fetched as well as * an additional step. * * This class is implicitly shared. */ class ECHONEST_EXPORT AudioSummary { public: AudioSummary(); AudioSummary( const AudioSummary& other ); ~AudioSummary(); AudioSummary& operator=( const AudioSummary& audio ); int key() const; void setKey( int key ); /** * The track's tempo. */ qreal tempo() const; void setTempo( qreal tempo ); /** * The track's mode. */ int mode() const; void setMode( int mode ); /** * The track's time signature, or -1 if it there is one, or 1 if it is * too complex. */ int timeSignature() const; void setTimeSignature( int timeSignature ); /** * The duration of the song, in msecs. */ qreal duration() const; void setDuration( qreal duration ); /** * The loudness of the song, in dB. */ qreal loudness() const; void setLoudness( qreal loudness ); /** * The danceability of this track, from 0 to 1. */ qreal danceability() const; void setDanceability( qreal dance ); /** * The energy of this song, from 0 to 1. */ qreal energy() const; void setEnergy( qreal energy ); /** * The acousticness of this song, from 0 to 1. */ qreal acousticness() const; void setAcousticness( qreal acousticness ); /** * The speechiness of this song, from 0 to 1. */ qreal speechiness() const; void setSpeechiness( qreal speechiness ); /** * The liveness of this song, from 0 to 1. */ qreal liveness() const; void setLiveness( qreal liveness ); /** * The valence of this song, from 0 to 1. */ qreal valence() const; void setValence( qreal valence ); /// The following require additional fetching to read /// /** TODO: implement **/ /** * If you wish to use any of the more detailed track analysis data, * use this method to begin the fetch. One the returned QNetworkReply* * has emitted the finished() signal, call parseFullAnalysis. */ QNetworkReply* fetchFullAnalysis() const; /** * Parses the result of a fetchFullAnalysis() call. This contains * information such as mode, fadein/fadeout, confidence metrics, * and the division of the song into bars, beats, sections, and segments. */ void parseFullAnalysis( QNetworkReply* reply ) throw( ParseError ); /// The following methods *ALL REQUIRE THAT parseFullAnalysis be called first* /** * How long it took to analyze this track. */ qreal analysisTime() const; void setAnalysisTime( qreal time ); /** * The version of the analyzer used on this track. */ QString analyzerVersion() const; void setAnalyzerVersion( QString version ); /** * Detailed status information about the analysis */ QString detailedStatus() const; void setDetailedStatus( const QString& status ); /** * The status code of the analysis */ int analysisStatus() const; void setAnalysisStatus( int status ); /** * The timestamp of the analysis. */ qreal timestamp() const; void setTimestamp( qreal timestamp ); /** ECHONEST_EXPORT * The sample rate of the track. */ qreal sampleRate() const; void setSampleRate( qreal sampleRate ); /** * The end of the track's fade in in msec. */ qreal endOfFadeIn() const; void setEndOfFadeIn( qreal time ); /** * The start of the fade out in msec. */ qreal startOfFadeOut() const; void setStartOfFadeOut( qreal time ); /** * The confidence of the key item. */ qreal keyConfidence() const; void setKeyConfidence( qreal confidence ); /** * The confidence of the mode item. */ qreal modeConfidence() const; void setModeConfidence( qreal confidence ); /** * The confidence of the tempo item. */ qreal tempoConfidence() const; void setTempoConfidence( qreal confidence ); /** * The confidence of the time signature item. */ qreal timeSignatureConfidence() const; void setTimeSignatureConfidence( qreal confidence ); /** * The number of samples in this track. */ qint64 numSamples() const; void setNumSamples( qint64 num ); /** * The MD5 of the sample. */ QString sampleMD5() const; void setSampleMD5( const QString& md5 ); /** * List of bars that are in the track. */ BarList bars() const; void setBars( const BarList& bars ); /** * List of beats in the track. */ BeatList beats() const; void setBeats( const BeatList& beats ); /** * List of sections in the track. */ SectionList sections() const; void setSections( const SectionList& sections ); /** * List of tatums in the track */ TatumList tatums() const; void setTatums( const TatumList& tatums ); /** * List of segments in the track with associated acoustic data. */ SegmentList segments() const; void setSegments( const SegmentList& segments ); void setAnalysisUrl( const QUrl& analysisUrl ); private: QSharedDataPointer d; }; ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::AudioSummary& summary); } // namespace #endif libechonest-2.3.1/tests/GenreTest.cpp000644 001750 001750 00000015274 12465467260 020411 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2014 Leo Franchi * * Copyright (c) 2014 Stefan Derkits * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "GenreTest.h" #include "Genre.h" #include "Artist.h" #include "TypeInformation.h" #include "Config.h" #include #include using namespace Echonest; void GenreTest::initTestCase() { Config::instance()->setAPIKey( "JGJCRKWLXLBZIFAZB" ); } void GenreTest::testArtistsUrl() { Genre g; g.setName( QLatin1String( "indie folk" ) ); QNetworkReply* reply = g.fetchArtists(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/genre/artists?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=indie+folk&limit=false" ) ); g.setName( QLatin1String( "funk" ) ); reply = g.fetchArtists(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/genre/artists?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=funk&limit=false" ) ); } void GenreTest::testArtists() { Genre g; g.setName( QLatin1String( "bubblegum pop" ) ); QNetworkReply* reply = g.fetchArtists( ArtistInformation( ArtistInformation::Genre ) ); QEventLoop loop; qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Artists artists = Genre::parseArtists( reply ); qDebug() << artists; QVERIFY( artists.size() > 0 ); bool genreOK = true; foreach(const Echonest::Artist& a, artists) { bool contains = false; foreach(const Echonest::Genre& other, a.genres() ) { if (other.name() == g.name()) contains = true; } genreOK = genreOK && contains; } QVERIFY( genreOK ); } void GenreTest::testListUrl() { QNetworkReply* reply = Genre::fetchList(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/genre/list?api_key=JGJCRKWLXLBZIFAZB&format=xml" ) ); } void GenreTest::testList() { QNetworkReply* reply = Genre::fetchList( GenreInformation() , 2000 ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Genres genres = Genre::parseList( reply ); qDebug() << genres.size(); QVERIFY( genres.size() > 0 ); Genre g; g.setName( QLatin1String( "indie rock" ) ); bool contains = false; foreach( const Genre& other, genres ) if ( other.name() == g.name() ) contains = true; QVERIFY( contains ); } void GenreTest::testProfileUrl() { Genres genres; Genre g; g.setName( QLatin1String( "classic rock" ) ); genres.append( g ); GenreInformation information; information.setGenreInformationFlags( GenreInformation::Description | GenreInformation::Urls ); QNetworkReply* reply = Genre::fetchProfile( genres, information); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/genre/profile?api_key=JGJCRKWLXLBZIFAZB&format=xml&bucket=description&bucket=urls&name=classic+rock" ) ); Genre g1; g1.setName( QLatin1String( "psychedelic rock" ) ); genres.append( g1 ); reply = Genre::fetchProfile( genres, information); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/genre/profile?api_key=JGJCRKWLXLBZIFAZB&format=xml&bucket=description&bucket=urls&name=classic+rock&name=psychedelic+rock" ) ); } void GenreTest::testProfile() { GenreInformation information; information.setGenreInformationFlags( GenreInformation::Description | GenreInformation::Urls ); Genres genres; Genre g; g.setName( QLatin1String( "classic rock" ) ); genres.append( g ); Genre g1; g1.setName( QLatin1String( "psychedelic rock" ) ); genres.append( g1 ); QNetworkReply* reply = Genre::fetchProfile( genres, information); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Genres profiledGenres = Genre::parseProfile( reply ); QVERIFY( profiledGenres.size() == 2 ); Genre g3 = profiledGenres.at(0); Genre g4 = profiledGenres.at(1); QVERIFY( g3.name() == g.name() ); QVERIFY( g4.name() == g1.name() ); QVERIFY( g3.wikipediaUrl().toString().startsWith( QLatin1String("http://en.wikipedia.org/wiki/" ) ) ); } void GenreTest::testSearchUrl() { QNetworkReply* reply = Genre::fetchSearch( QLatin1String( "rock" ) ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/genre/search?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=rock" ) ); } void GenreTest::testSearch() { QNetworkReply* reply = Genre::fetchSearch( QLatin1String( "rock" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Genres genres = Genre::parseList( reply ); qDebug() << genres.size(); qDebug() << genres; QVERIFY( genres.size() > 0 ); bool contains = true; foreach( const Genre& genre, genres ) contains = contains && genre.name().contains( QLatin1String( "rock" ) ); QVERIFY( contains ); } void GenreTest::testLegacyHack() { QNetworkReply* reply = Genre::fetchList( GenreInformation() , 2000 ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVector< QString > genres = Artist::parseGenreList( reply ); qDebug() << genres.size(); QVERIFY( genres.size() > 0 ); } QTEST_MAIN(GenreTest) #include "GenreTest.moc" libechonest-2.3.1/src/Generator_p.h000644 001750 001750 00000003565 12465467260 020050 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef LIBECHONEST_GENERATOR_H #define LIBECHONEST_GENERATOR_H #include #include "CatalogUpdateEntry.h" #include namespace Echonest { namespace Generator { /** * Miscellaneous functions for generating JSON to send to The Echo Nest */ QByteArray catalogEntriesToJson( const CatalogUpdateEntries& items ); QByteArray catalogEntryToJson( const CatalogUpdateEntry& item ); QVariantList catalogEntriesToVariant( const CatalogUpdateEntries& items ); QVariant catalogEntryToVariant( const CatalogUpdateEntry& item ); } } #endif libechonest-2.3.1/000755 001750 001750 00000000000 12465467407 014635 5ustar00stefanstefan000000 000000 libechonest-2.3.1/src/CatalogArtist.h000644 001750 001750 00000004310 12465467260 020331 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CATALOG_ARTIST_H #define ECHONEST_CATALOG_ARTIST_H #include "echonest_export.h" #include "Artist.h" #include "CatalogItem.h" class CatalogArtistData; namespace Echonest { /** * An artist that includes some additional information returned when getting an artist from a catalog listing. */ class ECHONEST_EXPORT CatalogArtist : public Artist, public CatalogItem { public: CatalogArtist(); explicit CatalogArtist(const QString& name); CatalogArtist(const QByteArray& id, const QString& name); CatalogArtist(const Echonest::CatalogArtist& other); virtual ~CatalogArtist(); CatalogArtist& operator=( const CatalogArtist& other ); /** * The type of this catalog item: Artist. */ virtual CatalogTypes::Type type() const; }; typedef QVector< CatalogArtist > CatalogArtists; } Q_DECLARE_METATYPE( Echonest::CatalogArtist ) #endif libechonest-2.3.1/libechonest.kdev4000644 001750 001750 00000000064 12465467260 020070 0ustar00stefanstefan000000 000000 [Project] Manager=KDevCMakeManager Name=libechonest libechonest-2.3.1/src/Playlist_p.h000644 001750 001750 00000003307 12465467260 017715 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Song.h" #include #include class DynamicPlaylistData : public QSharedData { public: DynamicPlaylistData() {} DynamicPlaylistData(const DynamicPlaylistData& other) : QSharedData(other) { sessionId = other.sessionId; currentSong = other.currentSong; } QByteArray sessionId; Echonest::Song currentSong; }; libechonest-2.3.1/src/Catalog_p.h000644 001750 001750 00000004352 12465467260 017467 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CATALOG_P_H #define ECHONEST_CATALOG_P_H #include "CatalogArtist.h" #include "CatalogSong.h" #include "Util.h" #include class CatalogData : public QSharedData { public: CatalogData() : total( 0 ), resolved( 0 ) {} CatalogData( const CatalogData& other ) : QSharedData( other ) { name = other.name; id = other.id; type = other.type; total = other.total; resolved = other.resolved; songs = other.songs; artists = other.artists; } ~CatalogData() {} // Fields we always get in a catalog object QString name; QByteArray id; Echonest::CatalogTypes::Type type; int total; // Fields we sometimes get depending on the query int resolved; // pending_tickets Echonest::CatalogSongs songs; // either has songs, or artists Echonest::CatalogArtists artists; }; #endif libechonest-2.3.1/src/qjsonwrapper/Json.cpp000644 001750 001750 00000007174 12465467260 021602 0ustar00stefanstefan000000 000000 /* Copyright 2014, Uwe L. Korn * * 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. */ #include "Json.h" // Qt version specific includes #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) #include #include #else #include #include #include #endif namespace QJsonWrapper { QVariantMap qobject2qvariant( const QObject* object ) { #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) QVariantMap map; if ( object == NULL ) { return map; } const QMetaObject* metaObject = object->metaObject(); for ( int i = 0; i < metaObject->propertyCount(); ++i ) { QMetaProperty metaproperty = metaObject->property( i ); if ( metaproperty.isReadable() ) { map[ QLatin1String( metaproperty.name() ) ] = object->property( metaproperty.name() ); } } return map; #else return QJson::QObjectHelper::qobject2qvariant( object ); #endif } void qvariant2qobject( const QVariantMap& variant, QObject* object ) { #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) for ( QVariantMap::const_iterator iter = variant.begin(); iter != variant.end(); ++iter ) { QVariant property = object->property( iter.key().toLatin1() ); Q_ASSERT( property.isValid() ); if ( property.isValid() ) { QVariant value = iter.value(); if ( value.canConvert( property.type() ) ) { value.convert( property.type() ); object->setProperty( iter.key().toLatin1(), value ); } else if ( QString( QLatin1String("QVariant") ).compare( QLatin1String( property.typeName() ) ) == 0 ) { object->setProperty( iter.key().toLatin1(), value ); } } } #else QJson::QObjectHelper::qvariant2qobject( variant, object ); #endif } QVariant parseJson( const QByteArray& jsonData, bool* ok ) { #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson( jsonData, &error ); if ( ok != NULL ) { *ok = ( error.error == QJsonParseError::NoError ); } return doc.toVariant(); #else QJson::Parser p; return p.parse( jsonData, ok ); #endif } QByteArray toJson( const QVariant &variant, bool* ok ) { #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) QJsonDocument doc = QJsonDocument::fromVariant( variant ); if ( ok != NULL ) { *ok = !doc.isNull(); } return doc.toJson(); #else QJson::Serializer serializer; return serializer.serialize( variant, ok ); #endif } } libechonest-2.3.1/src/CatalogSong.h000644 001750 001750 00000004235 12465467260 017777 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CATALOG_SONG_H #define ECHONEST_CATALOG_SONG_H #include "echonest_export.h" #include "Song.h" #include "CatalogItem.h" class CatalogSongData; namespace Echonest { /** * A song that includes some additional information returned when getting a song from a catalog listing. */ class ECHONEST_EXPORT CatalogSong : public Song, public CatalogItem { public: CatalogSong(); CatalogSong(const QByteArray& id, const QString& title, const QByteArray& artistId, const QString& artistName); CatalogSong(const Echonest::CatalogSong& other); virtual ~CatalogSong(); CatalogSong& operator=( const CatalogSong& other ); /** * The type of this item: Song. */ virtual CatalogTypes::Type type() const; }; typedef QVector< CatalogSong > CatalogSongs; } Q_DECLARE_METATYPE( Echonest::CatalogSong ) #endif libechonest-2.3.1/src/000755 001750 001750 00000000000 12465467260 015421 5ustar00stefanstefan000000 000000 libechonest-2.3.1/src/Track.h000644 001750 001750 00000016374 12465467260 016651 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_TRACK_H #define ECHONEST_TRACK_H #include "AudioSummary.h" #include "echonest_export.h" #include "Util.h" #include #include #include #include #include #include "Config.h" class QNetworkReply; class TrackData; namespace Echonest { class Song; /** * Upload-based Echo Nest Track API. If you want to search The Echo Nest for songs, use the Song API. * If you want to upload your own files and retrieve the acoustic information about them, use this Track * class. You can also fetch acoustic information from a track if you have the Track ID or MD5 of the file. * * A Track encapsulates the audio analysis from The Echo Nest. * * This class is implicitly shared. * */ class ECHONEST_EXPORT Track { public: Track(); explicit Track( const QByteArray& id ); Track( const Track& other ); Track& operator=( const Track& track ); ~Track(); /** * The track's artist. */ QString artist() const; void setArtist( const QString& artist ); /** * The track's title. */ QString title() const; void setTitle( const QString& title ); /** * The Echo Nest artist ID for this track. */ QByteArray id() const; void setId( const QByteArray& id ); /** * The MD5 hash of the track. */ QByteArray md5() const; void setMD5( const QByteArray& md5 ); /** * The album name of this track. */ QString release() const; void setRelease( const QString& release ); /** * The MD5 hashsum of the audio data. */ QByteArray audioMD5() const; void setAudioMD5( const QByteArray& md5 ); /** * The analyzer version that was used in this track's analysis. */ QString analyzerVersion() const; void setAnalyzerVersion( const QString& analyzerVersion ); /** * The samplerate of the track */ int samplerate() const; void setSamplerate( int samplerate ); /** * The bitrate of the track */ int bitrate() const; void setBitrate( int ); /** * If this track is fetched from a tracks bucket of a song search, the following information * will be populated for some id spaces. */ /** * The catalog this track is from, if not a native Echo Nest track */ QString catalog() const; void setCatalog( const QString& catalog ); /** * The foreign id of this track, that is in the \c catalog catalog. */ QByteArray foreignId() const; void setForeignId( const QByteArray& id ); /** * The release image associated with this track */ QUrl releaseImage() const; void setReleaseImage( const QUrl& imgUrl ); /** * The preview url for this track, if it exists */ QUrl previewUrl() const; void setPreviewUrl( const QUrl& preview ); /** * The Echo Nest song associated with this track, if it exists */ Song song() const; void setSong( const Song& song ); /** * The analysis status */ Analysis::AnalysisStatus status() const; void setStatus( Analysis::AnalysisStatus ); /** * The full audio summary of the track. This contains information about the track's bars, * beats, sections, and detailed segment information as well as more metadata about the song's * acoustic properties. * * Information about how to interpret the results of the audio summary can be found here: * http://developer.echonest.com/docs/v4/_static/AnalyzeDocumentation_2.2.pdf * * NOTE: This will return a copy of the AudioSummary object, which * is implicitly shared. If you make modifications to the returned * summary, for example by calling parseFullAnalysis(), it will detach * and you will have to call setAudioSummary() to save the changes back * to this Song object. */ AudioSummary audioSummary() const; void setAudioSummary( const AudioSummary& summary ); /** * Get a track object from the md5 hash of a song's contents. * * Call parseProfile() to get the track itself once the * QNetworkReply() emits the finished() signal. */ static QNetworkReply* profileFromMD5( const QByteArray& md5 ); /** * Get a track object from an Echo Nest track id. * * Call parseProfile() to get the track itself once the * QNetworkReply() emits the finished() signal. */ static QNetworkReply* profileFromTrackId( const QByteArray& id ); /** * Upload a track to The Echo Nest for analysis. The file can either be * a local filetype and include the file data as a parameter, or a url to a file on the internet. * * When the QNetworkReply emits its finished() signal, you can call parseProfile() * to get the resulting Track object. Be sure to check the status of the new track, * as it might be 'pending', which means it is still being analyzed and must be asked * for again later. * * Note that in the case of uploading a local file, the data QByteArray must stay in scope for the * whole completion of the upload operation. */ static QNetworkReply* uploadLocalFile( const QUrl& localFile, const QByteArray& data, bool waitForResult = true ); static QNetworkReply* uploadURL( const QUrl& remoteURL, bool waitForResult = true ); /** * Analyze a previously uploaded track with the current version of the analyzer. * It can be referenced by either track ID or file md5. */ static QNetworkReply* analyzeTrackId( const QByteArray& id, bool wait = true ); static QNetworkReply* analyzeTrackMD5( const QByteArray& id, bool wait = true ); /** * Parse the result of a track request, and turn it into a * Track object. * * Call this function after the QNetworkReply* object returned * from the parse*, upload*, and analyze* emits its finished() signal */ static Track parseProfile( QNetworkReply* ) throw( ParseError ); private: QSharedDataPointer d; }; typedef QVector Tracks; ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Track& track); } // namespace #endif libechonest-2.3.1/.gitignore000644 001750 001750 00000000022 12465467260 016614 0ustar00stefanstefan000000 000000 *~ build/ .kdev4/ libechonest-2.3.1/src/TypeInformation.h000644 001750 001750 00000014234 12465467260 020725 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_TYPEINFORMATION_H #define ECHONEST_TYPEINFORMATION_H #include "echonest_export.h" #include #include #include namespace Echonest { class ArtistInformationPrivate; class SongInformationPrivate; class GenreInformationPrivate; /** * This class describes what artist information to return with a query. * * The default behaviour is NoInformation. */ class ECHONEST_EXPORT ArtistInformation { public: enum ArtistInformationFlag { NoInformation = 0x0000, Audio = 0x0001, Biographies = 0x0002, Blogs = 0x0004, Familiarity = 0x0008, Hotttnesss = 0x0010, Images = 0x0020, News = 0x0040, Reviews = 0x0080, Terms = 0x0100, Urls = 0x200, Videos = 0x0400, Genre = 0x0800 }; Q_DECLARE_FLAGS( ArtistInformationFlags, ArtistInformationFlag ) ArtistInformation(); ArtistInformation( ArtistInformationFlags flags ); ArtistInformation( ArtistInformationFlags flags, const QStringList& idSpaces ); ArtistInformation( const ArtistInformation& other ); ~ArtistInformation(); ArtistInformation& operator=( const ArtistInformation& typeInfo ); /** * The individual pieces of information to fetch for this artist. * * Use \c setIdSpaces to set an id space for this query. */ ArtistInformationFlags flags() const; void setArtistInformationFlags( ArtistInformationFlags flags ); /** * The id spaces to limit this to. Do not include the "id:" prefix. */ QStringList idSpaces() const; void setIdSpaces( const QStringList& idspaces ); private: ArtistInformationPrivate* d_ptr; Q_DECLARE_PRIVATE( ArtistInformation ) }; Q_DECLARE_OPERATORS_FOR_FLAGS( ArtistInformation::ArtistInformationFlags ) /** * This class describes what song information to return with a query. * * The default behaviour is NoInformation. */ class ECHONEST_EXPORT SongInformation { public: enum SongInformationFlag { AudioSummaryInformation = 0x001, Tracks = 0x002, Hotttnesss = 0x04, ArtistHotttnesss = 0x008, ArtistFamiliarity = 0x010, ArtistLocation = 0x020, SongType = 0x40, NoInformation = 0x800 }; Q_DECLARE_FLAGS( SongInformationFlags, SongInformationFlag ) SongInformation(); SongInformation( SongInformationFlags flags ); SongInformation( SongInformationFlags flags, const QStringList& idSpaces ); SongInformation( const SongInformation& other ); ~SongInformation(); SongInformation& operator=( const SongInformation& info ); /** * The individual pieces of information to fetch for this song. * If id spaces are desired,see \c setIdSpaces for more information. */ SongInformationFlags flags() const; void setSongInformationFlags( SongInformationFlags flags ); /** * The id spaces to limit this to. Do not include the "id:" prefix. */ QStringList idSpaces() const; void setIdSpaces( const QStringList& idspaces ); private: SongInformationPrivate* d_ptr; Q_DECLARE_PRIVATE( SongInformation ) }; Q_DECLARE_OPERATORS_FOR_FLAGS(SongInformation::SongInformationFlags) /** * This class describes what genre information to return with a query. * * The default behaviour is NoInformation. */ class ECHONEST_EXPORT GenreInformation { public: enum GenreInformationFlag { Description = 0x001, Urls = 0x002, NoInformation = 0x800 }; Q_DECLARE_FLAGS( GenreInformationFlags, GenreInformationFlag ) GenreInformation(); GenreInformation( GenreInformationFlags flags ); GenreInformation( const GenreInformation& other ); ~GenreInformation(); GenreInformation& operator=( const GenreInformation& info ); /** * The individual pieces of information to fetch for this song. */ GenreInformationFlags flags() const; void setGenreInformationFlags( GenreInformationFlags flags ); private: GenreInformationPrivate* d_ptr; Q_DECLARE_PRIVATE( GenreInformation ) }; Q_DECLARE_OPERATORS_FOR_FLAGS(GenreInformation::GenreInformationFlags) } Q_DECLARE_METATYPE( Echonest::ArtistInformation ) Q_DECLARE_METATYPE( Echonest::SongInformation ) Q_DECLARE_METATYPE( Echonest::GenreInformation ) #endif libechonest-2.3.1/src/Track.cpp000644 001750 001750 00000021061 12465467260 017171 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Track.h" #include "Track_p.h" #include "Config.h" #include "Song.h" #include "Parsing_p.h" #include #include Echonest::Track::Track() : d( new TrackData ) { } Echonest::Track::Track(const Echonest::Track& other) : d( other.d ) {} Echonest::Track::Track(const QByteArray& id) : d( new TrackData ) { d->id = id; } Echonest::Track::~Track() { } Echonest::Track& Echonest::Track::operator=(const Echonest::Track& track) { d = track.d; return *this; } QString Echonest::Track::artist() const { return d->artist; } void Echonest::Track::setArtist(const QString& artist) { d->artist = artist; } QString Echonest::Track::title() const { return d->title; } void Echonest::Track::setTitle(const QString& title) { d->title = title; } QByteArray Echonest::Track::id() const { return d->id; } void Echonest::Track::setId(const QByteArray& id) { d->id = id; } QByteArray Echonest::Track::md5() const { return d->md5; } void Echonest::Track::setMD5(const QByteArray& md5) { d->md5 = md5; } QString Echonest::Track::release() const { return d->release; } void Echonest::Track::setRelease(const QString& release) { d->release = release; } QString Echonest::Track::analyzerVersion() const { return d->analyzer_version; } void Echonest::Track::setAnalyzerVersion(const QString& analyzerVersion) { d->analyzer_version = analyzerVersion; } int Echonest::Track::bitrate() const { return d->bitrate; } void Echonest::Track::setBitrate(int bitrate) { d->bitrate = bitrate; } int Echonest::Track::samplerate() const { return d->samplerate; } void Echonest::Track::setSamplerate(int samplerate) { d->samplerate = samplerate; } QByteArray Echonest::Track::audioMD5() const { return d->audio_md5; } void Echonest::Track::setAudioMD5(const QByteArray& md5) { d->audio_md5 = md5; } Echonest::Analysis::AnalysisStatus Echonest::Track::status() const { return Echonest::statusToEnum( d->status ); } void Echonest::Track::setStatus( Echonest::Analysis::AnalysisStatus status ) { d->status = Echonest::statusToString( status ); } Echonest::AudioSummary Echonest::Track::audioSummary() const { return d->audio_summary; } void Echonest::Track::setAudioSummary( const Echonest::AudioSummary& summary ) { d->audio_summary = summary; } QString Echonest::Track::catalog() const { return d->catalog; } void Echonest::Track::setCatalog(const QString& catalog) { d->catalog = catalog; } QByteArray Echonest::Track::foreignId() const { return d->foreign_id; } void Echonest::Track::setForeignId(const QByteArray& id) { d->foreign_id = id; } QUrl Echonest::Track::previewUrl() const { return d->preview_url; } void Echonest::Track::setPreviewUrl(const QUrl& preview) { d->preview_url = preview; } QUrl Echonest::Track::releaseImage() const { return d->release_image; } void Echonest::Track::setReleaseImage(const QUrl& imgUrl) { d->release_image = imgUrl; } Echonest::Song Echonest::Track::song() const { return d->song; } void Echonest::Track::setSong(const Echonest::Song& song) { d->song = song; } QNetworkReply* Echonest::Track::profileFromTrackId( const QByteArray& id ) { QUrl url = Echonest::baseGetQuery( "track", "profile" ); urlAddQueryItem( url, QLatin1String( "id" ), QString::fromLatin1( id ) ); urlAddQueryItem( url, QLatin1String("bucket"), QLatin1String( "audio_summary" ) ); qDebug() << "Creating profileFromTrackId URL" << url; return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Track::profileFromMD5( const QByteArray& md5 ) { QUrl url = Echonest::baseGetQuery( "track", "profile" ); urlAddQueryItem( url, QLatin1String( "md5" ), QString::fromLatin1( md5 ) ); urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "audio_summary" ) ); qDebug() << "Creating profileFromMD5 URL" << url; return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Track::uploadLocalFile( const QUrl& localFile, const QByteArray& data, bool waitForResult ) { QUrl url = Echonest::baseGetQuery( "track", "upload" ); QFileInfo info( localFile.path() ); urlAddQueryItem( url, QLatin1String( "filetype" ), info.suffix() ); urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "audio_summary" ) ); urlAddQueryItem( url, QLatin1String( "wait" ), QLatin1String( waitForResult ? "true" : "false" ) ); QNetworkRequest request( url ); request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/octet-stream" ) ); // qDebug() << "Uploading local file to" << url; return Echonest::Config::instance()->nam()->post( request, data ); } QNetworkReply* Echonest::Track::uploadURL( const QUrl& remoteURL, bool waitForResult ) { QUrl url = Echonest::baseGetQuery( "track", "upload" ); urlAddQueryItem( url, QLatin1String( "url" ), remoteURL.toString() ); urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "audio_summary" ) ); urlAddQueryItem( url, QLatin1String( "wait" ), QLatin1String( waitForResult ? "true" : "false" ) ); qDebug() << "Uploading URL:" << url; QNetworkRequest req( url ); req.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/x-www-form-urlencoded" ) ); return Echonest::Config::instance()->nam()->post( req, QByteArray() ); } QNetworkReply* Echonest::Track::analyzeTrackId( const QByteArray& id, bool wait ) { QUrl url = Echonest::baseGetQuery( "track", "analyze" ); urlAddQueryItem( url, QLatin1String( "id" ), QString::fromLatin1( id ) ); urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "audio_summary" ) ); urlAddQueryItem( url, QLatin1String( "wait" ), QLatin1String( wait ? "true" : "false" ) ); qDebug() << "Creating analyzeTrackId URL" << url; return Echonest::doPost( url ); // return Echonest::Config::instance()->nam()->post( QNetworkRequest( url ), QByteArray() ); } QNetworkReply* Echonest::Track::analyzeTrackMD5( const QByteArray& md5, bool wait ) { QUrl url = Echonest::baseGetQuery( "track", "analyze" ); urlAddQueryItem( url, QLatin1String( "id" ), QString::fromLatin1( md5 ) ); urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "audio_summary" ) ); urlAddQueryItem( url, QLatin1String( "wait" ), QLatin1String( wait ? "true" : "false" ) ); qDebug() << "Creating analyzeTrackMD5 URL" << url; return Echonest::doPost( url ); // return Echonest::Config::instance()->nam()->post( QNetworkRequest( url ), QByteArray() ); } Echonest::Track Echonest::Track::parseProfile( QNetworkReply* finishedReply ) throw( Echonest::ParseError ) { QByteArray data = finishedReply->readAll(); qDebug() << data; Echonest::Parser::checkForErrors( finishedReply ); QXmlStreamReader xml( data ); Echonest::Parser::readStatus( xml ); Echonest::Track track = Echonest::Parser::parseTrack( xml ); finishedReply->deleteLater(); return track; } QDebug Echonest::operator<<(QDebug d, const Echonest::Track& track) { d << QString::fromLatin1( "Track(%1, %2, %3, %4)" ).arg( QLatin1String( track.id() ) ).arg( track.title() ).arg( track.artist() ).arg( track.release() ); return d.maybeSpace(); } libechonest-2.3.1/src/CatalogArtist.cpp000644 001750 001750 00000004130 12465467260 020664 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "CatalogArtist.h" #include "CatalogItem_p.h" Echonest::CatalogArtist::CatalogArtist() { } Echonest::CatalogArtist::CatalogArtist(const QString& name) : Artist( name ) { } Echonest::CatalogArtist::CatalogArtist(const QByteArray& id, const QString& name) : Artist(id, name) { } Echonest::CatalogArtist::CatalogArtist(const Echonest::CatalogArtist& other) : Artist( other ) , CatalogItem( other ) { } Echonest::CatalogArtist::~CatalogArtist() { } Echonest::CatalogArtist& Echonest::CatalogArtist::operator=(const Echonest::CatalogArtist& other) { Artist::operator=( other ); CatalogItem::operator=( other ); return *this; } Echonest::CatalogTypes::Type Echonest::CatalogArtist::type() const { return Echonest::CatalogTypes::Artist; } libechonest-2.3.1/AUTHORS000644 001750 001750 00000000037 12465467260 015702 0ustar00stefanstefan000000 000000 Leo Franchi libechonest-2.3.1/src/ArtistTypes.h000644 001750 001750 00000025612 12465467260 020073 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_ARTISTTYPES_H #define ECHONEST_ARTISTTYPES_H #include "echonest_export.h" #include "Util.h" #include #include class QDateTime; class QUrl; class AudioFileData; class BiographyData; class BlogData; class ArtistImageData; class ReviewData; class TermData; class VideoData; namespace Echonest{ /** * A link to an audio file related to an artist on the web. */ class ECHONEST_EXPORT AudioFile { public: AudioFile(); AudioFile( const AudioFile& other ); AudioFile& operator=( const AudioFile& artist ); ~AudioFile(); /** * The title of the audio file. */ QString title() const; void setTitle( const QString& title ); /** * The artist name that this audio file is related to. */ QString artist() const; void setArtist( const QString& ); /** * The URL pointing to the audio file. */ QUrl url() const; void setUrl( const QUrl& url ); /** * The length of the referenced audio file. */ qreal length() const; void setLength( qreal length ); /** * The link to the website where the audio file is from. */ QUrl link() const; void setLink( const QUrl& url ); /** * The date that this audio was posted. */ QDateTime date() const; void setDate( const QDateTime& ); /** * The released album name of this audio file. */ QString release() const; void setRelease( const QString& release ); /** * The unique identifier for this artist audio file. */ QByteArray id() const; void setId( const QByteArray& id ); private: QSharedDataPointer d; }; /** * A biography of an artist, including the full text content * of the biography itself. */ class ECHONEST_EXPORT Biography { public: Biography(); Biography( const Biography& other ); Biography& operator=( const Biography& biblio ); ~Biography(); /** * The URL to the biography. */ QUrl url() const; void setUrl( const QUrl& url ); /** * The text contents of the biography. May be very long. */ QString text() const; void setText( const QString& text ); /** * The site that this biography is from. */ QString site() const; void setSite( const QString& site ); /** * The license that this biography is licensed under. */ License license() const; void setLicense( const License& license ); private: QSharedDataPointer d; }; /** * A blog post about a certain artist or track. */ class ECHONEST_EXPORT Blog { public: Blog(); Blog( const Blog& other ); Blog& operator=( const Blog& other ); ~Blog(); /** * The name of the blog or news article. */ QString name() const; void setName( const QString& name ); /** * The URL to the blog post or news article. */ QUrl url() const; void setUrl( const QUrl& url ); /** * The date when the blog post or news article was posted. */ QDateTime datePosted() const; void setDatePosted( const QDateTime& date ); /** * The date when this blog post or news article was found by The Echo Nest. */ QDateTime dateFound() const; void setDateFound( const QDateTime& date ); /** * A short summary of the blog or article. */ QString summary() const; void setSummary( const QString& text ); /** * The unique identifier for this entry. */ QByteArray id() const; void setId( const QByteArray& id ); private: QSharedDataPointer d; }; /** * A news article about an artist. */ typedef Blog NewsArticle; /** * An image related to an artist. */ class ECHONEST_EXPORT ArtistImage { public: ArtistImage(); ArtistImage( const ArtistImage& other ); ArtistImage& operator=( const ArtistImage& img ); ~ArtistImage(); /** * The image url. */ QUrl url() const; void setUrl( const QUrl& url ); /** * The license that governs this image. */ License license() const; void setLicense( const License& license ); private: QSharedDataPointer d; }; /** * A review of an artist, album, or track. */ class ECHONEST_EXPORT Review { public: Review(); Review( const Review& other ); Review& operator=( const Review& other ); ~Review(); /** * The name of the review site. */ QString name() const; void setName( const QString& name ); /** * The URL to the review. */ QUrl url() const; void setUrl( const QUrl& url ); /** * The date when the review was posted. */ QDateTime dateReviewed() const; void setDateReviewed( const QDateTime& date ); /** * The date when this review was found and indexed by The Echo Nest */ QDateTime dateFound() const; void setDateFound( const QDateTime& date ); /** * A summary of the review. */ QString summary() const; void setSummary( const QString& text ); /** * The url to an image associated with the review, if it exists. */ QUrl imageUrl() const; void setImageUrl( const QUrl& imageUrl ); /** * The album being reviewed if it is an album review, including specific release info, if it exists. */ QString release() const; void setRelease( const QString& release ); /** * The unique identifier for this entry. */ QByteArray id() const; void setId( const QByteArray& id ); private: QSharedDataPointer d; }; /** * A term used to describe an artist or track. */ class ECHONEST_EXPORT Term { public: Term(); Term( const Term& other ); Term& operator=( const Term& img ); ~Term(); /** * The term name. */ QString name() const; void setName( const QString& name ); /** * The frequency that this term is mentioned in relation to the artist/track. */ qreal frequency() const; void setFrequency( qreal freq ); /** * The weight that The Echo Nest assigns to this term. */ qreal weight() const; void setWeight( qreal weight ); private: QSharedDataPointer d; }; /** * A link to a video related to an artist. */ class ECHONEST_EXPORT Video { public: Video(); Video( const Video& other ); Video& operator=( const Video& other ); ~Video(); /** * The title of the video */ QString title() const; void setTitle( const QString& title ); /** * The URL to the title. */ QUrl url() const; void setUrl( const QUrl& url ); /** * The site that the video is from. */ QString site() const; void setSite( const QString& site ); /** * The date when this video was found */ QDateTime dateFound() const; void setDateFound( const QDateTime& date ); /** * The url to an image associated with this video, if it exists. */ QUrl imageUrl() const; void setImageUrl( const QUrl& imageUrl ); /** * The unique identifier for this video. */ QByteArray id() const; void setId( const QByteArray& id ); private: QSharedDataPointer d; }; ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::AudioFile& artist); ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Biography& biblio); ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Blog& blog); ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::ArtistImage& img); ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Review& review); ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Term& term); ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Video& video); typedef QVector< AudioFile > AudioList; typedef QVector< Biography > BiographyList; typedef QVector< Blog > BlogList; typedef QVector< ArtistImage > ArtistImageList; typedef QVector< NewsArticle > NewsList; typedef QVector< Review > ReviewList; typedef QVector< Term > TermList; typedef QVector< Video > VideoList; } // namespace #endif libechonest-2.3.1/src/Parsing_p.h000644 001750 001750 00000015374 12465467260 017526 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_PARSING_P_H #define ECHONEST_PARSING_P_H #include "Config.h" #include "Song.h" #include "Artist.h" #include "Catalog.h" #include "Playlist.h" #include "Genre.h" #include class QNetworkReply; namespace Echonest { namespace Parser { /** * Internal helper parsing functions for QXmlStreamParser */ void checkForErrors( QNetworkReply* reply ) throw( ParseError ); // read the start element and then the status element, throwing // if the result code is not Success void readStatus( QXmlStreamReader& xml ) throw( ParseError ); // parses a block and turns them into a list of Song object QVector< Song > parseSongList( QXmlStreamReader& xml ) throw( ParseError ); // parses a block Song parseSong( QXmlStreamReader& xml ) throw( ParseError ); // parses a block ArtistLocation parseSongArtistLocation( QXmlStreamReader& xml ) throw( ParseError ); // parses a block Track parseTrack( QXmlStreamReader& xml ) throw( ParseError ); // parses a chunk when asking for a song with tracks bucket Tracks parseSongTrackBucket( QXmlStreamReader& xml ) throw( ParseError ); // parses an chunk AudioSummary parseAudioSummary( QXmlStreamReader& xml ) throw( ParseError ); // parses the json of the detailed audio summary void parseDetailedAudioSummary( QNetworkReply* reply, AudioSummary& summary ) throw( ParseError ); // parses a list of artists in an block Echonest::Artists parseArtists( QXmlStreamReader& xml ) throw( ParseError ); // parses the contents of an artist fetch result, expects to be positioned after the readStatus() call // it could be a profile query, in which case it has a bunch of different artist attributes // or it could be a single fetch, in which case it starts with result number and offset. // the results are saved back into the artist int parseArtistInfoOrProfile( QXmlStreamReader&, Echonest::Artist& artist ) throw( ParseError ); // parse the individual artist attributes void parseArtistInfo( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); // parse each type of artist attribute void parseAudio( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseBiographies( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseImages( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseNewsOrBlogs( QXmlStreamReader& xml, Echonest::Artist& artist, bool news = true ) throw( ParseError ); void parseReviews( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseTerms( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseUrls( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseArtistSong( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseVideos( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseForeignArtistIds( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); void parseArtistGenres( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); // parse a list of terms TermList parseTopTermList( QXmlStreamReader& xml ) throw( ParseError ); QVector< QString > parseTermList( QXmlStreamReader& xml ) throw( ParseError ); //parse a list of genres QVector< QString > parseGenreListStrings( QXmlStreamReader& xml ) throw( ParseError ); Echonest::Genres parseGenres( QXmlStreamReader& xml ) throw( ParseError ); Echonest::Genre parseGenre( QXmlStreamReader& xml ) throw( ParseError ); Artists parseArtistSuggestList( QXmlStreamReader& xml ) throw( ParseError ); License parseLicense( QXmlStreamReader& xml ) throw( ParseError ); QByteArray parsePlaylistSessionId( QXmlStreamReader& xml ) throw( ParseError ); SongList parseDynamicLookahead( QXmlStreamReader& xml ) throw( ParseError ); // Catalog functions Catalogs parseCatalogList( QXmlStreamReader& xml ) throw( ParseError ); Catalog parseCatalog( QXmlStreamReader& xml, bool justOne = false /* the catalog API is ugly :( */ ) throw( ParseError ); QList parseCatalogItems( QXmlStreamReader& xml ) throw( ParseError ); void parseCatalogRequestItem( QXmlStreamReader& xml, Echonest::CatalogArtist&, Echonest::CatalogSong& ) throw( ParseError ); void saveArtistList( Catalog& catalog, QList& ); void saveSongList( Catalog& catalog, QList& ); Echonest::CatalogStatus parseCatalogStatus( QXmlStreamReader& xml ) throw( ParseError ); Echonest::CatalogStatusItem parseTicketUpdateInfo( QXmlStreamReader& xml ) throw( ParseError ); QByteArray parseCatalogTicket( QXmlStreamReader& xml ) throw( ParseError ); Catalog parseNewCatalog( QXmlStreamReader& xml ) throw( ParseError ); // parses a chunk when asking for a song with tracks bucket in a catalog.read call Tracks parseCatalogSongTracks( QXmlStreamReader& xml ) throw( ParseError ); SessionInfo parseSessionInfo( QXmlStreamReader& xml ) throw( ParseError ); QVector< QString > parseRulesList( QXmlStreamReader& xml ) throw( ParseError ); // QVector< SessionItem > parseSessionSongItem( QXmlStreamReader& xml, const QString& type ) throw( ParseError ); } } #endif libechonest-2.3.1/tests/000755 001750 001750 00000000000 12465467260 015774 5ustar00stefanstefan000000 000000 libechonest-2.3.1/COPYING000644 001750 001750 00000043541 12465467260 015674 0ustar00stefanstefan000000 000000 NOTE! The GPL below is copyrighted by the Free Software Foundation, but the instance of code that it refers to (the kde programs) are copyrighted by the authors who actually wrote it. --------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. libechonest-2.3.1/tests/ArtistTest.cpp000644 001750 001750 00000077277 12465467260 020632 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "ArtistTest.h" #include "Config.h" #include "Artist.h" #include "TypeInformation.h" #include #include #include using namespace Echonest; void ArtistTest::initTestCase() { Config::instance()->setAPIKey( "JGJCRKWLXLBZIFAZB" ); } void ArtistTest::testBiographiesUrl() { Artist testArtist; testArtist.setId( "ARH6W4X1187B99274F" ); // radiohead QNetworkReply* reply = testArtist.fetchBiographies(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/biographies?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=ARH6W4X1187B99274F" ) ); reply = testArtist.fetchBiographies( QLatin1String( "echo-source") ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/biographies?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=ARH6W4X1187B99274F&license=echo-source" ) ); reply = testArtist.fetchBiographies( QLatin1String( "cc-by-sa" ), 1, 5 ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/biographies?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=ARH6W4X1187B99274F&results=1&start=5&license=cc-by-sa" ) ); } void ArtistTest::testBiographies() { Artist testArtist; testArtist.setId( "ARH6W4X1187B99274F" ); QNetworkReply* reply = testArtist.fetchBiographies(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.biographies().size(); QVERIFY( testArtist.biographies().size() > 5 ); // qDebug() << testArtist.biographies().at( 0 ).license().type << testArtist.biographies().at( 0 ).site() << testArtist.biographies().at( 0 ).text() << testArtist.biographies().at( 0 ).url(); try { testArtist = Artist(); testArtist.setName( QLatin1String( "Florence + The Machine" ) ); reply = testArtist.fetchBiographies(); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.biographies().size(); QVERIFY( testArtist.biographies().size() > 5 ); } catch ( Echonest::ParseError& e ) { QVERIFY( false ); } } void ArtistTest::testBlogsUrl() { Artist testArtist; testArtist.setName( QLatin1String( "FooArtist" ) ); testArtist.setId( "FooId" ); QNetworkReply* reply = testArtist.fetchBlogs(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/blogs?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=FooId" ) ); testArtist.setName( QLatin1String( "NoChange" ) ); reply = testArtist.fetchBlogs( true, 10 ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/blogs?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=FooId&results=10&high_relevance=true" ) ); } void ArtistTest::testBlogs() { Artist testArtist; testArtist.setName( QLatin1String( "Goo Goo Dolls") ); QNetworkReply* reply = testArtist.fetchBlogs( true, 5 ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.blogs().size(); QVERIFY( testArtist.blogs().size() == 5 ); if( !testArtist.blogs().isEmpty() ) qDebug() << testArtist.blogs().at(0).dateFound() << testArtist.blogs().at(0).datePosted() << testArtist.blogs().at(0).id() << testArtist.blogs().at(0).name() << testArtist.blogs().at(0).summary() << testArtist.blogs().at(0).url(); } void ArtistTest::testFamiliarityUrl() { Artist testArtist; testArtist.setId( "FooId" ); QNetworkReply* reply = testArtist.fetchFamiliarity(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/familiarity?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=FooId" ) ); } void ArtistTest::testFamiliarity() { Artist testArtist; testArtist.setName( QLatin1String( "matchbox twenty" ) ); QNetworkReply* reply = testArtist.fetchFamiliarity(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.familiarity(); QVERIFY( testArtist.familiarity() != -1 ); } void ArtistTest::testHotttnesssUrl() { Artist testArtist; testArtist.setName( QLatin1String( "FooArtist" ) ); QNetworkReply* reply = testArtist.fetchHotttnesss(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/hotttnesss?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=FooArtist" ) ); } void ArtistTest::testHotttnesss() { Artist testArtist; testArtist.setName( QLatin1String( "matchbox twenty" ) ); QNetworkReply* reply = testArtist.fetchHotttnesss(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.hotttnesss(); QVERIFY( testArtist.hotttnesss() != -1 ); } void ArtistTest::testImagesUrl() { Artist testArtist; testArtist.setName( QLatin1String( "FooArtist" ) ); QNetworkReply* reply = testArtist.fetchImages(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/images?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=FooArtist" ) ); reply = testArtist.fetchImages( QLatin1String( "echo-source") ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/images?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=FooArtist&license=echo-source" ) ); reply = testArtist.fetchImages( QLatin1String( "cc-by-sa" ), 1, 5 ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/images?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=FooArtist&results=1&start=5&license=cc-by-sa" ) ); } void ArtistTest::testImages() { Artist testArtist; testArtist.setName( QLatin1String( "Goo Goo Dolls" ) ); QNetworkReply* reply = testArtist.fetchImages( QString(), 10 ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.images().size(); QVERIFY( testArtist.images().size() == 10 ); qDebug() << testArtist.images().at( 0 ).license().type << testArtist.images().at( 0 ).url(); } void ArtistTest::testNewsUrl() { Artist testArtist; testArtist.setName( QLatin1String( "FooArtist" ) ); testArtist.setId( "FooId" ); QNetworkReply* reply = testArtist.fetchNews(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/news?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=FooId" ) ); testArtist.setName( QLatin1String( "NoChange" ) ); reply = testArtist.fetchNews( true, 10 ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/news?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=FooId&results=10&high_relevance=true" ) ); } void ArtistTest::testNews() { Artist testArtist; testArtist.setName( QLatin1String( "Goo Goo Dolls") ); QNetworkReply* reply = testArtist.fetchNews( true ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.news().size(); // QVERIFY( testArtist.audio().size() == 5 ); if( !testArtist.news().isEmpty() ) qDebug() << testArtist.news().at(0).dateFound() << testArtist.news().at(0).datePosted() << testArtist.news().at(0).id() << testArtist.news().at(0).name() << testArtist.news().at(0).summary() << testArtist.news().at(0).url(); } void ArtistTest::testProfileUrl() { Artist testArtist; testArtist.setName( QLatin1String( "ReallyGoodArtist" ) ); QNetworkReply* reply = testArtist.fetchProfile( ArtistInformation( ArtistInformation::Familiarity | ArtistInformation::Videos ) ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/profile?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=ReallyGoodArtist&bucket=familiarity&bucket=video" ) ); ArtistInformation info( ArtistInformation::Biographies | ArtistInformation::News | ArtistInformation::Reviews | ArtistInformation::Terms | ArtistInformation::Urls ); info.setIdSpaces( QStringList() << QLatin1String( "musicbrainz" ) ); reply = testArtist.fetchProfile( info ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/profile?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=ReallyGoodArtist&bucket=biographies&bucket=news&bucket=reviews&bucket=terms&bucket=urls&bucket=id:musicbrainz" ) ); info.setArtistInformationFlags( ArtistInformation::Blogs | ArtistInformation::Hotttnesss | ArtistInformation::Images ); info.setIdSpaces( QStringList() << QLatin1String( "7digital" ) << QLatin1String( "playme" ) ); reply = testArtist.fetchProfile( info ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/profile?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=ReallyGoodArtist&bucket=blogs&bucket=hotttnesss&bucket=images&bucket=id:7digital&bucket=id:playme" ) ); } void ArtistTest::testProfile() { Artist testArtist; testArtist.setName( QLatin1String( "The American Dollar" ) ); QNetworkReply* reply = testArtist.fetchProfile( ArtistInformation( ArtistInformation::Hotttnesss | ArtistInformation::Familiarity | ArtistInformation::Videos ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.audio() << testArtist.hotttnesss() << testArtist.familiarity() << testArtist.videos(); } void ArtistTest::testReviewsUrl() { Artist testArtist; testArtist.setId( "FooId" ); QNetworkReply* reply = testArtist.fetchReviews(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/reviews?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=FooId" ) ); } void ArtistTest::testReviews() { Artist testArtist; testArtist.setName( QLatin1String( "Balmorhea" ) ); QNetworkReply* reply = testArtist.fetchReviews( 7 ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.reviews().size(); QVERIFY( testArtist.reviews().size() == 7 ); if( !testArtist.reviews().isEmpty() ) { qDebug() << testArtist.reviews().at(0).dateFound() << testArtist.reviews().at(0).dateReviewed() << testArtist.reviews().at(0).id() << testArtist.reviews().at(0).imageUrl() << testArtist.reviews().at(0).name() << testArtist.reviews().at(0).release() << testArtist.reviews().at(0).summary() << testArtist.reviews().at(0).url(); } } void ArtistTest::testSearchUrl() { Artist::SearchParams params; params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "emo^2" ) ) ); params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "female vocalist^2" ) ) ); params.append( Artist::SearchParamEntry( Artist::Mood, QLatin1String( "happy^2" ) ) ); params.append( Artist::SearchParamEntry( Artist::FuzzyMatch, true ) ); QNetworkReply* searchResult = Artist::search( params, ArtistInformation( ArtistInformation::Familiarity | ArtistInformation::Hotttnesss ) ); qDebug() << searchResult->url().toString(); QCOMPARE( searchResult->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/artist/search?api_key=JGJCRKWLXLBZIFAZB&format=xml&description=emo^2&description=female+vocalist^2&mood=happy^2&fuzzy_match=true&limit=false&bucket=familiarity&bucket=hotttnesss" ) ); params.clear(); params.append( Artist::SearchParamEntry( Artist::MaxFamiliarity, 12.221 ) ); params.append( Artist::SearchParamEntry( Artist::MinHotttnesss, 0.52 ) ); params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "alternative rock" ) ) ); params.append( Artist::SearchParamEntry( Artist::FuzzyMatch, true ) ); searchResult = Artist::search( params, ArtistInformation( ArtistInformation::Audio | ArtistInformation::Videos ), true ); QCOMPARE( searchResult->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/artist/search?api_key=JGJCRKWLXLBZIFAZB&format=xml&max_familiarity=12.221&min_hotttnesss=0.52&description=alternative+rock&fuzzy_match=true&limit=true&bucket=video" ) ); } void ArtistTest::testSearch() { Artist::SearchParams params; params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "emo" ) ) ); params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "female vocalist" ) ) ); params.append( Artist::SearchParamEntry( Artist::Mood, QLatin1String( "happy" ) ) ); params.append( Artist::SearchParamEntry( Artist::FuzzyMatch, true ) ); QNetworkReply* searchResult = Artist::search( params, ArtistInformation( ArtistInformation::Familiarity | ArtistInformation::Hotttnesss ) ); qDebug() << "searchResult:" << searchResult->url().toString(); QEventLoop loop; loop.connect( searchResult, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::Artists artists = Echonest::Artist::parseSearch( searchResult ); qDebug() << artists.size(); qDebug() << artists; QVERIFY( artists.size() > 0 ); Q_FOREACH( const Artist& artist, artists ) { QVERIFY( artist.familiarity() >= 0 ); QVERIFY( artist.hotttnesss() >= 0 ); } artists.clear(); params.clear(); params.append( Artist::SearchParamEntry( Artist::MaxFamiliarity, .95 ) ); params.append( Artist::SearchParamEntry( Artist::MinFamiliarity, .7 ) ); params.append( Artist::SearchParamEntry( Artist::MinHotttnesss, 0.72 ) ); params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "alternative rock" ) ) ); params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "stadium rock" ) ) ); params.append( Artist::SearchParamEntry( Artist::FuzzyMatch, true ) ); searchResult = Artist::search( params, ArtistInformation( ArtistInformation::Familiarity | ArtistInformation::Hotttnesss | ArtistInformation::News | ArtistInformation::Blogs ) ); qDebug() << "Querying:" << searchResult->url().toString(); QEventLoop loop2; loop2.connect( searchResult, SIGNAL(finished()), SLOT(quit()) ); loop2.exec(); artists = Echonest::Artist::parseSearch( searchResult ); qDebug() << artists.size(); qDebug() << artists; QVERIFY( artists.size() > 0 ); int count = 0; Q_FOREACH( const Artist& artist, artists ) { QVERIFY( artist.familiarity() >= 0 ); QVERIFY( artist.hotttnesss() >= 0 ); QVERIFY( artist.news().size() > 0 ); QVERIFY( artist.blogs().size() > 0 ); count++; } ArtistInformation info( ArtistInformation::Familiarity | ArtistInformation::Hotttnesss ); info.setIdSpaces( QStringList() << QLatin1String( "musicbrainz" ) ); params.clear(); params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "alternative rock" ) ) ); params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "stadium rock" ) ) ); params.append( Artist::SearchParamEntry( Artist::Description, QLatin1String( "melodic^2" ) ) ); searchResult = Artist::search( params, info, true ); qDebug() << "reply:" << searchResult->url().toString(); QEventLoop loop3; loop3.connect( searchResult, SIGNAL(finished()), SLOT(quit()) ); loop3.exec(); artists = Echonest::Artist::parseSearch( searchResult ); foreach( const Echonest::Artist& artist, artists ) { QVERIFY( !artist.foreignIds().isEmpty() ); foreach( const Echonest::ForeignId& id, artist.foreignIds() ) { QVERIFY( !id.catalog.isEmpty() ); QVERIFY( !id.foreign_id.isEmpty() ); } // qDebug() << "foreign ids:" << artist.foreignIds(); } // try limiting it to a catalog info.setIdSpaces( QStringList() << QLatin1String( "CAXBXBZ12BF92A9AC2" ) ); // artist catalog with 2 artists searchResult = Artist::search( params, info, true ); qDebug() << "reply:" << searchResult->url().toString(); loop3.connect( searchResult, SIGNAL(finished()), SLOT(quit()) ); loop3.exec(); artists = Echonest::Artist::parseSearch( searchResult ); foreach( const Echonest::Artist& artist, artists ) { foreach( const Echonest::ForeignId& id, artist.foreignIds() ) { QVERIFY( !id.catalog.isEmpty() ); QVERIFY( id.foreign_id.contains( QLatin1String( "CAXBXBZ12BF92A9AC2:artist:" ) ) ); } } } void ArtistTest::testSimilarUrl() { Artist::SearchParams params; params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "The Beatles" ) ) ); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Rilo Kiley" ) ) ); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Queen" ) ) ); params.append( Artist::SearchParamEntry( Artist::MinHotttnesss, 0.5 ) ); QNetworkReply* reply = Artist::fetchSimilar( params, ArtistInformation( ArtistInformation::Hotttnesss | ArtistInformation::Familiarity ) ); qDebug() << reply->url().toString(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/similar?api_key=JGJCRKWLXLBZIFAZB&format=xml&bucket=familiarity&bucket=hotttnesss&name=The+Beatles&name=Rilo+Kiley&name=Queen&min_hotttnesss=0.5" ) ); params.clear(); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Devo" ) ) ); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "The New Pornographers" ) ) ); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Lady Gaga" ) ) ); params.append( Artist::SearchParamEntry( Artist::MinFamiliarity, 0.5 ) ); reply = Artist::fetchSimilar( params, ArtistInformation( ArtistInformation::Biographies | ArtistInformation::News ), 10 ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/similar?api_key=JGJCRKWLXLBZIFAZB&format=xml&bucket=biographies&bucket=news&results=10&name=Devo&name=The+New+Pornographers&name=Lady+Gaga&min_familiarity=0.5" ) ); } void ArtistTest::testSimilar() { Artist::SearchParams params; params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "The Beatles" ) ) ); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Rilo Kiley" ) ) ); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Queen" ) ) ); params.append( Artist::SearchParamEntry( Artist::MinHotttnesss, 0.5 ) ); QNetworkReply* reply = Artist::fetchSimilar( params, ArtistInformation( ArtistInformation::Hotttnesss | ArtistInformation::Familiarity ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::Artists artists = Echonest::Artist::parseSimilar( reply ); qDebug() << artists.size(); qDebug() << artists; QVERIFY( artists.size() > 0 ); Q_FOREACH( const Artist& artist, artists ) { QVERIFY( artist.familiarity() >= 0 ); QVERIFY( artist.hotttnesss() >= 0 ); } artists.clear(); params.clear(); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Devo" ) ) ); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Florence + The Machine" ) ) ); params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "Lady Gaga" ) ) ); params.append( Artist::SearchParamEntry( Artist::MinFamiliarity, 0.5 ) ); reply = Artist::fetchSimilar( params, ArtistInformation( ArtistInformation::Biographies | ArtistInformation::News | ArtistInformation::Videos ), 10 ); qDebug() << reply->url().toString(); QEventLoop loop2; loop2.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop2.exec(); artists = Echonest::Artist::parseSimilar( reply ); qDebug() << artists.size(); qDebug() << artists; QVERIFY( artists.size() == 10 ); Q_FOREACH( const Artist& artist, artists ) { QVERIFY( artist.biographies().size() >= 0 ); QVERIFY( artist.news().size() >= 0 ); QVERIFY( artist.videos().size() > 0 ); } } void ArtistTest::testSongsUrl() { Artist testArtist; testArtist.setId( "DummyDudeID" ); QNetworkReply* reply = testArtist.fetchSongs(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/songs?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=DummyDudeID" ) ); reply = testArtist.fetchSongs( 20, 5 ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/songs?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=DummyDudeID&results=20&start=5" ) ); } void ArtistTest::testSongs() { Artist testArtist; testArtist.setName( QLatin1String( "Balmorhea" ) ); QNetworkReply* reply = testArtist.fetchSongs(); qDebug() << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.songs().size(); QVERIFY( testArtist.songs().size() > 0 ); QVERIFY( !testArtist.songs().at(0).title().isEmpty() ); QVERIFY( !testArtist.songs().at(0).id().isEmpty() ); qDebug() << testArtist.songs(); reply = testArtist.fetchSongs( 10, 5 ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); qDebug() << testArtist.songs().size(); QVERIFY( testArtist.songs().size() == 10 ); qDebug() << testArtist.songs(); // TODO support ID spaces } void ArtistTest::testTopHotttUrl() { QNetworkReply* reply = Artist::topHottt( ArtistInformation( ArtistInformation::Hotttnesss ) ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/top_hottt?api_key=JGJCRKWLXLBZIFAZB&format=xml&bucket=hotttnesss&limit=false" ) ); } void ArtistTest::testTerms() { Artist testArtist; testArtist.setName( QLatin1String( "Balmorhea" ) ); QNetworkReply* reply = testArtist.fetchTerms(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); QVERIFY( testArtist.terms().size() > 0 ); qDebug() << testArtist.terms(); reply = testArtist.fetchTerms( Echonest::Artist::Weight ); QEventLoop loop2; loop2.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop2.exec(); testArtist.parseProfile( reply ); QVERIFY( testArtist.terms().size() > 0 ); qDebug() << testArtist.terms(); } void ArtistTest::testTermsUrl() { Artist testArtist; testArtist.setId( "DummyDudeID" ); QNetworkReply* reply = testArtist.fetchTerms(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/terms?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=DummyDudeID&sort=frequency" ) ); reply = testArtist.fetchTerms( Echonest::Artist::Weight ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/terms?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=DummyDudeID&sort=weight" ) ); } void ArtistTest::testListTerms() { QNetworkReply* reply = Artist::listTerms( QLatin1String( "style" ) ); qDebug() << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVector< QString > terms = Artist::parseTermList( reply ); qDebug() << terms; QVERIFY( terms.size() > 0 ); reply = Artist::listTerms( QLatin1String( "mood" ) ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); terms = Artist::parseTermList( reply ); qDebug() << terms; QVERIFY( terms.size() > 0 ); } void ArtistTest::testSuggest() { QNetworkReply* reply = Artist::suggest( QLatin1String( "tallest ma" ), 12 ); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/artist/suggest?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=tallest+ma&results=12" ) ); qDebug() << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Artists artists = Artist::parseSuggest( reply ); qDebug() << "Got suggestions:" << artists; QCOMPARE( artists.count(), 2 ); QCOMPARE( artists.first().name(), QLatin1String( "The Tallest Man On Earth" ) ); QCOMPARE( artists.first().id(), QByteArray( "AREDHSB1187FB57583" ) ); reply = Artist::suggest( QLatin1String( "matchbox 20" ), 12 ); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/artist/suggest?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=matchbox+20&results=12" ) ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); artists = Artist::parseSuggest( reply ); qDebug() << "Got suggestions:" << artists; QCOMPARE( artists.size(), 1 ); reply = Artist::suggest( QLatin1String( "tallest mangggggg" ), 12 ); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/artist/suggest?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=tallest+mangggggg&results=12" ) ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); artists = Artist::parseSuggest( reply ); qDebug() << "Got suggestions:" << artists; QCOMPARE( artists.size(), 0 ); } void ArtistTest::testTopHottt() { QNetworkReply* reply = Artist::topHottt(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Artists artists = Artist::parseTopHottt( reply ); qDebug() << artists.size() << artists; QVERIFY( artists.size() > 0 ); reply = Artist::topHottt( ArtistInformation( ArtistInformation::Hotttnesss | ArtistInformation::Familiarity ), 20 ); QEventLoop loop2; loop2.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop2.exec(); artists = Artist::parseTopHottt( reply ); qDebug() << artists.size() << artists; QVERIFY( artists.size() == 20 ); Q_FOREACH( const Artist& artist, artists ) { QVERIFY( artist.familiarity() > 0 ); QVERIFY( artist.hotttnesss() > 0 ); } } void ArtistTest::testTopTermsUrl() { QNetworkReply* reply = Artist::topTerms( 5 ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/top_terms?api_key=JGJCRKWLXLBZIFAZB&format=xml&results=5" ) ); } void ArtistTest::testTopTerms() { QNetworkReply* reply = Artist::topTerms( 5 ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); TermList terms = Artist::parseTopTerms( reply ); qDebug() << terms.size() << terms; QVERIFY( terms.size() == 5 ); } void ArtistTest::testTwitterUrl() { Artist testArtist; testArtist.setId( "ARFC93D1187FB49D2E" ); // Arcade Fire QNetworkReply* reply = testArtist.fetchTwitter(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/twitter?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=ARFC93D1187FB49D2E" ) ); } void ArtistTest::testTwitter() { Artist testArtist; testArtist.setId( "ARFC93D1187FB49D2E" ); // Arcade Fire QNetworkReply* reply = testArtist.fetchTwitter(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); QVERIFY( testArtist.twitter() == QLatin1String( "arcadefire" ) ); } void ArtistTest::testUrlsUrl() { Artist testArtist; testArtist.setName( QLatin1String( "Goo Goo Dolls" ) ); QNetworkReply* reply = testArtist.fetchUrls(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/urls?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=Goo+Goo+Dolls" ) ); } void ArtistTest::testUrls() { Artist testArtist; testArtist.setName( QLatin1String( "Balmorhea" ) ); QNetworkReply* reply = testArtist.fetchUrls(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); QVERIFY( !testArtist.lastFmUrl().isEmpty() ); QVERIFY( !testArtist.myspaceUrl().isEmpty() ); QVERIFY( !testArtist.musicbrainzUrl().isEmpty() ); } void ArtistTest::testVideosUrl() { Artist testArtist; testArtist.setId( "DummyDudeID" ); QNetworkReply* reply = testArtist.fetchVideo(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/video?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=DummyDudeID" ) ); testArtist.setId( QByteArray() ); testArtist.setName( QLatin1String( "OHAI ARTIST" ) ); reply = testArtist.fetchVideo( 10, 5 ); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/artist/video?api_key=JGJCRKWLXLBZIFAZB&format=xml&name=OHAI+ARTIST&results=10&start=5" ) ); } void ArtistTest::testVideos() { Artist testArtist; testArtist.setName( QLatin1String( "Balmorhea" ) ); QNetworkReply* reply = testArtist.fetchVideo(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); testArtist.parseProfile( reply ); QVERIFY( testArtist.videos().size() > 0 ); qDebug() << testArtist.videos(); reply = testArtist.fetchVideo( 5, 2 ); QEventLoop loop2; loop2.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop2.exec(); testArtist.parseProfile( reply ); QVERIFY( testArtist.videos().size() > 0 ); qDebug() << testArtist.videos(); } void ArtistTest::testUrlsFix() { Artist::SearchParams params; params.append( Artist::SearchParamEntry( Artist::Name, QLatin1String( "The Beatles" ) ) ); QNetworkReply* reply = Artist::fetchSimilar( params, ArtistInformation( ArtistInformation::Urls ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::Artists artists = Echonest::Artist::parseSimilar( reply ); QVERIFY( artists.size() > 0 ); Q_FOREACH( const Artist& artist, artists ) { QVERIFY( !artist.lastFmUrl().isEmpty() ); QVERIFY( !artist.musicbrainzUrl().isEmpty() ); } } QTEST_MAIN(ArtistTest) #include "ArtistTest.moc" libechonest-2.3.1/src/Catalog.h000644 001750 001750 00000017555 12465467260 017161 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CATALOG_H #define ECHONEST_CATALOG_H #include "Artist.h" #include "CatalogUpdateEntry.h" #include "echonest_export.h" #include "Song.h" #include "TypeInformation.h" #include "Util.h" #include #include #include "CatalogSong.h" #include "CatalogArtist.h" class QNetworkReply; class CatalogData; namespace Echonest { class Catalog; typedef QVector< Catalog > Catalogs; class ECHONEST_EXPORT Catalog { public: Catalog(); explicit Catalog( const QByteArray& id ); Catalog( const Catalog& ); virtual ~Catalog(); Catalog& operator=( const Catalog& ); /// Basic information about the catalog /** * The name of this catalog. */ QString name() const; void setName( const QString& name ); /** * The id of this catalog. */ QByteArray id() const; void setId( const QByteArray& id ); /** * The type of this catalog. */ CatalogTypes::Type type() const; void setType( CatalogTypes::Type type ); /** * The total number of items in this catalog. */ int total() const; void setTotal( int total ); /// The following fields only have data if the appropriate parse* methods have been called /** * The number of resolved items in the catalog. */ int resolved() const; void setResolved( int resolved ); /** * The number of pending tickets still to be resolved */ int pendingTickets() const; void setPendingTickets( int pending ); /** * The songs in this catalog, if this is a song catalog. */ CatalogSongs songs() const; void setSongs( const CatalogSongs& songs ); /** * The artists in this catalog, if it is an artist catalog. */ CatalogArtists artists() const; void setArtists( const CatalogArtists& artists ); /** * Update this catalog with the given items. Each item has an associated action, default is Update. * Call parseTicket() to access the result ticket from this call. * * See more information about this api call at http://developer.echonest.com/docs/v4/catalog.html#update * * Requires catalog id. * * \param entries The list of entries to update the catalog with. */ QNetworkReply* update( const CatalogUpdateEntries& entries ) const; /** * Get basic information on a catalog. Only requires one of catalog id or name. */ QNetworkReply* profile() const; /** * Fetch the full list of data from this catalog. It is possible to specify specific audio * information that you wish to have included with each item. Use the appropriate artist- or * song-specific method calls in order to achieve this. * * \param info The list of desired information to be included with each item. * \param results How many results to return in total * \param start The index of the first result */ QNetworkReply* readArtistCatalog( ArtistInformation info = ArtistInformation(), int results = 30, int start = -1 ) const; QNetworkReply* readSongCatalog( SongInformation info = SongInformation(), int results = 30, int start = -1 ) const; /** * Deletes this catalog from The Echo Nest. Only the API key used to create a catalog can delete it. */ QNetworkReply* deleteCatalog() const; /** * Create a new catalog with the given name and type. * * Parse the finished QNetworkReply with parseCreate() * * \param name The name of the catalog to create * \param type The type of the catalog to create */ static QNetworkReply* create( const QString& name, CatalogTypes::Type type ); /** * Returns a list of catalogs created with this key. */ static QNetworkReply* list( int results = 30, int start = -1 ); /** * Creates a new catalog with the given items. * See more information about this api call at http://developer.echonest.com/docs/v4/catalog.html#update * * Call parseTicket() to access the result ticket from this call. * * \param entries The list of entries to populate the catalog with. * */ static QNetworkReply* updateAndCreate( const CatalogUpdateEntries& entries ); /** * Checks the status of a catalog operation given a catalog ticket. * * Parse the result with parseStatus() * * \param ticket The catalog ticket returned from an update() or updateAndCreate() call */ static QNetworkReply* status( const QByteArray& ticket ); /** * Parses the result of a status call, returning the status information along with information on * item resolution if available. */ static CatalogStatus parseStatus( QNetworkReply* ) throw( Echonest::ParseError ); /** * Parses the result of a profile() call. Saves the data to this catalog object. */ void parseProfile( QNetworkReply* ) throw( Echonest::ParseError ); /** * Parses the result of the read*Catalog() calls. Saves the catalog data to this object. */ void parseRead( QNetworkReply * ) throw( Echonest::ParseError ); /** * Parse the result of a delete call. Will throw if the catalog was not successfully deleted, * and returns the name/id pair. * * \return QPair of catalogName, catalogId that was just deleted. */ QPair< QString, QByteArray > parseDelete( QNetworkReply* ) throw( Echonest::ParseError ); /** * Parse the result of the list() API call. Will return a list of catalogs---each catalog only * has id, name, type, and total tracks information. */ static Catalogs parseList( QNetworkReply* ) throw( Echonest::ParseError ); /** * Parse the result of a catalog call. The calls return a ticket that can be used to check the status * of the call with status() */ static QByteArray parseTicket( QNetworkReply* ) throw( Echonest::ParseError ); /** * Parse the result of a create() call. */ static Catalog parseCreate( QNetworkReply* reply ) throw( Echonest::ParseError ); private: static QNetworkReply* updatePrivate( QUrl&, const CatalogUpdateEntries& entries ); QNetworkReply* readPrivate( QUrl& url, int results, int start ) const; static void addLimits( QUrl&, int results, int start ); QSharedDataPointer< CatalogData > d; }; ECHONEST_EXPORT QDebug operator<<(QDebug d, const Catalog &catalog); } Q_DECLARE_METATYPE( Echonest::Catalog ) #endif libechonest-2.3.1/tests/CatalogTest.cpp000644 001750 001750 00000033534 12465467260 020722 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "CatalogTest.h" #include "Config.h" #include "Catalog.h" #include "TypeInformation.h" #include #include void CatalogTest::initTestCase() { Echonest::Config::instance()->setAPIKey( "JGJCRKWLXLBZIFAZB" ); } void CatalogTest::testList() { QNetworkReply* reply = Echonest::Catalog::list(); qDebug() << reply->url().toString(); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/tasteprofile/list?api_key=JGJCRKWLXLBZIFAZB&format=xml" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::Catalogs catalogs = Echonest::Catalog::parseList( reply ); qDebug() << catalogs.size(); qDebug() << catalogs; QVERIFY( !catalogs.isEmpty() ); // Now limit it to just 1, but the second one. reply = Echonest::Catalog::list( 1, 1 ); qDebug() << reply->url().toString(); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/tasteprofile/list?api_key=JGJCRKWLXLBZIFAZB&format=xml&results=1&start=1" ) ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); catalogs = Echonest::Catalog::parseList( reply ); qDebug() << catalogs.size(); QCOMPARE( catalogs.size(), 1 ); } void CatalogTest::testProfile() { Echonest::Catalog c( "CAWRKLJ12BF92BC7C3" ); QNetworkReply* reply = c.profile(); qDebug() << reply->url().toString(); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/tasteprofile/profile?api_key=JGJCRKWLXLBZIFAZB&format=xml&id=CAWRKLJ12BF92BC7C3" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); c.parseProfile( reply ); qDebug() << c; QCOMPARE( c.total(), 3 ); } void CatalogTest::testRead() { Echonest::Catalog c( "CAWRKLJ12BF92BC7C3" ); QNetworkReply* reply = c.readSongCatalog( Echonest::SongInformation( Echonest::SongInformation::AudioSummaryInformation | Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity | Echonest::SongInformation::ArtistLocation ) ); qDebug() << reply->url().toString(); QCOMPARE( reply->url(), QUrl( QLatin1String( "http://developer.echonest.com/api/v4/tasteprofile/read?api_key=JGJCRKWLXLBZIFAZB&format=xml&bucket=audio_summary&bucket=song_hotttnesss&bucket=artist_hotttnesss&bucket=artist_familiarity&bucket=artist_location&id=CAWRKLJ12BF92BC7C3" ) ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); c.parseRead( reply ); qDebug() << c; QVERIFY( !c.songs().isEmpty() ); QVERIFY( c.songs().at( 0 ).audioSummary().duration() > 0 ); QVERIFY( c.songs().at( 0 ).audioSummary().danceability() > 0 ); QVERIFY( c.songs().at( 0 ).audioSummary().energy() > 0 ); // QVERIFY( !c.songs().at( 0 ).tracks().isEmpty() ); QVERIFY( c.songs().at( 0 ).hotttnesss() > 0 ); QVERIFY( !c.songs().at( 0 ).dateAdded().isNull() ); QVERIFY( !c.songs().at( 0 ).artistId().isEmpty() ); QVERIFY( !c.songs().at( 0 ).foreignId().isEmpty() ); QVERIFY( c.songs().at( 0 ).artistHotttnesss() >= 0 ); QVERIFY( c.songs().at( 0 ).artistFamiliarity() >= 0 ); QCOMPARE( c.songs().at( 0 ).request().itemId(), QByteArray( "1f2cc282-068b-4d0c-aa41-d0d3b1638986" ) ); QCOMPARE( c.songs().at( 0 ).request().songName(), QLatin1String( "Your Hand In Mine" ) ); QCOMPARE( c.songs().at( 0 ).request().artistName(), QLatin1String( "Explosions in the sky" ) ); QCOMPARE( c.songs().at( 1 ).request().itemId(), QByteArray( "a61a4ac1-55a8-4c04-8789-5693d5d285d6" ) ); QCOMPARE( c.songs().at( 1 ).request().songName(), QLatin1String( "Your Hand In Mine" ) ); QCOMPARE( c.songs().at( 1 ).request().artistName(), QLatin1String( "Explosions in the sky" ) ); QCOMPARE( c.songs().at( 2 ).request().itemId(), QByteArray( "d951f4d6-b678-4264-8193-cd90c6b3ee4d" ) ); QCOMPARE( c.songs().at( 2 ).request().songName(), QLatin1String( "Your Hand In Mine" ) ); QCOMPARE( c.songs().at( 2 ).request().artistName(), QLatin1String( "Explosions in the sky" ) ); QCOMPARE( c.songs().size(), 3 ); foreach( const Echonest::Song& song, c.songs() ) { QCOMPARE( song.tracks().size(), 0 ); // QCOMPARE( song.tracks().at( 0 ).id().constData(), "TRCIYSH1254845BAEE" ); // QCOMPARE( song.tracks().at( 1 ).id().constData(), "TRWYCBR128F9325C60" ); // QCOMPARE( song.tracks().at( 2 ).id().constData(), "TRXIFWD123E8589514" ); // QCOMPARE( song.tracks().at( 3 ).id().constData(), "TRXKSJB128F92E8307" ); // QCOMPARE( song.tracks().at( 4 ).id().constData(), "TRZMJVA128F42636CE" ); } // test an artist catalog Echonest::Catalog c2( "CAXBXBZ12BF92A9AC2" ); reply = c2.readArtistCatalog( Echonest::ArtistInformation( Echonest::ArtistInformation::Audio | Echonest::ArtistInformation::Blogs | Echonest::ArtistInformation::Biographies | Echonest::ArtistInformation::Familiarity | Echonest::ArtistInformation::Hotttnesss | Echonest::ArtistInformation::Images | Echonest::ArtistInformation::News | Echonest::ArtistInformation::Reviews | Echonest::ArtistInformation::Terms | Echonest::ArtistInformation::Urls | Echonest::ArtistInformation::Videos ) ); qDebug() << reply->url().toString(); QCOMPARE( reply->url(), QUrl( QLatin1String( "http://developer.echonest.com/api/v4/tasteprofile/read?api_key=JGJCRKWLXLBZIFAZB&format=xml&bucket=audio&bucket=biographies&bucket=blogs&bucket=familiarity&bucket=hotttnesss&bucket=images&bucket=news&bucket=reviews&bucket=terms&bucket=urls&bucket=video&id=CAXBXBZ12BF92A9AC2" ) ) ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); c2.parseRead( reply ); qDebug() << c2; QVERIFY( !c2.artists().isEmpty() ); QVERIFY( c2.artists().at( 0 ).familiarity() > 0 ); QVERIFY( !c2.artists().at( 0 ).terms().isEmpty() ); QVERIFY( !c2.artists().at( 0 ).biographies().isEmpty() ); QVERIFY( !c2.artists().at( 0 ).blogs().isEmpty() ); QVERIFY( !c2.artists().at( 0 ).name().isEmpty() ); QVERIFY( c2.artists().at( 0 ).hotttnesss() > 0 ); QVERIFY( !c2.artists().at( 0 ).reviews().isEmpty() ); QVERIFY( !c2.artists().at( 0 ).videos().isEmpty() ); QVERIFY( c2.artists().at( 0 ).lastFmUrl().isValid() ); QVERIFY( !c2.artists().at( 0 ).news().isEmpty() ); QVERIFY( !c2.artists().at( 0 ).images().isEmpty() ); QVERIFY( !c2.artists().at( 0 ).audio().isEmpty() ); QVERIFY( !c2.artists().at( 0 ).dateAdded().isNull() ); QVERIFY( !c2.artists().at( 0 ).id().isEmpty() ); QVERIFY( !c2.artists().at( 0 ).foreignId().isEmpty() ); QCOMPARE( c2.artists().at( 0 ).request().itemId(), QByteArray( "473cb167-6459-429a-b4a2-561702753d8b" ) ); QCOMPARE( c2.artists().at( 0 ).request().artistName(), QLatin1String( "Mono" ) ); QCOMPARE( c2.artists().at( 1 ).request().itemId(), QByteArray( "f1bc85de-de19-4cc2-a77b-475a5b4afb7b" ) ); QCOMPARE( c2.artists().at( 1 ).request().artistName(), QLatin1String( "Balmorhea" ) ); QCOMPARE( c2.artists().size(), 2 ); } void CatalogTest::testStatus() { } void CatalogTest::testCreateUpdateDeleteSong() { /*{ Echonest::Catalog c( "CALARIS148554DBBF3" ); QNetworkReply* reply = c.deleteCatalog(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); exit(1);}*/ try { QNetworkReply* reply = Echonest::Catalog::create( QLatin1String( "unittest_catalog_song_Y" ), Echonest::CatalogTypes::Song ); // QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); m_songC = Echonest::Catalog::parseCreate( reply ); qDebug() << "CREATED NEW CATALOG:" << m_songC; QVERIFY( !m_songC.id().isEmpty() ); QVERIFY( !m_songC.name().isEmpty() ); // m_songC = Echonest::Catalog( "CAGFEYU12BFD4789A2" ); // for deleting manually a catalog Echonest::CatalogUpdateEntry entry; entry.setSongName( QLatin1String( "Your Hand In Mine" ) ); entry.setArtistName( QLatin1String( "Explosions in the sky" ) ); // entry.S( "fooid" ); // entry.setFingerpring( "FINGERPRINT" ); // entry.setRelease( QLatin1String( "FooAlbum:" ) ); // entry.setGenre( QLatin1String( "Rock" ) ); // entry.setRating( 5 ); // entry.setTrackNumber( 5 ); // entry.setDiscNumber( 1 ); // entry.setUrl( QLatin1String( "myurl" ) ); entry.setFavorite( true ); entry.setAction( Echonest::CatalogTypes::Update ); entry.setPlayCount(10000); Echonest::CatalogUpdateEntries entries; entries << entry; reply = m_songC.update( entries ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QByteArray ticket = Echonest::Catalog::parseTicket( reply ); qDebug() << ticket; QVERIFY( !ticket.isEmpty() ); // now check the ticket status after 10s QTest::qWait( 10000 ); reply = Echonest::Catalog::status( ticket ); qDebug() << "Getting status info:" << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::CatalogStatus cs = Echonest::Catalog::parseStatus( reply ); qDebug() << "Catalog status:" << cs.status << cs.items_updated << cs.items; // now read the catalog reply = m_songC.readSongCatalog(); qDebug() << reply->url(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); m_songC.parseRead( reply ); qDebug() << m_songC; QCOMPARE( m_songC.songs().size(), 1 ); } catch( const Echonest::ParseError& e ) { qWarning() << "Got exception, failing:" << e.what(); QVERIFY( false ); } } void CatalogTest::testCreateUpdateDeleteArtist() { try { QNetworkReply* reply = Echonest::Catalog::create( QLatin1String( "unittest_catalog_artist_Y" ), Echonest::CatalogTypes::Artist ); // QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); m_artistC = Echonest::Catalog::parseCreate( reply ); qDebug() << "CREATED NEW ARTIST CATALOG:" << m_artistC; QVERIFY( !m_artistC.id().isEmpty() ); QVERIFY( !m_artistC.name().isEmpty() ); // c = Echonest::Catalog( "CAPRWVK12BFA1A6F17" ); Echonest::CatalogUpdateEntry entry; entry.setArtistName( QLatin1String( "Balmorhea" ) ); entry.setGenre( QLatin1String( "Post Rock" ) ); entry.setAction( Echonest::CatalogTypes::Update ); Echonest::CatalogUpdateEntry entry2; entry2.setArtistName( QLatin1String( "Mono" ) ); entry2.setGenre( QLatin1String( "Post Rock" ) ); entry2.setAction( Echonest::CatalogTypes::Update ); Echonest::CatalogUpdateEntries entries; entries << entry << entry2; reply = m_artistC.update( entries ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QByteArray ticket = Echonest::Catalog::parseTicket( reply ); qDebug() << ticket; QVERIFY( !ticket.isEmpty() ); // now check the ticket status after 5s QTest::qWait( 10000 ); reply = Echonest::Catalog::status( ticket ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::CatalogStatus cs = Echonest::Catalog::parseStatus( reply ); qDebug() << "Catalog status:" << cs.status << cs.items_updated << cs.items; // now read the catalog reply = m_artistC.readSongCatalog(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); m_artistC.parseRead( reply ); qDebug() << m_artistC; QCOMPARE( m_artistC.artists().size(), 2 ); } catch( const Echonest::ParseError& e ) { qWarning() << "Got exception, failing:" << e.what(); QVERIFY( false ); } } void CatalogTest::cleanupTestCase() { // delete the two test catalogs we created qDebug() << "Deleting song catalog!"; QNetworkReply* reply = m_songC.deleteCatalog(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); qDebug() << "Deleting artist catalog!"; reply = m_artistC.deleteCatalog(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); return; } QTEST_MAIN(CatalogTest) #include "CatalogTest.moc" libechonest-2.3.1/src/Genre.cpp000644 001750 001750 00000015465 12465467260 017200 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2014 Leo Franchi * * Copyright (c) 2014 Stefan Derkits * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Genre.h" #include "Genre_p.h" #include "Parsing_p.h" Echonest::Genre::Genre() : d( new GenreData ) { init(); } Echonest::Genre::Genre( const Echonest::Genre& other ) : d( other.d ) { init(); } Echonest::Genre::Genre( const QString& name ) { init(); setName( name ); } Echonest::Genre& Echonest::Genre::operator=( const Echonest::Genre& genre ) { d = genre.d; return *this; } Echonest::Genre::~Genre() { } void Echonest::Genre::init() { qRegisterMetaType("Echonest::Genre"); } QString Echonest::Genre::name() const { return d->name; } void Echonest::Genre::setName( const QString& name ) { d->name = name; } Echonest::Artists Echonest::Genre::artists() const { return d->artists; } void Echonest::Genre::setArtists( const Artists& artists ) { d->artists = artists; } QUrl Echonest::Genre::wikipediaUrl() const { return d->wikipedia_url; } void Echonest::Genre::setWikipediaUrl( const QUrl& url ) { d->wikipedia_url = url; } QString Echonest::Genre::description() const { return d->description; } void Echonest::Genre::setDescription( const QString& description ) { d->description = description; } QNetworkReply* Echonest::Genre::fetchArtists( ArtistInformation information, int numResults, bool limit ) { QUrl url = setupQuery( "artists", numResults ); urlAddQueryItem( url, QLatin1String( "limit" ), QLatin1String( limit ? "true" : "false" ) ); Artist::addQueryInformation( url, information ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Genre::fetchSimilar( GenreInformation information, int numResults, int start ) { QUrl url = setupQuery( "similar", numResults, start ); addQueryInformation( url, information ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Genre::fetchProfile( const Echonest::Genres& genres, GenreInformation information ) { QUrl url = setupStaticQuery( "profile" ); addQueryInformation( url, information ); foreach( const Genre& g, genres ) urlAddQueryItem( url, QLatin1String( "name" ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( g.name() ) ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Genre::fetchList( GenreInformation information, int numResults ) { QUrl url = setupStaticQuery( "list", numResults ); addQueryInformation( url, information ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Genre::fetchSearch( const QString& name, Echonest::GenreInformation information, int numResults, int start ) { QUrl url = setupStaticQuery( "search", numResults ); addQueryInformation( url, information ); urlAddQueryItem( url, QLatin1String( "name" ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( name ) ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } Echonest::Artists Echonest::Genre::parseArtists( QNetworkReply* reply ) throw( Echonest::ParseError ) { return Artist::parseSearch( reply ); } Echonest::Genres Echonest::Genre::parseSimilar( QNetworkReply* reply ) throw( Echonest::ParseError ) { return parseList( reply ); } Echonest::Genres Echonest::Genre::parseProfile( QNetworkReply* reply ) throw( Echonest::ParseError ) { return parseList( reply ); } Echonest::Genres Echonest::Genre::parseList( QNetworkReply* reply ) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); Echonest::Genres genres = Echonest::Parser::parseGenres( xml ); reply->deleteLater(); return genres; } Echonest::Genres Echonest::Genre::parseSearch( QNetworkReply* reply ) throw( Echonest::ParseError ) { return parseList( reply ); } QUrl Echonest::Genre::setupStaticQuery( const QByteArray& methodName, int numResults, int start ) { QUrl url = Echonest::baseGetQuery( "genre", methodName ); if( numResults > 0 ) urlAddQueryItem( url, QLatin1String( "results" ), QString::number( numResults ) ); if( start >= 0 ) urlAddQueryItem( url, QLatin1String( "start" ), QString::number( start ) ); return url; } QUrl Echonest::Genre::setupQuery( const QByteArray& methodName, int numResults, int start) const { QUrl url = setupStaticQuery( methodName, numResults, start ); if( !d->name.isEmpty() ) { urlAddQueryItem( url, QLatin1String( "name" ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( d->name ) ) ); } else if ( methodName != "list" || methodName != "search" ) { qWarning() << "Genre method" << methodName << "called on a genre object without name or id!"; return QUrl(); } return url; } void Echonest::Genre::addQueryInformation( QUrl& url, Echonest::GenreInformation information ) { if( information.flags().testFlag( Echonest::GenreInformation::Description ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "description" ) ); if( information.flags().testFlag( Echonest::GenreInformation::Urls ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "urls" ) ); } QDebug Echonest::operator<<(QDebug d, const Echonest::Genre& genre) { return d.maybeSpace() << QString::fromLatin1( "Genre(%1)" ).arg( genre.name() ); } libechonest-2.3.1/Doxyfile.in000644 001750 001750 00000015636 12465467260 016760 0ustar00stefanstefan000000 000000 #--------------------------------------------------------------------------- # Project related configuration options, copied from ${KDE_SRC}/kdesdk/scripts/kdedoxygen.sh #--------------------------------------------------------------------------- PROJECT_NAME = libechonest PROJECT_NUMBER = 2.0.1 OUTPUT_DIRECTORY = apidocs CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The \$name class" \ "The \$name widget" \ "The \$name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO BUILTIN_STL_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = YES HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = YES CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_MEMBERS_CTORS_1ST = YES SORT_BRIEF_DOCS = YES SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST = YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_FORMAT = "\$file:\$line: \$text" WARN_LOGFILE = doxygen.log #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src/ FILE_PATTERNS = *.cpp \ *.cc \ *.cxx \ *.h \ *.hh \ *.hxx \ *.hpp \ *.dox RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */.svn/* \ */.git/* \ */cmake/* \ *.moc.* \ moc* \ *.all_cpp.* \ *unload.* \ */test/* \ */tests/* \ *_p.cpp \ *_p.h EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = $PWD INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # do NOT generate any formats other than html #--------------------------------------------------------------------------- SOURCE_BROWSER = NO GENERATE_HTML = YES GENERATE_LATEX = NO GENERATE_RTF = NO GENERATE_XML = NO GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO DISABLE_INDEX = YES #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EGENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = NO GROUP_GRAPHS = NO UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = YES SERVER_BASED_SEARCH = NO libechonest-2.3.1/src/CatalogUpdateEntry.h000644 001750 001750 00000012066 12465467260 021336 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CATALOG_ENTRY_H #define ECHONEST_CATALOG_ENTRY_H #include "echonest_export.h" #include "Util.h" #include #include #include #include class CatalogUpdateEntryData; namespace Echonest { /** * This rather simple struct collects information about a status update */ typedef QVector< QPair< QByteArray, QString > > CatalogStatusItem; typedef struct CatalogStatusStruct { CatalogTypes::TicketStatus status; QString details; int items_updated; CatalogStatusItem items; // List of [ item_id, info ] // int percent_complete; CatalogStatusStruct() : status( CatalogTypes::Unknown ), items_updated( -1 ) {} } CatalogStatus; /** * This class described a catalog entry for use in the Catalog update() call. * All data fields are optional except Action, and only the ones specified will be sent. */ class ECHONEST_EXPORT CatalogUpdateEntry { public: CatalogUpdateEntry(); CatalogUpdateEntry( CatalogTypes::Action action ); virtual ~CatalogUpdateEntry(); CatalogUpdateEntry( const CatalogUpdateEntry& other ); CatalogUpdateEntry& operator=( const CatalogUpdateEntry& ); /** * Optional, the item id for the catalog entry. hash( catalog_id + item_id ) * MUST be unique. If this is not set, a unique id will be generated internally. */ QByteArray itemId() const; void setItemId( const QByteArray& id ); /** * The type of action that this item represents, required. */ CatalogTypes::Action action() const; void setAction( CatalogTypes::Action action ); /** * The Echo Nest fingerprint. */ QByteArray fingerprint() const; void setFingerprint( const QByteArray& id ); // deprecated void setFingerpring( const QByteArray& id ); /** * The song id. Rosetta id or Echo Nest ID. */ QByteArray songId() const; void setSongId( const QByteArray& id ); /** * The song name. Mutually exclusive with song id. */ QString songName() const; void setSongName( const QString& name ); /** * The artist id, either a rosetta stone ID or an Echo Nest ID. */ QByteArray artistId() const; void setArtistId( const QByteArray& id ); /** * The artist name, mutually exclusive with artist id. */ QString artistName() const; void setArtistName( const QString& name ); /** * The release, or album, name. */ QString release() const; void setRelease( const QString& release ); /** * The genre of the item. */ QString genre() const; void setGenre( const QString& genre ); /** * The track number. */ int trackNumber() const; void setTrackNumber( int trackNum ); /** * The disc number of this item. */ int discNumber() const; void setDiscNumber( int disc ); /** * The url or the local filename or remote url. */ QString url() const; void setUrl( const QString& url ); /** * If this song was marked as a favorite or not */ bool favorite() const; void setFavorite( bool fav ); /** * If this song was banned. */ bool banned() const; void setBanned( bool banned ); /** * The play count of this item. */ int playCount() const; void setPlayCount( int playCount ); /** * The skip count of this item. */ int skipCount() const; void setSkipCount( int skipCount ); /** * The rating of this item, from 1 to 10. */ int rating() const; void setRating( int rating ); bool favoriteSet() const; bool bannedSet() const; private: QSharedDataPointer d; }; typedef QVector CatalogUpdateEntries; } #endif libechonest-2.3.1/src/echonest_export.h000644 001750 001750 00000003334 12465467260 021006 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_EXPORT_H #define ECHONEST_EXPORT_H #if defined _WIN32 #if defined ( echonest_EXPORTS ) || defined ( echonest5_EXPORTS ) #define ECHONEST_EXPORT __declspec(dllexport) #else #define ECHONEST_EXPORT __declspec(dllimport) #endif #elif __GNUC__ >= 4 #define ECHONEST_EXPORT __attribute__ ((visibility("default"))) #else #define ECHONEST_EXPORT #endif #endif libechonest-2.3.1/CMakeLists.txt000644 001750 001750 00000012041 12465467260 017370 0ustar00stefanstefan000000 000000 # This file and all other CMakeLists.txt files in this project are # copyright Leo Franchi, 2010, and licensed under the MIT license. project( libechonest ) cmake_minimum_required( VERSION 2.6 ) set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII" ) option(ECHONEST_BUILD_TESTS "Build all unit tests" ON) option(BUILD_WITH_QT4 "Build libechonest with Qt4 no matter if Qt5 was found" ON) if( NOT BUILD_WITH_QT4 ) find_package(Qt5Core QUIET) # CMAKE 2.8.13+/3.0.0+ requires these for IMPORTed targets find_package(Qt5Xml REQUIRED) if( Qt5Core_DIR ) message(STATUS "Found Qt5! Be aware that Qt5-support is still experimental and not officially supported!") set(ECHONEST_LIB_VERSION_SUFFIX 5) macro(qt_wrap_cpp) qt5_wrap_cpp(${ARGN}) endmacro() endif() # pkg-config names of QtCore and QtNetwork are Qt5Core and Qt5Network for # Qt5 set(ECHONEST_QT_MAJOR_VERSION "5") set(ECHONEST_PC_DEPS "") endif() if( NOT Qt5Core_DIR ) message(STATUS "Could not find Qt5, searching for Qt4 instead...") if( ECHONEST_BUILD_TESTS ) find_package( Qt4 COMPONENTS QtCore QtNetwork QtTest REQUIRED ) else( ECHONEST_BUILD_TESTS ) find_package( Qt4 COMPONENTS QtCore QtNetwork REQUIRED ) endif() include( ${QT_USE_FILE} ) macro(qt5_use_modules) endmacro() macro(qt_wrap_cpp) qt4_wrap_cpp(${ARGN}) endmacro() set(ECHONEST_QT_MAJOR_VERSION "") set(ECHONEST_PC_DEPS "QJson") # QJson is only required for Qt4 find_package(QJSON REQUIRED) endif() set( ECHONEST_LIB_MAJOR_VERSION "2" ) set( ECHONEST_LIB_MINOR_VERSION "3" ) set( ECHONEST_LIB_PATCH_VERSION "1" ) set( ECHONEST_LIB_VERSION "${ECHONEST_LIB_MAJOR_VERSION}.${ECHONEST_LIB_MINOR_VERSION}.${ECHONEST_LIB_PATCH_VERSION}" ) set( ECHONEST_LIB_VERSION_SONAME "${ECHONEST_LIB_MAJOR_VERSION}.${ECHONEST_LIB_MINOR_VERSION}") if (CMAKE_COMPILER_IS_GNUCXX) ADD_DEFINITIONS( -Wall -Wundef -Wcast-align -Wchar-subscripts -Wpointer-arith -Wwrite-strings -Wpacked -Wformat-security -Wmissing-format-attribute -Wold-style-cast -Woverloaded-virtual -Wnon-virtual-dtor ) if ( NOT WIN32 ) add_definitions( -fvisibility=hidden ) endif() # to be added: # -Wshadow # FIXME we might want this one back in, but Qt 4.4.3 spits gazillions of warnings with it on Linux-64: # -Wconversion endif(CMAKE_COMPILER_IS_GNUCXX) if(MSVC) # since msvc doesn't implement it, we can simply ignore the warning: # cf. http://msdn.microsoft.com/en-us/library/sa28fef8.aspx add_definitions(/wd4290) endif(MSVC) if(ECHONEST_BUILD_TESTS) enable_testing() endif(ECHONEST_BUILD_TESTS) set( CPACK_GENERATOR "TBZ2" "DEB" ) set( CPACK_PACKAGE_VERSION_MAJOR "${ECHONEST_LIB_MAJOR_VERSION}" ) set( CPACK_PACKAGE_VERSION_MINOR "${ECHONEST_LIB_MINOR_VERSION}" ) set( CPACK_PACKAGE_VERSION_PATCH "${ECHONEST_LIB_PATCH_VERSION}" ) set( CPACK_PACKAGE_VERSION "${ECHONEST_LIB_VERSION}" ) set( CPACK_DEBIAN_PACKAGE_MAINTAINER "lfranchi@kde.org" ) set( CPACK_PACKAGE_DESCRIPTION_SUMMARY "A c++/qt library to access the APIs provided by The Echo Nest." ) if( APPLE ) set( CPACK_GENERATOR "DragNDrop" ) set( CPACK_DMG_FORMAT "UDBZ" ) set( CPACK_DMG_VOLUME_NAME "libechonest" ) set( CPACK_SYSTEM_NAME "OSX" ) endif( APPLE ) # pkg-config #add extra search paths for libraries and includes SET (LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) SET (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE STRING "Directory where lib will install") SET (INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in") IF (NOT WIN32) CONFIGURE_FILE (${CMAKE_CURRENT_SOURCE_DIR}/libechonest.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libechonest${ECHONEST_LIB_VERSION_SUFFIX}.pc @ONLY) INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/libechonest${ECHONEST_LIB_VERSION_SUFFIX}.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) ENDIF (NOT WIN32) include(CPack) # add a target to generate API documentation with Doxygen find_package(Doxygen) if(DOXYGEN_FOUND) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM ) endif(DOXYGEN_FOUND) # make uninstall support CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") set(ECHONEST_LIB_TARGET_NAME echonest${ECHONEST_LIB_VERSION_SUFFIX} CACHE INTERNAL "Target name of libechonest" FORCE) add_subdirectory( src ) if( ECHONEST_BUILD_TESTS ) add_subdirectory( tests ) endif( ECHONEST_BUILD_TESTS ) libechonest-2.3.1/src/Song.cpp000644 001750 001750 00000036070 12465467260 017041 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Song.h" #include "Config.h" #include "Song_p.h" #include "AudioSummary.h" #include "Parsing_p.h" #include "qjsonwrapper/Json.h" #include #include #include #include Echonest::Song::Song() : d( new SongData ) {} Echonest::Song::Song(const QByteArray& id, const QString& title, const QByteArray& artistId, const QString& artistName) :d( new SongData ) { d->id = id; d->title = title; d->artistId = artistId; d->artistName = artistName; } Echonest::Song::Song(const QByteArray& id) :d( new SongData ) { d->id = id; } Echonest::Song::Song(const Echonest::Song& other) : d( other.d ) { } Echonest::Song::~Song() { } Echonest::Song& Echonest::Song::operator=(const Echonest::Song& song) { d = song.d; return *this; } QByteArray Echonest::Song::id() const { return d->id; } void Echonest::Song::setId(const QByteArray& id) { d->id = id; } QString Echonest::Song::title() const { return d->title; } void Echonest::Song::setTitle(const QString& title) { d->title = title; } QByteArray Echonest::Song::artistId() const { return d->artistId; } void Echonest::Song::setArtistId(const QByteArray& artistId) { d->artistId = artistId; } QString Echonest::Song::artistName() const { return d->artistName; } void Echonest::Song::setArtistName(const QString& artistName) { d->artistName = artistName; } QString Echonest::Song::release() const { return d->release; } void Echonest::Song::setRelease(const QString& release) { d->release = release; } QVector< Echonest::Track > Echonest::Song::tracks() const { return d->tracks; } void Echonest::Song::setTracks(const QVector< Echonest::Track >& tracks) { d->tracks = tracks; } qreal Echonest::Song::hotttnesss() const { return d->hotttnesss; } void Echonest::Song::setHotttnesss(qreal hotttnesss) { d->hotttnesss = hotttnesss; } qreal Echonest::Song::artistHotttnesss() const { return d->artistHotttnesss; } void Echonest::Song::setArtistHotttnesss(qreal artistHotttnesss) { d->artistHotttnesss = artistHotttnesss; } Echonest::AudioSummary Echonest::Song::audioSummary() const { return d->audioSummary; } void Echonest::Song::setAudioSummary(const Echonest::AudioSummary& summary) { d->audioSummary = summary; } qreal Echonest::Song::artistFamiliarity() const { return d->artistFamiliarity; } void Echonest::Song::setArtistFamiliarity(qreal artistFamiliarity) { d->artistFamiliarity = artistFamiliarity; } Echonest::ArtistLocation Echonest::Song::artistLocation() const { return d->artistLocation; } void Echonest::Song::setArtistLocation(const Echonest::ArtistLocation& artistLocation) { d->artistLocation = artistLocation; } QList< QString > Echonest::Song::songTypes() const { return d->songTypes; } void Echonest::Song::setSongTypes(const QList< QString >& songTypes) { d->songTypes = songTypes; } void Echonest::Song::addSongType(const QString& songType) { d->songTypes.append( songType ); } QNetworkReply* Echonest::Song::fetchInformation( Echonest::SongInformation information ) const { QUrl url = Echonest::baseGetQuery( "song", "profile" ); urlAddQueryItem( url, QString::fromLatin1( "id" ), QString::fromLatin1( d->id ) ); addQueryInformation( url, information ); qDebug() << "Creating fetchInformation URL" << url; return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Song::search( const Echonest::Song::SearchParams& params, Echonest::SongInformation information ) { QUrl url = Echonest::baseGetQuery( "song", "search" ); addQueryInformation( url, information ); SearchParams::const_iterator iter = params.constBegin(); for( ; iter < params.constEnd(); ++iter ) { urlAddQueryItem( url, QString::fromLatin1( searchParamToString( iter->first ) ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( iter->second.toString() ) ) ); } qDebug() << "Creating search URL" << url; return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Song::identify( const Echonest::Song::IdentifyParams& params, const Echonest::SongInformation& information ) { QVariantMap query; QVariantMap metadata; IdentifyParams::const_iterator iter = params.constBegin(); for( ; iter < params.constEnd(); ++iter ) { if( iter->first == Code ) query[ QLatin1String( identifyParamToString( iter->first ) ) ] = iter->second; else metadata[ QLatin1String( identifyParamToString( iter->first ) ) ] = iter->second.toString(); } metadata[ QLatin1String( "version" ) ] = QLatin1String( "4.12" ); query[ QLatin1String( "metadata" ) ] = metadata; QByteArray data = QJsonWrapper::toJson( query ); QUrl url = Echonest::baseGetQuery( "song", "identify" ); addQueryInformation( url, information ); qDebug() << "Creating identify URL" << url; QNetworkRequest request( url ); request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/octet-stream" ) ); // qDebug() << "Uploading local file to" << url; return Echonest::Config::instance()->nam()->post( request, data ); } Echonest::SongList Echonest::Song::parseIdentify( QNetworkReply* reply ) throw( ParseError ) { Echonest::Parser::checkForErrors( reply ); QByteArray data = reply->readAll(); QVariantMap res = QJsonWrapper::parseJson( data ).toMap(); // qDebug() << "Got data from identify call:" << data << res; qDebug() << res[ QLatin1String( "response" ) ].toMap()[ QLatin1String( "songs" ) ].toList(); if( !res.contains( QLatin1String( "response" ) ) || !res[ QLatin1String( "response" ) ].toMap().contains( QLatin1String( "songs" ) ) ) { qDebug() << "No response or songs elemnt in json..."; throw ParseError( UnknownParseError, QLatin1String( "Invalid json response" ) ); } SongList songs; QVariantList songsV = res[ QLatin1String( "response" ) ].toMap()[ QLatin1String( "songs" ) ].toList(); foreach( const QVariant& s, songsV ) { QVariantMap sM = s.toMap(); Echonest::Song song; if( sM.contains( QLatin1String( "title" ) ) ) song.setTitle( sM[ QLatin1String( "title" ) ].toString() ); if( sM.contains( QLatin1String( "artist_id" ) ) ) song.setArtistId( sM[ QLatin1String( "artist_id" ) ].toByteArray() ); if( sM.contains( QLatin1String( "artist_name" ) ) ) song.setArtistName( sM[ QLatin1String( "artist_name" ) ].toString() ); if( sM.contains( QLatin1String( "id" ) ) ) song.setId( sM[ QLatin1String( "id" ) ].toByteArray() ); songs.append( song ); } reply->deleteLater(); return songs; } void Echonest::Song::parseInformation( QNetworkReply* reply ) throw( ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); // we'll just take the new data. it is given as a list even though it can only have 1 song as we specify the song id QVector< Echonest::Song > songs = Echonest::Parser::parseSongList( xml ); if( songs.size() != 1 ) { // no data for this song. returned empty. return; } // copy any non-default values Echonest::Song newSong = songs.at( 0 ); if( newSong.hotttnesss() >= 0 ) setHotttnesss( newSong.hotttnesss() ); if( newSong.artistHotttnesss() >= 0 ) setArtistHotttnesss( newSong.artistHotttnesss() ); if( newSong.artistFamiliarity() >= 0 ) setArtistFamiliarity( newSong.artistFamiliarity() ); if( !newSong.artistLocation().location.isEmpty() ) setArtistLocation( newSong.artistLocation() ); reply->deleteLater(); } QVector< Echonest::Song > Echonest::Song::parseSearch( QNetworkReply* reply ) throw( ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); QVector songs = Echonest::Parser::parseSongList( xml ); reply->deleteLater(); return songs; } QByteArray Echonest::Song::searchParamToString( Echonest::Song::SearchParam param ) { switch( param ) { case Echonest::Song::Title: return "title"; case Echonest::Song::Artist: return "artist"; case Echonest::Song::Combined: return "combined"; case Echonest::Song::Description: return "description"; case Echonest::Song::ArtistId: return "artist_id"; case Echonest::Song::Start: return "start"; case Echonest::Song::Results: return "results"; case Echonest::Song::MaxTempo: return "max_tempo"; case Echonest::Song::MinTempo: return "min_tempo"; case Echonest::Song::MaxDanceability: return "max_danceability"; case Echonest::Song::MinDanceability: return "min_danceability"; case Echonest::Song::MaxComplexity: return "max_complexity"; case Echonest::Song::MinComplexity: return "min_complexity"; case Echonest::Song::MaxDuration: return "max_duration"; case Echonest::Song::MinDuration: return "min_duration"; case Echonest::Song::MaxLoudness: return "max_loudness"; case Echonest::Song::MinLoudness: return "min_loudness"; case Echonest::Song::MaxFamiliarity: return "max_familiarity"; case Echonest::Song::MinFamiliarity: return "min_familiarity"; case Echonest::Song::MaxHotttnesss: return "max_hotttnesss"; case Echonest::Song::MinHotttnesss: return "min_hotttnesss"; case Echonest::Song::MaxLongitude: return "max_longitude"; case Echonest::Song::MinLongitude: return "min_longitude"; case Echonest::Song::MinEnergy: return "min_energy"; case Echonest::Song::MaxEnergy: return "max_energy"; case Echonest::Song::Mode: return "mode"; case Echonest::Song::Key: return "key"; case Echonest::Song::Sort: return "sort"; case Echonest::Song::SongType: return "song_type"; case Echonest::Song::ArtistStartYearBefore: return "artist_start_year_before"; case Echonest::Song::ArtistStartYearAfter: return "artist_start_year_after"; case Echonest::Song::ArtistEndYearBefore: return "artist_end_year_before"; case Echonest::Song::ArtistEndYearAfter: return "artist_end_year_after"; case Echonest::Song::MaxAcousticness: return "max_acousticness"; case Echonest::Song::MinAcousticness: return "min_acousticness"; case Echonest::Song::MaxSpeechiness: return "max_speechiness"; case Echonest::Song::MinSpeechiness: return "min_speechiness"; case Echonest::Song::MaxLiveness: return "max_liveness"; case Echonest::Song::MinLiveness: return "min_liveness"; case Echonest::Song::MaxValence: return "max_valence"; case Echonest::Song::MinValence: return "min_valence"; } return QByteArray(); } QByteArray Echonest::Song::identifyParamToString( Echonest::Song::IdentifyParam param ) { switch( param ) { case Echonest::Song::Code: return "code"; case Echonest::Song::IdentifyArtist: return "artist"; case Echonest::Song::IdentifyDuration: return "duration"; case Echonest::Song::IdentifyGenre: return "genre"; case Echonest::Song::IdentifyRelease: return "release"; case Echonest::Song::IdentifyTitle: return "title"; } return QByteArray(); } void Echonest::Song::addQueryInformation(QUrl& url, Echonest::SongInformation information) { if( information.flags().testFlag( Echonest::SongInformation::AudioSummaryInformation ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "audio_summary" ) ); if( information.flags().testFlag( Echonest::SongInformation::Tracks ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "tracks" ) ); if( information.flags().testFlag( Echonest::SongInformation::Hotttnesss ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "song_hotttnesss" ) ); if( information.flags().testFlag( Echonest::SongInformation::ArtistHotttnesss ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "artist_hotttnesss" ) ); if( information.flags().testFlag( Echonest::SongInformation::ArtistFamiliarity ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "artist_familiarity" ) ); if( information.flags().testFlag( Echonest::SongInformation::ArtistLocation ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "artist_location" ) ); if( information.flags().testFlag( Echonest::SongInformation::SongType ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "song_type" ) ); if( !information.idSpaces().isEmpty() ) { foreach( const QString& idSpace, information.idSpaces() ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "id:" + idSpace.toUtf8() ) ); } } QString Echonest::Song::toString() const { return QString::fromLatin1( "Song(%1, %2, %3, %4)" ).arg( title() ).arg( artistName() ).arg( QString::fromLatin1( id() ) ).arg( QString::fromLatin1( artistId() ) ); } QDebug Echonest::operator<<(QDebug d, const Echonest::Song& song) { d << song.toString(); return d.maybeSpace(); } libechonest-2.3.1/tests/ArtistTest.h000644 001750 001750 00000004657 12465467260 020267 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_ARTIST_TEST_H #define ECHONEST_ARTIST_TEST_H #include class ArtistTest : public QObject { Q_OBJECT private slots: void initTestCase(); void testBiographiesUrl(); void testBiographies(); void testBlogsUrl(); void testBlogs(); void testFamiliarityUrl(); void testFamiliarity(); void testHotttnesssUrl(); void testHotttnesss(); void testImagesUrl(); void testImages(); void testNewsUrl(); void testNews(); void testProfileUrl(); void testProfile(); void testReviewsUrl(); void testReviews(); void testSearchUrl(); void testSearch(); void testSongsUrl(); void testSongs(); void testSimilarUrl(); void testSimilar(); void testTermsUrl(); void testTerms(); void testListTerms(); void testSuggest(); void testTopHotttUrl(); void testTopHottt(); void testTopTermsUrl(); void testTopTerms(); void testTwitterUrl(); void testTwitter(); void testUrlsUrl(); void testUrls(); void testVideosUrl(); void testVideos(); void testUrlsFix(); }; #endif libechonest-2.3.1/src/Track_p.h000644 001750 001750 00000010005 12465467260 017151 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_TRACK_P_H #define ECHONEST_TRACK_P_H #include "AudioSummary.h" #include "Config.h" #include "Song.h" #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) #include #endif #include #include #include namespace Echonest { inline QNetworkReply* doPost(const QUrl& url) { // UGLY :( Build url, then extract the encded query items, put them in the POST body, and send that to the url minus the encoded params. // The final data QByteArray data; #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) int size = QUrlQuery( url ).queryItems().size(); for( int i = 0; i < size; i++ ) { const QPair< QString, QString > item = QUrlQuery( url ).queryItems().at( i ); data.append( item.first.toLatin1() + "=" + item.second.toLatin1() + "&" ); } #else int size = url.encodedQueryItems().size(); for( int i = 0; i < size; i++ ) { const QPair< QByteArray, QByteArray > item = url.encodedQueryItems().at( i ); data.append( item.first + "=" + item.second + "&" ); } #endif data.truncate( data.size() - 1 ); // remove extra & //qDebug() << "Sending data:" << data << "for method:" << url.path(); // strip the extras QUrl url2( url.toString().mid( 0, url.toString().indexOf( QLatin1Char( '?' ) ) ) ); QNetworkRequest request = QNetworkRequest( url2 ); request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "application/x-www-form-urlencoded" ) ); return Echonest::Config::instance()->nam()->post( request, data ); } } class TrackData : public QSharedData { public: TrackData() {} TrackData(const TrackData& other) : QSharedData( other ) { analyzer_version = other.analyzer_version; artist = other.artist; bitrate = other.bitrate; id = other.id; md5 = other.md5; release = other.release; samplerate = other.samplerate; status = other.status; title = other.title; catalog = other.catalog; foreign_id = other.foreign_id; release_image = other.release_image; preview_url = other.preview_url; } QString artist; QString analyzer_version; int bitrate; QByteArray id; QByteArray md5; QString release; QByteArray audio_md5; int samplerate; QString status; QString title; // used when fetched as a foreign id in a tracks bucket QString catalog; QByteArray foreign_id; QUrl release_image; QUrl preview_url; // song tracks have an associated song Echonest::Song song; Echonest::AudioSummary audio_summary; }; #endif libechonest-2.3.1/src/Util.h000644 001750 001750 00000007755 12465467260 016525 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_UTIL_H #define ECHONEST_UTIL_H #include "echonest_export.h" #include #include #include /** * Some shared declarations */ namespace Echonest { namespace Analysis { enum AnalysisStatus { Unknown = 0, Pending = 1, Complete = 2, Error = 4 }; } namespace CatalogTypes { enum Type { Artist = 0, Song = 1 }; enum Action { Delete, Update, Play, Skip }; enum TicketStatus { Unknown = 0, Pending = 1, Complete = 2, Error = 4 }; } typedef struct { qreal confidence; qreal duration; qreal start; } AudioChunk; typedef AudioChunk Bar; typedef AudioChunk Beat; typedef AudioChunk Section; typedef AudioChunk Tatum; typedef struct { qreal confidence; qreal duration; qreal loudness_max; qreal loudness_max_time; qreal loudness_start; QVector< qreal > pitches; qreal start; QVector< qreal > timbre; } Segment; typedef QVector< Bar > BarList; typedef QVector< Beat > BeatList; typedef QVector< Section > SectionList; typedef QVector< Tatum > TatumList; typedef QVector< Segment > SegmentList; typedef struct { QUrl url; QString attribution; QString type; } License; typedef struct { QString catalog; QString foreign_id; } ForeignId; typedef struct { qreal latitude; qreal longitude; QString location; } ArtistLocation; typedef QVector< ForeignId > ForeignIds; QByteArray escapeSpacesAndPluses( const QString& in ); Analysis::AnalysisStatus statusToEnum( const QString& status ); QString statusToString( Analysis::AnalysisStatus status ); QByteArray catalogTypeToLiteral( CatalogTypes::Type ); CatalogTypes::Type literalToCatalogType( const QByteArray& type ); QByteArray catalogStatusToLiteral( CatalogTypes::TicketStatus ); CatalogTypes::TicketStatus literalToCatalogStatus( const QByteArray& type ); QByteArray catalogUpdateActionToLiteral( CatalogTypes::Action ); CatalogTypes::Action literalToCatalogUpdateAction( const QByteArray& type ); void urlAddQueryItem( QUrl& url, const QString& key, const QString& value ); void urlRemoveQueryItem( QUrl& url, const QString& key ); ECHONEST_EXPORT QDebug operator<<(QDebug d, const ForeignId& id); ECHONEST_EXPORT QDebug operator<<(QDebug d, const ArtistLocation& id); } #endif libechonest-2.3.1/src/CommonTypes.h000644 001750 001750 00000003174 12465467260 020054 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2014 Leo Franchi * * Copyright (c) 2014 Stefan Derkits * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_COMMONTYPES_H #define ECHONEST_COMMONTYPES_H namespace Echonest{ class Artist; typedef QVector< Artist > Artists; class Genre; typedef QVector< Genre > Genres; } // namespace #endif libechonest-2.3.1/tests/GenreTest.h000644 001750 001750 00000003500 12465467260 020043 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2014 Leo Franchi * * Copyright (c) 2014 Stefan Derkits * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_GENRE_TEST_H #define ECHONEST_GENRE_TEST_H #include class GenreTest : public QObject { Q_OBJECT private slots: void initTestCase(); void testArtistsUrl(); void testArtists(); void testListUrl(); void testList(); void testProfileUrl(); void testProfile(); void testSearchUrl(); void testSearch(); void testLegacyHack(); }; #endif libechonest-2.3.1/src/Config.cpp000644 001750 001750 00000013763 12465467260 017344 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * Copyright (c) 2011 Jeff Mitchell * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Config.h" #include #include #include #include #include "Util.h" Echonest::Config* Echonest::Config::s_instance = 0; QUrl Echonest::baseUrl() { QUrl url; url.setScheme( QLatin1String( "http" ) ); url.setHost( QLatin1String( "developer.echonest.com" ) ); return url; } QUrl Echonest::baseGetQuery(const QByteArray& type, const QByteArray& method) { QUrl url = baseUrl(); url.setPath( QString::fromLatin1( "/api/v4/%1/%2" ).arg( QLatin1String( type ) ).arg( QLatin1String( method ) ) ); urlAddQueryItem( url, QLatin1String( "api_key" ), QString::fromLatin1( Echonest::Config::instance()->apiKey() ) ); urlAddQueryItem( url, QLatin1String( "format" ), QLatin1String( "xml" ) ); return url; } Echonest::ParseError::ParseError(Echonest::ErrorType error): exception() { type = error; } Echonest::ParseError::ParseError(Echonest::ErrorType error, const QString& text): exception() { type = error; extraText = text; } Echonest::ParseError::~ParseError() throw() {} Echonest::ErrorType Echonest::ParseError::errorType() const throw() { return type; } void Echonest::ParseError::setNetworkError( QNetworkReply::NetworkError error ) throw() { nError = error; } const char* Echonest::ParseError::what() const throw() { // If we have specific error text, return that first if( !extraText.isEmpty() ) return extraText.toLatin1().constData(); switch( type ) { case UnknownError: return "Unknown Echo Nest Error"; case NoError: return "No Error"; case MissingAPIKey: return "Missing Echo Nest API Key"; case NotAllowed: return "Method not allowed"; case RateLimitExceeded: return "Rate limit exceeded"; case MissingParameter: return "Missing parameter"; case InvalidParameter: return "Invalid parameter"; case UnfinishedQuery: return "Unfinished query object"; case EmptyResult: return "No results"; case NetworkError: return "Network Error"; case UnknownParseError: return "Unknown Parse Error"; } return ""; } QNetworkReply::NetworkError Echonest::ParseError::networkError() const throw() { return nError; } class Echonest::ConfigPrivate { public: ConfigPrivate() { threadNamHash[ QThread::currentThread() ] = new QNetworkAccessManager(); ourNamSet.insert( QThread::currentThread() ); } ~ConfigPrivate() { QThread *currThread = QThread::currentThread(); if( threadNamHash.contains( currThread ) ) { if ( ourNamSet.contains( currThread ) ) delete threadNamHash[ currThread ]; threadNamHash.remove( currThread ); ourNamSet.remove( currThread ); } } QMutex accessMutex; QHash< QThread*, QNetworkAccessManager* > threadNamHash; QSet< QThread* > ourNamSet; QByteArray apikey; }; Echonest::Config::Config() : d( new Echonest::ConfigPrivate ) { } Echonest::Config::~Config() { delete d; } void Echonest::Config::setAPIKey(const QByteArray& apiKey) { d->apikey = apiKey; } QByteArray Echonest::Config::apiKey() const { return d->apikey; } void Echonest::Config::setNetworkAccessManager(QNetworkAccessManager* nam) { if( !nam ) return; QMutexLocker l( &d->accessMutex ); QThread* currThread = QThread::currentThread(); QNetworkAccessManager* oldNam = 0; if( d->threadNamHash.contains( currThread ) && d->ourNamSet.contains( currThread ) ) oldNam = d->threadNamHash[ currThread ]; if( oldNam == nam ) { // If we're being passed back our own NAM, assume they want to // ensure that we don't delete it out from under them d->ourNamSet.remove( currThread ); return; } d->threadNamHash[ currThread ] = nam; d->ourNamSet.remove( currThread ); if( oldNam ) delete oldNam; } QNetworkAccessManager* Echonest::Config::nam() const { QMutexLocker l( &d->accessMutex ); QThread* currThread = QThread::currentThread(); if( !d->threadNamHash.contains( currThread ) ) { QNetworkAccessManager *newNam = new QNetworkAccessManager(); d->threadNamHash[ currThread ] = newNam; d->ourNamSet.insert( currThread ); return newNam; } return d->threadNamHash[ currThread ]; } Echonest::Config* Echonest::Config::instance() { if ( !s_instance ) { s_instance = new Config; } return s_instance; } libechonest-2.3.1/src/ArtistTypes.cpp000644 001750 001750 00000025001 12465467260 020416 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "ArtistTypes.h" #include "ArtistTypes_p.h" #include #include Echonest::ArtistImage::ArtistImage() : d( new ArtistImageData ) { } Echonest::ArtistImage::ArtistImage(const Echonest::ArtistImage& other) { d = other.d; } Echonest::ArtistImage& Echonest::ArtistImage::operator=(const Echonest::ArtistImage& img) { d = img.d; return *this; } Echonest::ArtistImage::~ArtistImage() { } QUrl Echonest::ArtistImage::url() const { return d->url; } void Echonest::ArtistImage::setUrl(const QUrl& url) { d->url = url; } Echonest::License Echonest::ArtistImage::license() const { return d->license; } void Echonest::ArtistImage::setLicense(const Echonest::License& license) { d->license = license; } Echonest::AudioFile::AudioFile() : d( new AudioFileData ) { } Echonest::AudioFile::AudioFile(const Echonest::AudioFile& other) { d = other.d; } Echonest::AudioFile& Echonest::AudioFile::operator=(const Echonest::AudioFile& artist) { d = artist.d; return *this; } Echonest::AudioFile::~AudioFile() { } void Echonest::AudioFile::setTitle(const QString& title) { d->title = title; } QString Echonest::AudioFile::title() const { return d->title; } QString Echonest::AudioFile::artist() const { return d->artist; } void Echonest::AudioFile::setArtist(const QString& artist) { d->artist = artist; } QDateTime Echonest::AudioFile::date() const { return d->date; } void Echonest::AudioFile::setDate(const QDateTime& date) { d->date = date; } QString Echonest::AudioFile::release() const { return d->release; } void Echonest::AudioFile::setRelease(const QString& release) { d->release = release; } QByteArray Echonest::AudioFile::id() const { return d->id; } void Echonest::AudioFile::setId(const QByteArray& id) { d->id = id; } qreal Echonest::AudioFile::length() const { return d->length; } void Echonest::AudioFile::setLength(qreal length) { d->length = length; } QUrl Echonest::AudioFile::link() const { return d->link; } void Echonest::AudioFile::setLink(const QUrl& url) { d->link = url; } QUrl Echonest::AudioFile::url() const { return d->url; } void Echonest::AudioFile::setUrl(const QUrl& url) { d->url = url; } Echonest::Biography::Biography() : d( new BiographyData ) { } Echonest::Biography::Biography(const Echonest::Biography& other) { d = other.d; } Echonest::Biography& Echonest::Biography::operator=(const Echonest::Biography& biblio) { d = biblio.d; return *this; } Echonest::Biography::~Biography() { } Echonest::License Echonest::Biography::license() const { return d->license; } void Echonest::Biography::setLicense(const Echonest::License& license) { d->license = license; } QString Echonest::Biography::site() const { return d->site; } void Echonest::Biography::setSite(const QString& site) { d->site = site; } QString Echonest::Biography::text() const { return d->text; } void Echonest::Biography::setText(const QString& text) { d->text = text; } QUrl Echonest::Biography::url() const { return d->url; } void Echonest::Biography::setUrl(const QUrl& url) { d->url = url; } Echonest::Blog::Blog() : d( new BlogData ) { } Echonest::Blog::Blog(const Echonest::Blog& other) : d( other.d ) { } Echonest::Blog& Echonest::Blog::operator=(const Echonest::Blog& other) { d = other.d; return *this; } Echonest::Blog::~Blog() { } QDateTime Echonest::Blog::dateFound() const { return d->date_found; } void Echonest::Blog::setDateFound(const QDateTime& date) { d->date_found = date; } QDateTime Echonest::Blog::datePosted() const { return d->date_posted; } void Echonest::Blog::setDatePosted(const QDateTime& date) { d->date_posted = date; } QByteArray Echonest::Blog::id() const { return d->id; } void Echonest::Blog::setId(const QByteArray& id) { d->id = id; } QString Echonest::Blog::name() const { return d->name; } void Echonest::Blog::setName(const QString& name) { d->name = name; } QString Echonest::Blog::summary() const { return d->summary; } void Echonest::Blog::setSummary(const QString& text) { d->summary = text; } QUrl Echonest::Blog::url() const { return d->url; } void Echonest::Blog::setUrl(const QUrl& url) { d->url = url; } Echonest::Review::Review() : d( new ReviewData ) { } Echonest::Review::Review(const Echonest::Review& other) : d( other.d ) { } Echonest::Review& Echonest::Review::operator=(const Echonest::Review& other) { d = other.d; return *this; } Echonest::Review::~Review() { } QDateTime Echonest::Review::dateFound() const { return d->date_found; } void Echonest::Review::setDateFound(const QDateTime& date) { d->date_found = date; } QDateTime Echonest::Review::dateReviewed() const { return d->date_reviewed; } void Echonest::Review::setDateReviewed(const QDateTime& date) { d->date_reviewed = date; } QByteArray Echonest::Review::id() const { return d->id; } void Echonest::Review::setId(const QByteArray& id) { d->id = id; } QUrl Echonest::Review::imageUrl() const { return d->image_url; } void Echonest::Review::setImageUrl(const QUrl& imageUrl) { d->image_url = imageUrl; } QString Echonest::Review::name() const { return d->name; } void Echonest::Review::setName(const QString& name) { d->name = name; } QString Echonest::Review::release() const { return d->release; } void Echonest::Review::setRelease(const QString& release) { d->release = release; } QString Echonest::Review::summary() const { return d->summary; } void Echonest::Review::setSummary(const QString& text) { d->summary = text; } QUrl Echonest::Review::url() const { return d->url; } void Echonest::Review::setUrl(const QUrl& url) { d->url = url; } Echonest::Term::Term() : d( new TermData ) { } Echonest::Term::Term(const Echonest::Term& other) : d( other.d ) { } Echonest::Term& Echonest::Term::operator=(const Echonest::Term& other) { d = other.d; return *this; } Echonest::Term::~Term() { } qreal Echonest::Term::frequency() const { return d->frequency; } void Echonest::Term::setFrequency(qreal freq) { d->frequency = freq; } QString Echonest::Term::name() const { return d->name; } void Echonest::Term::setName(const QString& name) { d->name = name; } qreal Echonest::Term::weight() const { return d->weight; } void Echonest::Term::setWeight(qreal weight) { d->weight = weight; } Echonest::Video::Video() : d( new VideoData ) { } Echonest::Video::Video(const Echonest::Video& other) : d( other.d ) { } Echonest::Video& Echonest::Video::operator=(const Echonest::Video& other) { d = other.d; return *this; } Echonest::Video::~Video() { } QDateTime Echonest::Video::dateFound() const { return d->date_found; } void Echonest::Video::setDateFound(const QDateTime& date) { d->date_found = date; } QByteArray Echonest::Video::id() const { return d->id; } void Echonest::Video::setId(const QByteArray& id) { d->id = id; } QUrl Echonest::Video::imageUrl() const { return d->image_url; } void Echonest::Video::setImageUrl(const QUrl& imageUrl) { d->image_url = imageUrl; } QString Echonest::Video::site() const { return d->site; } void Echonest::Video::setSite(const QString& site) { d->site = site; } QString Echonest::Video::title() const { return d->title; } void Echonest::Video::setTitle(const QString& title) { d->title = title; } QUrl Echonest::Video::url() const { return d->url; } void Echonest::Video::setUrl(const QUrl& url) { d->url = url; } QDebug Echonest::operator<<(QDebug d, const Echonest::AudioFile& audio) { return d.maybeSpace() << QString::fromLatin1( "AudioFile [%1, %2, %3, %4]" ).arg( audio.title() ) .arg( audio.artist() ).arg( audio.release() ).arg( audio.url().toString() ); } QDebug Echonest::operator<<(QDebug d, const Echonest::Biography& biblio) { return d.maybeSpace() << QString::fromLatin1( "Bibliography [%1, %2, %3, %4]" ).arg( biblio.site() ).arg( biblio.url().toString() ).arg( biblio.license().type ).arg( biblio.text().left( 100 ) ); } QDebug Echonest::operator<<(QDebug d, const Echonest::Blog& blog) { return d.maybeSpace() << QString::fromLatin1( "Blog [%1, %2, %3, %4, %5, %6]" ).arg( blog.name() ).arg( blog.datePosted().toString() ).arg( blog.dateFound().toString() ).arg( blog.url().toString() ).arg( QLatin1String( blog.id() ) ).arg( blog.summary().left( 100 ) ); } QDebug Echonest::operator<<(QDebug d, const Echonest::ArtistImage& img) { return d.maybeSpace() << QString::fromLatin1( "ArtistImage [%1, %2]" ).arg( img.url().toString() ).arg( img.license().type ); } QDebug Echonest::operator<<(QDebug d, const Echonest::Review& review) { return d.maybeSpace() << QString::fromLatin1( "Review [%1, %2, %3]" ).arg( review.name() ).arg( review.release() ).arg( review.summary().left( 100 ) ); } QDebug Echonest::operator<<(QDebug d, const Echonest::Term& term) { return d.maybeSpace() << QString::fromLatin1( "Term [%1, %2, %3]" ).arg( term.name() ).arg( term.frequency() ).arg( term.weight() ); } QDebug Echonest::operator<<(QDebug d, const Echonest::Video& video) { return d.maybeSpace() << QString::fromLatin1( "Video [%1, %2, %3]" ).arg( video.title() ).arg( video.site() ).arg( video.url().toString() ); } libechonest-2.3.1/src/CatalogSong.cpp000644 001750 001750 00000004053 12465467260 020330 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "CatalogSong.h" #include "CatalogItem_p.h" Echonest::CatalogSong::CatalogSong() { } Echonest::CatalogSong::CatalogSong(const QByteArray& id, const QString& title, const QByteArray& artistId, const QString& artistName) : Song(id, title, artistId, artistName) { } Echonest::CatalogSong::CatalogSong(const Echonest::CatalogSong& other) : Song(other) , CatalogItem( other ) { } Echonest::CatalogSong& Echonest::CatalogSong::operator=(const Echonest::CatalogSong& other) { Song::operator=( other ); CatalogItem::operator=( other ); return *this; } Echonest::CatalogSong::~CatalogSong() { } Echonest::CatalogTypes::Type Echonest::CatalogSong::type() const { return Echonest::CatalogTypes::Song; } libechonest-2.3.1/tests/SongTest.cpp000644 001750 001750 00000110173 12465467260 020251 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "SongTest.h" #include "Config.h" #include "Song.h" #include #include QByteArray code = "eJzVnQGS7aqSXackCQFiOIBg_kPwWpSjn_0jREW44kW33e7s_6vqniOhzJ17k0nqOI4jHhsTzp25086kuTMl7UwbOzPSxpwh70ydG3Nd987c786UnQkp7cz7bsx9hJ3Jc2fqsTO9bEwMc2e88G_zjJ3xtj5N4sY25mo709-NyXzExpxtZ3LamXJtzHPHnXmOnRnXxhQf8rfxy79NCzszysZUl_PbjHtjmsv5bWramH7lncnnzpS6M_3YmZE35q11Z1iRb-NT3Jhw7Ux8dkY4_DTzKDsT687wF5_mPK6-M_e1M8_cmZo35sRrN6bPjblYzo1hOTcmz53pY2dm2phwtJ2JW7M-4Mvcd9iZGHemhJ1pbWfw-W8Tc9uZ_uzM2zcm4fLfJuu23-YdG_NAlDaGKN2Y0HZmjo0pfMHGXHVnSOAbE9-dedrOjLkxNcSdiX1n8rUz492YFsfG9KPuTOs7owt8G0Pt07zQjo255s7EtDPe9KcZwMfGPHlj5nXuDMR_YyAdG9PytyFYrp25ys7cc2POtDXIkY0Z58ZcZ9yZMDZGWfBp_qja8s78i6otb8wZnp158sb8osty3Jm-NePdmJDixtz65rd5ws7UuDP_nmojVX6blN-dgc58m6x0-zYh7EzqG_NIV79NzDuzPuDTlLIzPWxMUax8mzfszKgbUxUN34Y0-22ay_ltvOlvo2T8NL_osufemVp2xiX5NvPZmFcJ9W3cpvo2I2zMOPrO7FVb25kZw86U89uQc96dkbx_m3rtzDg25pSSfBsW7NuYvjcmhJ1R9n2aAIHbGEXht5HsfJpf9FGqO1PunaltYyLRsjFP35l27EyvO7PXVv7Jt5lhY_J974wL9m22qu2Rcn6buDU1bkw5351B2W1M2Zq9elJQfpu9tiLtbExvG9O8rW8jP_s0XVj6Nm4JfJved-adG_NCor_NuK6dMYi_Tco786SNmSSHjbnzzuTwba4jXDsT6848Y2dm3xiRZ2P6uTNb9RTOtjN33Zkxvs0f1dO-5jV2psSd-UvN69mad2d-qXn9op7Czoy-MSFsTQo789aNuZUU3yaeO_OXehnpbmP22iocO3PPjfmlbrVVT_u6VW5lY35RXntt1e6NKantzL6qBfXbmPfcmdE25peqltL827hg3wbA-zb9aDuzV15u23wby1PfZq-tSFobs9dWpPeNsfL1aUa4N2bq8t_mvnYmlZ1p-dsgoe6d2WuruTOnROnbjGtjLgJxY0hoG1PLzsy4Mabojdkrr1g25gY8NqaOjYnogo35RT3dO7PVR3GWjUmzbUz2UXybu-0MbrsxCo5P84S8M2VsTJFEfxtLLt_mCTtjqH2aSnrfmK3GqaNvzF4BdcJpY5Sb3yZdO5PTzuzV014fXXFn5rsxv9SPQt2ZGDZmCgDf5o47YyH3y7CW986EujOy5E9jd8HG3GVnIP4bA4R_G0F6Y2LamTw3JrT4bf4_rT1t1dMxd2avrerYma3ywjM35jddtjNBcfZtbO_6NnttdW3N9vH-RVulq-_M0t6fZt8T-Af19Fvtqe6MHSDfpj4744V_mpLqzvyinp6dscT0bSzkfpoqef80DaD9Nl3_-TYu57fZK6BfKlN7fZQ35k_6yEfxbbbqaRx1Z9xO-DYx7Ywtpd_GMu-nmWffmX0_YT13xuavb7PXVpYRvo3tbN9mnBvzSz_hX-pWlnq-DS7_be4j7cw1duaXmte7MyVuTKx9Z_a1Jx_jp8ln2RkbTr_NX9QTKXpjwrMz9d2YX7r-UtwZpeqnqQbTt7GU92n-VCH6RR_dO_NL_WhrLD9-mrdtzfvsjO2Zn2ac585cz87stZVq59NMmxm_zS_K69kZ4fDL_KKeyP47s689Kd2-zb6vr507Q-r4Nr-opzvsTIzf5o_a6i-Vqa22-ot62usjG5o-zZ9OY2310VX6zmyrWiFdO7PXVn1r9sorXjuz11Y2f32a-Lw7Y3Hj0yS7Rz6NiXRjUtuZP1Wmnp2R6n6bfcegxZxP80vdykv7NuPZmN_OW8WN-a0yVTfmt9pT35k_aas_1J7-Z6qnrT4a896YXypTe_W0r0x5sObbbJWXSWdjrq2B-23Mair8Mr_VrXba6lqHvT7N2Jm9Lrs9tPFt9trKIyzfxkLft7GB69NED5l9GvvUNybHnVFef5t_T5d505_ml9NYS5x9Gg_HfBpB-tv8UntKW_OL8vrXevMUHN_mL9rql968rUlpZyzGfpt27cycGzMI1I0xID7Nb-pp29fnsbkvcx2h7MxeWz15Z-rcmF_OPbW-M_u61VY9XTVtTIDqfhq1zM78d6mnf68ytZ908ZaN-UvXXzi3Zq-Pct4Zl-TT_NK55xiGb0NS-jYxjJ3ZT6sgtWxMKDvjMf1Pk8-8M5Yvvo3k_duIS5_mkdp_m70-sij6bdq5Mb_Urfbq6Ze-vrQx9ac89WGahc1P84s-sqPp2-zrVv9d6smWw0_zP1Qf7atLd9sZKMmn-bWvb1uZGm1nJB2fRqXzbS6LOd8m9535RR-9OzPPjfmlbpXaxtzS82_j8bVv46P4NsDhxuzV01s2Jlk0-zZX2RkF5afJ4d4Z2xW_zf5UlC1a3yZeO-PG7LexiPRtPOr3aYp85dvslZfNX9_m36tqke42Zq-89urJTY5v88tMib4ze330l6qWbYPf5p47I9P9NL-pp_5truN-d-ZfU0_HuzPugG3MX7TV8ezMvm7lMbIv8z-1MvWLtoob85e-vuuKO3P3ndmfqNpXpv69E1Xt2plfKlP_77ostq3ZK6_QdkY58ml-q0xtz0xJhD_NL7Wn-O5MCTuzSltfpnjc59vstZUB8WmqQwu-jQMtvo0P6tM0y0Sf5t_UR3_o3PufWV3a9-Z59O3b_EV57bTVb5MAf-nrKzvjQYRPc9nS8m329aNfpvnt9dF2mt9eH_nxn-a36tJf9FHZmfJuzJ9OVCk3Pw3LtTPbytS_V10ylr7NL-rpF300NqaEtDNpa7Zdf0XH_DTVIRzf5i_a6i_qyRLxt9lPjXCj4tO8V9uZP6inX_RRKTvTxsb8Vns6d-Z5v83l2fON-UUBpZ3Z9_Xdz8Z4dHVjbNP6Nn9SQH-pLu3PPf03VZf29aM4d-bNG_NLdemXqRHbmXtP2xlLW5_mTzplXyFK985s--t-mXbuo_g2UrBPk8-6M3_ovvuTxnEf8tv0vDG_aJw1OuLTvG1jflMxdWPauTW_zHbIO3O1nYlhZ_YKyLLYt_lvmvzwJwW01zi_qJi6M0rVL_ObxtnXj9w6_TS_nDCyyPBt9hrnl3nmu_rRb6eTzp2xpPZpfjt_tFUxDh74NPGsOyNt_DTJAtS38VjGt-lpZ3C9b_OLitl3wUlXv80vVZ7t7DvHw38bS3nfZlvHKQ4O-TR_q_L8pYNua8bO9HNrfjnZtNc4-5NNcWNev_zT_KJE-rEz496Yf_H8kUcMv81e4_xrOuWEvG_ML7Mdtsahfd-m7Uyo3ya-xPBb3usIR4uxX2O8MR4xQieulPLo6SnDH9f3vq8Uc7ze6yznbOVObZajj_D0J_Qj9nGHa5yt8HSf-w3PPNtbcrp6dWcXEsKf55ZgQWn92wAPG-U63-cp-R4lzFGvp7bzefN1tzvE1ut9n3eod-j96p5qi9f5nPmR4I0cAzr86TWEq1513CmfWamSYok5e2pz_bO1_5SstN3Fa729slxjvuc77zCgKe0825pFFst8HtYj9zziebcx4rhbflsdNdzX0Ut63ifcc0TWsNQWnrfF-33me14tlhzqzLGXqwIIISR0SG4wjtcgr_29K1DPpedxxMlSh55KGnngXTxK8La0yrKkftbWY6lPu7w5R8SPMI_59CvOK44-uYr2pPK84fSdQu-TE2ve0uC-auZj7hcCxmM57bGdvV6Zx9KuVPvdHh4TKz15ElxNdE-ixzP32MYFl-RRvYcb47j1-8QRzjdwi-cR0uzrj_tz3c_67Qx81PrtMArWb6-zXjPqBuu3ENvS0xXv_E57IfzkEkdiHeEm8WXtW2GV3uPhV7pKOoDT0lmRO8wr43499VnfEsJd540jDJhLG-ElKntuLR-pkoG5G7TOjM_V8bR65vE8T3vPeEzfKBJvqfAbUzmvcZN1uYgeUkuw62QQ1xHq5cDQnst4ZkkTr8w85aNmEqlDw_iX7z1TrKOHNnjiOaXIo54sUpstvDyB457e0hvuhgO4-9nKWULjGuXMKbceZn2MiHeikSp_wEXhDXM8eVwplMdFB3Vmyu9b-tvxiLI-pr2-6ik4APh4cMqC0uWZ4fshVrJUn6wRkY3X5Psd9_2Mi6XkKksKR651xvKcZ0y2D1TUDi7J_z8nTKKPh3-GA_KUCs8uZELkud4HXdtKPiKP6loTHNpNiLSU7o56LFfiiY_ysO6sC8otslJET-JqU2qjsy7PfXE5aRIq5SKOidcUau_hrYiMMm5iuvFsCQC-7UrjxhXLLKS_cNbniuf1gAaxEX2sSc8AAb9kHVvEL8OLQibOSgGplv9NQosQ4enk9mQ-lRhC-iViCTdIk2WeqNuOzkwtvrMRR_c9K44FAo02eFqPy_fkzA95ltcc4I8f1J8jOxzHvSJC4pj17L57gqCCcA_SowQI_8_5zqz4fbL0Vw7zPEMOpcx89zs0BwvUBEbwa5TlWQC8zrdPETInZ0KxOuHET09-0i6clifB1bdrXI0leOf1jJzuQr7Wk8An3OqOMCzrfAc-GMJTj5vHzUfF_CbQe3YYdg8VcHuuzPfiH_fgzmq-E5GdCNQhP-gz9zAy0J_gAx2ImDdLjY-VyzdXRLAr16O3O_GA7x4j18TC8TPyKRAYn5OnSCg2lqCBr8QvFK7iMDgxoMMSjObqzNqfGFk6nsY4Qn4IgpsAeCqoVYFdgiM33Obx3V59lloaQJZ6eSOUEbjjhsFYoLm_NYyb_-mlBi4YECxg4c3i4yjnhfPzH-Z199Fw0pM8qVv02Qx6_jIWyCAIT5qYgBKRHh9iJjTg7Ol8mM7ROzd4IesCHvaQmxroAwqRGojW8vBYgcxyNR9BXZnzvTLZhpCDtg0yb0kkIZaIgLsmoHzp4ERTAOUBxkRSxMsri9ES0ccyANpy-icBjA93FwXL11gOffWhAUSE3Kyx3jh15tnzVetmYgQfFz6HA1cag7U9Gl9PQui-eGHkgpu9rDFJ-r3GvIx5oLD0m_-SAUwS_JHBW9IDMUWUsPYXcV34cn1jXBlqwC3ewM0R-EPQ4LxeMTGE0yY7gB5UGNkXWkWu870S0QC2PZXgBx5fPPitB17cuVjum4-5H7w744uk-yPdranU4Fzl4gta5zJIh1coZPALGO7N6CSxsQIHTiTVCQO8vll1sLZ10GimvrIMXmdzH9ky8GN8lwfNlbijgs_xLZ37ZwFZxQtkrfMi8b8RXQ04I72vSWwieEojd10A83Ue7cp8zEXu4RG39F5XZaHILmm2_uJtvlyH-0rPHAEuBXANfkguO_Cq8zz4w5d1IrcLIw8-Dg24j0gOA78jFIYrJJlO0hfZ_xyTtS0OlCC_DunLCkTQ9Dzf9HJNgMp88uz5mvw4kLIGtwHaPu9d7llGfHjCsR0D_GOtSDDQv_uEMgVCG_5Q8FSIAc_3XR57dwjDwSIQqXCv5335aeN6yc3jLWAumhcil2GDLcLtJnFKJN0ZJgm6S4SgPIRSdL77W2aPeGSHwOCkNwsWEr90LABcDNJRB84J_Qtc4-0r_HDdJ3W4zukasyZ4G4tS5Jc846RfJtATrEjicCMDGpYj1VhIu9zYGao0gvSCG7F0Z-gvWbC8iGNoJHQKXzXw3_YAIzJCGC7pq1TSHJgyV3cZqWiYWNIEqcLFWg8cFAc-M6mSRFoGrgi5qLBFbo4Ae0rCdQgUWNTxjoPVsNoRY_XFhznhLeTKHO4MC3j4XcAVU8CxKtFrFfi4hSyH8MGBJ7njSXgciwBqtd5N0aFI1iQh702AxFafzPOBI3G_8yo3C3wd7X1TiCDiuDNI8MK41jsFbTMBhGp-M2F4SdESUM_1DXBwkoqI_tK5OyL_JmvOjEMjJ9ZMfSCpOIbmTCfsaYzWWdDCx83Jo86kNRbqWjezltM6YVuZ_c1AzdNmIHtmvDVF76E2WH05I_g_YcQzcRW1gv-zVZAcYLBKeT_m8QHzeW5ZIt9-4Q1kFwCJODpGy-gObo2FB1FBJZLc0YM7YQ9gjRsAK-TsCSSUec1AYHPj_XhfV5vbszsHLIcJsgTzhsJeotMVwCmAICRDgJt-UCuQNtYFLCLflso9pOGzIX3oZA2uMs3m5P3_MgTI0_7jZ_-YLM_hyslsEccKL4kC3cvCiE4xA9vd9GXqh9kibEOOOLHQzD0ARAl97sMR9Mjpg9iCeaNOIL7Iv8xd-VvwOSFDgCxgBl5AYug6JL84wQrCoB_8jBQKb7nCA-0OLvp9o2naC6W4gI5HDh-I9YPketwkE6CFB8FiIHtICgkGQxYSVE_i09JM4F8T58C4jgsAIp2gmuAfyZw8C7UwC9RcEXPosyFHWbcAMQXPjwSLKYUI8z4IJBYg-_oJP5kAMJ3G91n_LMsiIWD9OZ1hEIExYAk_kf1n1C7YiIs9SNWEdyFtvVPyB1RS8p4E-DLA4RIiH3e93AZQeBd0BDkY7TLLajUYcxwPIU14oq7xK-AGBCDz4fGlF1u9DzICzxEh0WXvSEgwukPfZJHgDahGAhs8_WOANujeR-RNBCDPBAyIiLBOXoxkDFIqogsCVdA2EXUNozL3z5dvAg_ehnIE_69G7gNIE4AzWRcglcSMHwfEY7G_ikXgHst9IEh5tqDZMEjJ3-QAIp08GQ94KUKdRPt07p8kjsgL3F1_y-3iWtLzAvDLWuCEBRgNsF9Ys6khI5HPCvbe6_VERAmclCsAiIc4jcDovkBmEoUqSzC2yPitUfRXNQugQg15PtdLrITaVJlIhHH2QaAiiABamcuFh3PBJEpYMzQGWpROd6XwUxQV2QcGBZ0pFwqJZxPnA1rVA9zA_6YsCw2fPNOTSLP4OExlkuUDIgvqD30h7Q3y2oiwfOhxRMUhCXsgRNYErAoMgjmwXwCQa6mo43JVcIfbfN7JL02nkNTLOVAsK_Sto_z4ogkR4YJhls9Dsu4KkADKvGYEaHV6dcCZ3U5RcJUZeZok114amNQOMBmHJejcSkW2EfEofKUe_wZ0I1LhkLNmhQxhBzstBX2Zy8RfuDx4du6KNhIjsPjC9ghKUPzgv97AcOXBBq69ZFL3BYLwiMnY6-WLbihwHwcBVNcTel812otcuCHFPBTYEk8o1tyC4J_66_tAUZv3k5MPsKiFIBlIVFIpxFRF-74kQrkbPBbZf4HMDT3HIo1EPLs1AKLALKNBRFjdpFOwe0EWrpMJAhJBinrrRCt2NOlpBkW2wSaHOSaovfnnkDxcDHhAFuKqBAnRTk7hAUbFizIJFneQBeu1TgYGVhK55YbFNU4u5gHKUMIwI57NCA9gQ_xMd4kGdBZc4qk-DZIPaAHxd-euAux79VLy3-GdZDtuDIjMJAJ-H1AcRfyDIr8s0ZyQZN-fQVCeSMwipIWXTE7i476ddvzUE2VI-JwxCsXAPuSPm3iRRDnyoAo43Mgr2bcDSbBQyTBx-KVYYwDrE4kwBdN59hDYKXm-eYAwiXS6gQQ5TqYwhF1I3j5XjKx3QwNO_EbIJg8Ix-8H4FcfAu-ACgOMSXzlEmECvi6Ay0GhQEwA3JTdOSQij9dZ5EAAtO_CRZBkDcUD8-0ZnIPj4O2BbEcagkMBXwFYwenOChsNK0YhNkj08v6ACEiIDuZquVWVfWzgL0vOn78tukN385_yA_CLgOQk5H06fj7xrdU9FEBtefLl1I6n4Jg3cpdvw386wV5y8r3dBWzJxCuxtbY6O6QY8CNTctcNRUoCAIwabDSROdyRRfuSseyZ5nlCx08pOcKxBxaQGzpmbvBd-Tp59ZEcvQhh91hRXCupV1QrGMsSHmAfgcU_qsQ-l2Hcgy1W0vsFOANWAcHIRb6E8uL0J3oAPMFByT0mUhw4FcgMZG1O-CLpysxcESVkZBUEEQvo9uGVuLOBZn6V6E63Nu0m0CAKMkDrhDbXEygGTRKwExfINlQgNBFYgyr2Cwc5su-WeWKHjTTxcd5kPdYIfnVMYpXsBWAgp0j3GXTqo1cIXQKWVncDWY-P4QGQaoP077lx5Y6sigPwIvaOEzZPuoXCh8uXOHOTXThiefsrGIK7AcxB3kVnkV094e0Pz_4kjxbAkuRtU5FJP4cLBs4d3BlwCxPVyjPgU1NcMmuGk9xwdqgbi5Byd0cLIUIU4mZ4DKKHeMPV1guGoq_J5hnB_fgHHZ6GBzx3961HkFqEng1JQ9kEbOC5kMGTZA9kr70AYLvGAJZkdcb7wBRwrFnXPqJ-BFnLAbeDunRDCGiGz2dAjrQt4CZvgFsHAsl84Sog-t1U5lwEHHUBPH4ozeOpIRggE_jUKymZyC-0ItI1A5Lx0G2U_RF1SaTzREE2xCzkH9ILCgGnoKUbnLg9j8gEme_V9lgR2KYKxHoY4NuBcgWqYGq4Ja7NB10wQji9W4V2wcdJRub63Woko_Z7SR8iAfbUXIsQXtD9esF0d7tfEQ2OTAQA4Ty6Kb-1p8t_mizyTkAS8I9EQxkXaPVAPotUCzHLCpwwOdJMvAirx32YC-fDSdHWF4LLXViTF_qW0EI393Oq4AcUjLtBy73ch4DyuGtpYwdBJqW237gcaWbl9LHaJt0JzG47QrrJ-awTJNgtsovHfyKfyFgPpI8EPUSisWYKIsVTJgNyS3g8zkbynNAkIMk3QSCEJ7RmOHS8kQNAczJDvCB6PC7dMbmBFsnLkO0cke_cHlQC3CCMiXgIfIFSB7dkoXT2Fp7jRfBFeDnedsL-O6waz31OMj8cAseIchqk0RxIt6fEtX2LE7hbcrf2j2mEUfqPn_1jyCTwUfexJzkf_oXPLrF-G-CyFVYeCgR7LU-98LTD_eZp98dzuGIAbOKCBhrrgQgEVhI6CxAJEKWcbigZcDBEVgOhzO0QvjcElXDsraG0gYd6EY0GV7MCJ1YcwL7bvG7pQzTBKSsRJF5w6b2U4D41vqhKAcjEOlc5ndF_CrE8pPdBTJC8EEY8ViSbCR4iFE7ckjAgWCD_fDyY-wIYt5wKPWNigkXEAxEOXLsjzmVY6SPX2Be-lttPcc_0iawvoIkQAQEeWMcDHb9ENIhsXh9Pdmnk45vUjtQnBV3kCJS2_XUA0fDVaojrdybLQ_BIXTyp2HnS4Ad0lNwHmnH5D4QezynjXCMUrB7Ao0hgTRp8rOTBHxffEQkogf9cI45uwoaJHbiwbXkF_7IkEMnQL7yHNVPNQkkEjTi6Mvl0GzrFcFXWupdr5IlYh1eEBnEI7yQKCBlUFaoY52V1HBzO0-BiI0Ai07NomUiIJNL4AEH8W4CWp5tb9GCPSZZU3k5y2_RQTeDm9S60OIkXoci1ck1ksEsffVgvMAXPtLh3SGiQK9lYO90blD251TAsSLqHwmp0qGviI5-8FhtYq4_KQHd2d3UqJ9CKZE4iEe2PwGDBWYd84GwBPLVmOKsFywuiQV5y8_CUKPTL4jDCdtxIGfdB57mYwGv3xonjuzCA6NrITDBFQIO17wOEZOHNCfBidzeBZC70vYg4cifYBR7zKNEqYaLH3V7oIJ8sJEFAwJOJRhaTb6sWyerqW3GX_FoN6D03nllkNW9901onMRN65HETTrgsrBiNiXo6eXYsC8nYBOde0uFJFgsCTkzBHRvi7roLgO94tFz0OwLKvZ4MP-twFe7UaulhbQV-BB0gXnAYnAJnRBTD7Iu17BDAa7cUK7SLPIqMnqfF1pZU7vI-Ah6RigwGZp0XguwKrVuzUY_0c5Vz3Qq5p3lg6DNPa_DFy0LF08g75IKMujaJHUsuJAt3E4aejzMjsXA72RJcCOe6Ziw1Qbdfvv3EuQgDL4NfQYdCJQoOYMbWxiCzhqSGkaRjsYJoIBECkv_tBYqfwfycSL1-nq04PBOorqpr3G7oIy9JIS8iM0NBLAtdYwZfgytZ5_nExY0h5_jmTaJgdeAabm3f67e4GWtDbiZXQ7CFNuguecl9tfKDz1CAd93We4XmIjW3yVHhRwKzL9AdunRZAhiraAVjPfNI45Jo8MfJdePZQE7v-VM14-rIBoBIfXD1G6KG2iRbusmMCLyhUveJ2s4LdyHh0w0V6B0w-S5o5k8jFCP3DBEbuIK7MIiIWdy2XohJ9KPBMrfUzjlZaQTCeiEj_5cIuG9yHytCRBOw61-s-wWmCDL-K4sLt3Br1x14fB5PHNZDuTWkw7pzvGiCFG6gHz_zlaxKlKdcirI2UOrgzoN6j1boXlX8tD4EseaJx1bdUcxQu5QqzOQlt7GoXPhqplhbq-AjGnXeJGlwFE0KWUm2h3DvwKNlNzCGtEkWxh4FqHHcTwYTr9rXKLo1OXcSwgfsLZ0zkEnG7Syqm8Tttv1FpBKEcFkU0RxkwlfmVg3fy20-wpCfVVkrCF3xdPyeK6_rmi83N8y8PBn3B83dK0IzSfjBN1n--5KxoICr-y24ZWCJGlndq4e34acACYKSZGlNcuaDpHCr0VhSMvqF76JuLhsZDqhAIa6QKQ-w6jY-7pncJLOoiyxBCqEnAmSLtYaHtfWuKZI2cT-Fv3iQFREDvvpo7Rpe0-02nAMl9KIUXqD0dJsdQCN3nNZ2JcNcSXph-28oaNPsEycqLzew3kcWe17IigcQKQawTQP84TGyJYVxWhNDPQ8bYwAOPDCosfgmf4WuaejXMxbCH-EzVh-WPTFKS4ghfD1HxCjuyRfDoUkLcNNxxGEd_QUxkNH82NCpkySE9MC6xQatONzldBtl4pD4jmmP1PvCZWCUGbx9uDxShwmETANRtYjr-5JgZeRr2M39WBvG-R6I0rROSTpHKJbRT9Mi4VvIUTxW_t0DOrUsJRn85zP4JelVYJ1Y3Bx68A7QoL-gKAkbegbJcSdMvgw0WrlMPPyT2CDDwjohJrh5J--_wDg5vYO31k9xJK4OD-qEg_vXfNddayTZo6JDcIgDYkXcdqOrpueGX4NsVxbxkOtwJTSpWyNoUYtmCVltf22Aj8GduWiCIb6k8IEQLNm6TSYRRQtnpCe8hasS_UD8mxXE8x-B5oIeDiIve8YD6bACMRsccBiCJJbVMIBQcai_G_TCiM0Zia-oHfJfAMx39Xc95C8EWwuHFZ4wLMOjk3NVt0ZTCGSI1TI2YDkoOKj_Q8qFDr0jgjmKCvSq8pS_fsiZ5-tOrkdIKj41uRpbqmpWhbjHBG-Cj1vShAX9w31ueGzjQc1smfOHAMkqhp1vLHWWJvANwC_A2YJtCjhJBsAeN8ogrPmKK53DfC6rDyyUonjaJsLP3Y2GIN05WnKZj0ldeQzzxHNAJLeq4CWsOGkUzYGsz94rupKghDujry9bgKYFkPNIP8ngefgsOJC74ie0udxEG-hmnwBSICO0TDM8GNKAhcdA1GU4T2n8GZfv2ZbUSH8THmtoHxAnaMM_xjLi9R8_-8dACJAjnetSghM777BUVnH4SWbA22DEaSg1H4C6C1YQnmvYOGblwKrntNmwWf_C78NrKQP1R3r2I87Visg6QLb--S2SqtlMcrtPDPWHcCvK5Sxon_VbjNW9OaXjiTu9Hngxjr2-6EIJQCHS-_PbFK3SpmxZARbSy9sfgHM1-bghy1peHtRbV9XEO3LH2lw47fIhlSR-v-4IfE1vAELOfPmGIUf2vP3imSNpQN0hv-X2Vt9HhZgG3wQmO7llcxWesNpRzst3QqNi0DfEArz2hFDhYs1ykOv64jtOlQcu3BPmp2Bk5eJOO-1KPJ_M3V4_a08qaa9tbnadIEQk4267riWGoXYL1YPVekcb0PW2SGsHKU4ylTKAgOVxWnro9UEQjAxXaoTPkNnV25alAA6CpstxHhzvDshoHgmIbwFlLQnL5eBarsqRNO5AHYmHBaStVykmd8AgZ5BYgcHd_kzyhuHG5OmxxqUDeLmY7VkHBPgF-SsXQPQgjuERYXQPob1kmvS0ih8e92tnHNIlhaxFEwUPDDZEB2tJUCYSwVMA6jqfaZ5sh81KMMNV2i7m4Gq2ejoc7Ea8Bd_JAKy9YCikeHliiqB8QVPwGAa4Cvg5a4YIIun22_JH59OtaoGvtnwSl8RAHiSXAjlv7amnaeGEbpDRbY62lk_WgMq-_V3vMcUXyS0RLgZn5VmFpcJKDEsmnG6Jt3QpT1T48Ex1i36A_Hoq7DfMxurbZwsAQOnT8OWaFr34p_g5HohbFWg5tPUkCSB8YfXA0Z3c8JfeO_vj7m7qWPoAUQFm_82LMk7u7sJSWDcWl-DEq5Gs5HWX3wqVvZW47LCNDq5X8dDbtIQ451tdX7zwkkMQVSS3AGfFzVDHbvMlUlC9TbvQa1vAOnI1wnUjnzagovUFfnE0_jFL5F4L-QTMBQXd5iECE0jVQVgdDeZ82SsBQHbUESnAbFpg_LEeltFYyL42iJItcd19XFJunRkZb8OU4hOyqiixRQZ-DNPIL0QctciX_t-fzJq6u44MRD_k-fPJxcbDaNHgdLs0utuEXiqybV-dOVGVXNH6ZC7BGoIcoMpM_cp1VQihl4AgLtf3HqtL73zLs77jQn-X9UXu7CH8bne5x3BEsaOJTCO9zGlN6FjfuzAMflxsSMXbbHRDuLkrCGLOnhC0sF6YsLVdMtxbzwMl8rY-lRHCMvEFqJLF6m2tRapx3rYhc_tEAnF6r45fwgQNd6_N0Rvn4PYbmoQURT4DzmK4gXWItL2-ZAWxvZyDTCjyn1xmsIbe-AOSrg0eqz79gmtATzDlxVX74ik-3MHabLZHC89CQ0CbWLfrTLZkP8iTBKI9q20N2rlww5LuXIPC-7QzmjXAz9S85AEbC8Md3BZAa0W3f36QJwUTjTuJ6ewviBktZw3rcGXE1-3ajPogeQpuPA03ty-3nQl4CWSWsgATUA0oVipvGqvOc08Z0msvMbS3Q_NOtwbSawmNYLZzL-C_2Y7-VWkIt_qmTDD9_MlWlYd2ERnm0tvyFssGDHcYNjFlQx9_zdOxe6k7ioBs7ztgC3efSyRHC3yRtO8YGrAOwrLWmUU0VF8oNsCI44p357W2mcEbsiRrdVs641Nx4X7iDNa1zheid63-TSipfXTcsV2svTmA1AF53C2cp6Bfko2tCHQAo__4xsrOBxQnW-RMUvu8OMPCg4eM9i5GsZrpomKd8OGejQU8rDbSVRbdbd-EyeAkYCTCwMJitO0biv9UO53JvHZmD_ti5hhm4rU1ZU8-1P0m6lDFyAokKqpYjs6jLae7l6ctmw9Oa0M_9BTOpjw_V2vgPfiBcqq5JTJXgxq6I9hfDQN4LtROc8O9rhdFvlwyvOhy_5G1GTwXh3KNCME7Q8eZyCD8dthScqPkQnVzBfIDxgR7Hjsr2a5oGfgVBPCEa4AQoOWFO3DFfEy5BJ_OvVnADtluhADuIhgIiLhOtJJVWUtfdxETflhUpL0XC8ke8HzxZcu0iAeYu1ObPaXBSowxbAF7hq1aB-wSBuEGx4Nqt2nsPgmTE99uCIf3dr8zPOgce78QtJcbuTYgv-71VOifZWoC4ron5MPqpSjJT24WirzoNhEwR8ARpm01kHuPMu8itIbgPAD0CAwsLhRlAe24z775bTUzHsQxMcBH4dfVfnS0OU_wafJ5VuWc02QzAEmyIqrxsRiUFPAkLm6_3O70PqEIN_BDEuPBKvK93YIzwv85D7-zPDDfVoOYYMKZtRJgw3SDOiqS7w5XIV3zICFwwHbwBbP6_cVtA518twQGP1BgqF2uTAhZhe7B9pbg5na1kxQtZLmVZ5ItYD-lEAUESD7mmPZ_oRO6TXsn2dxu3uZLkZ5ha8G9xFRBFpJrdds1X-q0w9RzH9Iko8AN6O7GDsxdUWSQXgpDn2NB9rOI9nOdhnC_X544j8X3g592RhdP_SExoA23Jxrtv5WH29WJFrI9n6c1ofDN_0KusrUKiCGRQbplE9A9JelFbLo1dzbIACvMFbzugMQAu4AE9tciGvLpxlMP3HCgHRShKACpB8m2uL1Orsf3QNEGkU985ntbzGrqXpjLddmFWSbyd_5jwh1tWfy_fvaPedzXrngMQJAtKx6ujNjNFx4eU0Lrk_efYRnvQoGbujyCk275qW_sjCM_96x6GXQ0R9CnnwWm88j5J9zbFgFWwjlBpBoS5bS5Cy1zK8yR3xP08ATSOr9EzoCBTKdEN5R5AWEEWkhSslUjPbfigyxjeao0eFBy7gbc8OaJZf4hgXRbsj_8CM9zOIgRp8mDRMhlSwvjw0PtyXHh7lOvDd51R7YR8ymQGRtzIetWn8kk8Xz1EfJfdCBDcuO02TANYYTmcUk1uZHB4gA7UA-UKa5xsM48xXK5sy6jtjMfhoayIYudVmRtKzqtoPFjv7shppEG-ASpMbXMA75_LsgTbSwOOcqaDZr2gswHS_kkCrLhZUcfHiadxWXxP0h4BMhh15dddizIa4MD5MDu2WQnQ23usYY3wobMl0Gyb_9hOFxSX7jNVZJWfWUskDZY0xuqWjzY9JDzSI8AODEDV4tQxHJaKoaoEolxUbLLFv55cRnd_knWSye3Sxh-h0-B8hnpFAExWBMUzCbWgycI6cWf3Ia2dSMMEAq4vt0NTvBscvh8XxRjRpOt98feMLQaHartgrVkfcWO4HWrgix_zpIhv0l7aJ_mWRH3mdeeyBl_FhGEAmMHpAdJyYe8rNKbju7v9J5DquU5i9dmKNCZYIc5d9fvRUKxgJC2yvcUWzAAV48x4ksw87U1bxMiAP8AkheEDr8nbdSQ3_99fAbu2mQqp4y4yi54iHbIv5AsoDPf2U_CtZHFcqIHDp876aAZgcirSuZFf0LSRQ9Lsp4sKbbnHM_a6wkQbXtGuoOHUNGWe4t9d9HtLwPwIg7tU4flg0ckvifZZyDFLsOa_r0UdLA3x2amUC_yEF9KDkFXJUsYiLuHBZ42qgO05BJ8zzNhYO6J2yYrxXigDUViETrG-ngeP0Fnr3YgzccJ9-R_kPkDOg9Vtg4CTA3lyHFZXoOFosP5PzxY0pYVWekdqbAJC0gsz9Ggxu2oHOvIEY5nA0CFgtoCUj2bXuzWPgKcgvgKq7e0ZbdXeA4EY7BgXNHTF0uQBg8Nmcs1r6e_fANtAyu5-M9kzPzzvQiXea6rcm__Xh2F-A4M8TCEbcQEVAQTSe7wIMBT8KCpXNZP10elx521xz1Esmta35bd1LW7wOa_dnY1OriW37UQ646sgINP3XonEgBXcEvWa-EJFi5AzlIcbYRusyZJUE2PD5BWL_ex7Nz0qNUDd7FbWI2-KgPOJyBPo0ifF4CXHQd5PMrVOr6HOO1P6rf9VWBXsNPPPS8e3-PpsdOuc3ItFCrEZ51hyqhC-KyHdOByuM2ECI1VIKjPpXixfi9ZwclYaDcp4ffjAnAT6e71rUPw98rziaxjnZbYeRZvbNb6J6DpVhxiYEzUyx25KPKn0R7kzG7vw3-BJODILl4A_iUOkWYqn8M8UlbYnyLzQYwmj8nYQcYiNCvgkDuygVtGPCyPoMFexvQqzSvRDo7Z4eSQNuAYWezpu44r5MPaIxAyiwymX1bjlFzwBsgQz79aYPaCLU3fnmCFGKGi-RsWw25fkH-gHvhk4N0TQ6UQ-jdAbJdeIbwqwubniNRh9UPucVjF8PAvuu95URxFEY7rIl9G_unmsYM7uxH38jDMDoWIRlLz0Dysw5KeHoqwvsr6do9wZOuSJwJWHORB8byJ3-ix3OgudZlELCSr2D8Nc4JC8lD6I_oSW0O-7JuUn3th-9EI08duUrAaLUHuJueV42wworHCZWVskHfaq9jLCo1mU3rnSYeF2XVIaUD-hycnET7kP9kmXztYpCgs24kQeMpKoDZLP8N0MT3ES1RVu0XyWcQwIg-uj35EaoDWrx3oyfayG4pIMnQWP6RznRALts09sPpgrzDrDdnjv-JoV-2DYKlkHvBJkop6yFbVIUlDQo4gChDV8HN6mgiFoclRHs-foORJdign5DoCG4ggrURwu_W3nr7Wt7oPDwTgW1CB95LaHwQ2ksOXLtx29YPvMuhoEf4J885y4XKuAx4IRpwYLoN-TTG7vWOpIXregZWy9At5C9zG7SsDrXDYX6PEf7iAoKgFxUpsngyzXwjKAFxNqT2uSIzyLcB5SucLZXfWafcl9wOu4paBmwysbrNw1eAkl-3tjhW4pS3kE9vpql9LurhIC9Zhk8fDCKzz5UEhXvO9CsrNB7o67cXdx1c4uIfa2moMdVOWW6r91m086Stlx9mazf9kgoKc9nAjZAXnf_F7LjwenihEjINf03ot_A50HqhKj7hy-bDe_DR37Jt_kGDHsfDJZIO65Bopl0UEoI1syWS1S6mfAcyK0C5b_0Fo20Nc0PRaBOge9KyvzbnDArhbCPf0RA_cvHlqdhy5cAvuK9i764QBEjAaeb6lT2XujDwCG5LlSLBs1h-a8HigDqd0a_JYnSpuWXsePTsaICjv3umJPRktD6ZI_m2XwmPOMggRwi--5DwQyaOWq43RZpSCD8OgW7VJL8B7Ufh52Hmeiuc_CKRBfumXp4Fh6-1YSQm1jBuhmcgmU6YaESkex2I1ThPvTTomWsiYnhW7YXXFjcE8ztXubudmtkFBRLE5G_eWPPP7B_JRV1_Lf5n7BRL-42f_h7EADJWxn5dbwhdY76lYO80OaGNiYkJcUZFBF0IJoSGBLU8kQt8PS7sPEn1tg1abMN0hgwyCbefPb8NpKiO9cr_Hs3oZMlTqJlvZufrAAki3P78Nbm0l65lQHw_yE7q2c-AT7rkiS0geUAyo-E36h8GRiIqz9cfPUaJxeYyq-ZYOD3VaPo7ZCmkFt8AGD21ZnEWDEKVADmnbLih8CA9sXKRHv8sJsXFDxU2R2wOWGCsj64sEFYnCnDCr6_VN19yGh1OatbMTdz_6etRu93pGQGgE-EwT5EcPAheIIDSjeFjRHqgUIMA1_Zx1jwRejtw3UXR51DpAdiHQuVo5Jvl1917EmJfHgJxBOhEAq3fPvtl1ksCzAsAN4vnRiYubGVAd7hCeQoYijkg4lQx8g0jZEwdPthnZzmBnlVxcG9RY-UFeApAhF2hDMtEc0OVgv1BD2jW1G9SA9JZ984bDUG6c6VWfexO2lHksEwrnqREPXrc1I4CHpd605iycPiwJ-cFjoopuEnAIl8WTQ9Z03AQiTpmd29BwV_xuWu44vUVSzFEcIkrgt-SJKffUQC9nkt7WeyDYBgKPC4dkHYN-7ClGh1vEaD_OUNbx4K5MSIhIhKC9eRmFTuzDunGwoDIHOfVVJAgUMdsoBtEg7VkG94itiJb62pImCyTbgYflkDasOTtPpQT_WSbfeHzsfDy_aK3Wvg885fUMJUECFYCsBLdbp6egali9y5cFsQBxRaF4oj33q7cTlcqaOwLyRt5BQDwhFe12Bwkn4eGAigpzAZsb7qwTVrdirOjyqK_kXgncxik2qCBP7Nm2a3_t4QmD1xOf3PUpJWUlUWITagihhtjBWh0rcj9KRQhpVW49wdMsHiH3zLMHlWDn1Z32GFbsP81zdSIMsGMHXCTVNLSBUyVwtetGHUx4Lrd5DlEINYO884x5lLFVeR9eGGyXhpnCoDuOCbCfuJ7N1dF97mFp9wHF78vZNTk4NeFJxYOx-E9s8mueXk4dBjjX8W6WGlzIHkxJ83qcVsOS4r_EAk7Ig2ryYPKG-u1M5_oOtOS9jkanDCJ4QOiF-VwgSiTlkhoe-CDkTdFwWyptEY1bfm6BZIxEc8wbjsNiZDPndduXhWfrm1ZybcM73MEmfhIJwRE897r66HrUA85Q3WN38_GwMsXF8TfAYiWaUeCrA-bg2cNU8-URXcByJFts-YLrRtEdnYBoKm0-IXveyI0NlD40KvAQIVmsFStXLucKoPizh0h6cw0dhzT4BAsVQ8YKGeNho6mBl-ngg2HnL8-sVd9hf7lHOz0cPkGN8wQZeoJ0nwttSRfTujJZ-4So2mDfLO1xreP0BHc8PbFmWwN4BVyhCIZ7jelo8hQWxM4QMGX0NhGkXn-4ydlkLYH39gTWata0qghOkskdHtQrkQwbhWzpXKiK610-iZfbqAdHgMraEGCr2okKfPqR1zw2cvzN5fdsk3cFBYZzqGHZr4cEkgfVWJXzhcLWHNchxLi2r1DKBV-TFfDd9nyuE5Z2hhfBMlogYGlXHJkFD_cBosMBDxLkT7-W06jgJzjYZRPNYXcxIKvUYpHWjpaTS8BC2-nwYBbU40mevnrfaP8YwQrReAUPJ3ffNmM65GmuhojHbTdoABp2yc6lmE281ZNC9sy6V1dZ39VG5OQOd4U78A2ssZAg-WkVxnSXk-NokomRLMRzdWoI0tshOq8c-KoF2HTv5yFMIjzk7bBC1nDwZ8l6wXvixNLXmadTdMao-VLNDkfYgDTSVf4FEk49StTgR2umg4nTuRCggrftYFePUwdPcIG9EOxk35htWO5oJgRkgtlHgOawdYJVvQpy1IzVnM6CXJFjn15-caLTsdqV82VIOi3qslWmt-bBHz5sypAhD-51RCN_ksFgNhU_hwxelzsQ0SKI5QoYBWwURgVJwN9Oq3d8omAagx06UCUSH1nOs3ZJTUrWAkTtpffsUl19ZMlJNhHiY39c9eC_swwsOntxPr8T8nmZp23iaRlSb2PcAepF2zOgHd4O-BlkVx5IWBOk4gN4k2wAqOHrhSISxYam5vyPBM2094KwHGobT09YFiaxlNW1ZPc07I-Pm3BdYoMn87o7QIRkx5c8SFoozhoA1528cEb-GyQ1eWAPFo7Wcv6IAx8ct9GsRVujg-chUmHr8OnmHJ_iSUFu4FoN57G6U-QZaRtXLrlE8_-c1T1j3-ADQiy4XjyxFnIHIgnnlWR5KAvB7nYmaeSxGQoSFOzarxAuWNnwIOvrEI80eSp-TSFGW_FahoECZMv4Ouywea4ZZKqgSJIPB6fhpOwZCTzdQ3l3Z40JzbkGggQSE4_Irr7T-RZuWLfTx0pOsQDH1fe0Yu7qULdgzzHxDKW63SNMNqRx4x6HObubzWPNtin_u1oxPOzZV8ZZZyy6Q-Ae89hr0EOe7DK6bRYETyxNIRWIJMB_eD23KdwqNsESbUjD7R_3jUJyKMm0RwxHuyNYgs4tTqMgDpo0jRsjf1nvfU7HcHkQnqBh3YNnSKCeaKVc550IS8LeTZhhycaRt57RGFXcWfuAD4TxZ_RL8citc7bsfEXgj1U-JSD5DtTILG-3yiaXHpYDkOs2F4x_zIECP__jZ_-Y4LFdQsYivt2nhDVfGg1MEs9qD8o4IjHeHQ84TxMjD9ImJx45SuwKoCAUivAUeeuxfJW0yafl3B2-Mc0vuCS0KKGfTYHr30ZHZzov4BSPC1gKnznWmANSaEeC2mxUwGF7xNrpLLFnzeVzbN5zHsP-TNLTdB5ItgzmILJ1thct7uEBD0LYWGF839JL-5hgZjabcr92bxe0LJ8gVf3nmi_bz9ZleOz-qNByjy2N9nOXRwn_x9UXGSVBbJdy4Rk5sw5uwh89PY91C7awQMjXBJTmwR_BDDyHO92jrhWyJdkZJ2trbvVOOLGY_L8uaN3WuvoDsAT5HEZXCcH1b1lfgtmbuWAHtjyKMOSYrj_4c6fCVQtYqDMQrkw3PAiUOyBxpWLn4b7CJVlb9R2b_q9cHXxshRDWm6USAh3Jev4sNtLjsf-4OjgMX-H-ydrw9xFzuRDDCYkJh8topxYcM-mdxIZwcNcAMmVY2wTCI07dRqfGmrkf-Y7bFAgdKAmB4BF5EBlsa-vIk5PgkEceoVnNPCcqs639uQdBlJGyab3TFcLcXRL-1Kl9nrNxiAeeA71CY1gbcXAV9BeN9nNg5YrgiOMrPMta8BrPMUP_Jm5gAwEUjOeJj9lMDpc47h8aQugneLDj1-rtbBSE7mMAG2KOIEmOV0v8FFbKV3lmPS0Vxuo2Jxo4hgau70A5cqdztwpP_LkzWosc8ZhGLrMb9AzBfPs6GTAb6t6O6YlEFIijOs7D_11nlMjroJiH4YKnIIFwFt9t3kV6m0Mv3uYuZLKZyxmQJPzhQCG9Z7R2EgR4x2uZ2vkvtRXY4mrh5zZtt5NipY7y8iSqp9buCS6kaCN9dQ_Gs9EyGV863KBNgMqwi_QQfw6EBQAM0ZDcWpWFQrzDBqlhGftNTsCDR67e7EIKzPFu2eNcFaL-HtYpn3iFw10Feyjt3ng85oWghwLL6SaEjQBh_TxpVxJOXK1fvzcYPrNiJzsmBFTog6ycmxuKE3qI0gHxyZ8QJtQta2wHSSA_EGw4m18kfYx9dYbY-yD74Mkny-fONHL244KHFatwHyKfBAqHeNzfAQnXly_MacnXKKJo7_VbT0CR21nx0_Z5PHOd9b0BEvJsiLbZOMAJgfta933sKTurDR72bUp7YX5puDPPE7-sjxJloCcp5vJ0BGt0ScXxNFQMkt12z9ZsJrOnyvHkh1v5B_782n7UACSkhGc2W_Nc_-zFYWOIy0yumVZdPG6cnZr2jrqaQVNw_42ci-xAH3X4PbkxIk-kqECso5Qc2Xlzoe5a1rAu2mZ9N1u6g33GWoj1fG1hIKOaEBxUqB608Ic2sKnqcQhCdKjsetIHTAj2J84i0571fHF0vt0yA2oESsA_VUIEO2HCWyVhJGgS5PPO5MCVCe_gMmy_dn6Xp-bg2PFncNbpm0EieNqq3WenAwpfIi3JTjxi6ISi5ogBT3Da7o5qd9raeoK9cVmkVccP8PzXQEG3p3uvNjf-dGiis0jkLxwLnjbqerOHp3PjMaNVWOil3eC3bbwsxbm28Anh1zlBL1THsYtqQshyXIPLcDKnc1zZ1m_A0Yk4YIGz8PAvTwb3oJp-PLbKg1CQe1DQojbBZKu9k9EuN2E977bms_LNyQlFI9rQN5wSYMUEv4OnOzG3II1jlcI0MJxE62Tn215iOzCH5049VPzIrsABwuvBUQG3_jgJrnvic7bJXYVb5Zscq2s_gNNyD7f73Zq1Nm4TqOe7EqD8Wqt28gcw2myXdXj6rJfZBsZNYuGTLU4SEjhpm_VnVA4y2dZwFoUIWW-If5GRnaf6sB7NaefV_ryjGGr9dOhVriQCksEaQwTCPAAhKcFzJAWS7CsMSDAO70KErX43_hBfi84nUyN7mjB7ssexPHcHnOG9rUMIH9T-BQ0nAqezo1pdhwg8a43nvM4fIG-QDp2v01-ygF2ph2Wh1t0pfWUD1nnhBuiBC45u94gFycOy9ACVb6gGVydZ4jLAWE_FX77RtOrEowHkeuxiArdNBVbKHTnUQgn2zV0OGngdA0fMVYcXHS6K7xXwqEuda5pvvS4ymEUGN0BxmHb5lZcB7sGaAOdeYwUdhGW3LUmEMLSprrqH1myfHnaikrf4slet8ArXkeuB1gdPBZwPapFPsJ-mOhqPDOLuNS41uuOh7a0HYvxk5z_wxx4pgbzZCOjkSJILFK_A4JNbz1DZdNl2hK6-V7uw41wQU7PM9Z-I6nx0OZBVumK_LZTZeqbv-XGf_nW215SZc_053XjUtEfuQT-BmdXiV9NvnfuDx3XPPg9I4RpU5qzZxMeSz8lAp5B2SMehbuvQy-tBc89w2rZ6jcORIYQy1IIQQVfZ6z3RTS_Cx9lqwIaHO3m2T-ahiUh18uBjdZSanSxXXkVgS23kdQeInsGez9FiOoftQmGg7Uwwb0HFTVC1cUEOEoY0kUmmRxcIM-dPkk0rcJectIODr6ob3OXxqItM27M9fLBiDnSwJbI6znk6N9Bz6jAbhMF9N7mRE1IcXVmBan3C4dCS7_SP8RRq_I-f_WMOrsaOwdPjlx4JacEJHkiS6owZxKWHZu0PdeLU_ThbDShpDt04bcSCYwwP1sgqhvtqNUXUGanf38LFX_fBAakZ3KeBMVmwB1U8Pfg6zhN1cP38lvzuCS-C2slScQ0LdL8FHWh-g0iP4jxat0Gux_PNfXhexoNpgZg_nKW2PrQ_iFH4HrfidtMDKSkTBfu89gA6FdZa8I0EN3-ebn1b9Ua-TY-Qe6Yw2JQE70Rn2nGbbGN1I9P5eGSh5mH8dQ4SxRB6Pz1R5CiglLKbL9ls6t5LJlTCmvp6OCM-Zme8Wuy3Z8qTLm5uNpSLdRTyJqy2QJHRw9Wmxf545r9Od2bvNcWHxcseDLd94j7tcCnObNM5GpQ9H5d9xEAiTn_bt3k49yM5MLTCKZX3LEQkM03H_pYMcsDq0UwFSLVZTk84SHDTDb542jRmWdvtENPrjeN6UPp0JmBwPm12r-Wxa6BWD-dBwACz6MRX2xDgvR5kh6w_Hm635wj4JPngRZOks2aCsIKeDnO7ZvFA6_SPDTD19nXCYx19wJeytS13DkEw94SG-3L5JgRsM4rZdgBPIkKHYeBQkRnAiOxEJZWnUwCJQnJ6sf0VtJ8W1jxJJ7-GwJPiHSx8OieTJ-wLlnFWt5RP_v8Q9Q4CX9cDURzhYcPl2ttETCJLPOO7ytPdzWb71R03CY8J-W241uXecnPf6Zz2FpMhHPL2c_1HVrQ60AnJ7c7qfT2J9ELQFdgqLI1n1gAueRbRMh6FTFnn898Cwp8eVymdROnwd-cJkeGre2FOT1JFRfsfXo_botfLOnTYc3mL-4xwx-x585svLM90d9RzBEp-Z8xct0MVotw7JWfap_ra8ELawM1gy4lHT3w7kwzi43wAJQLueaRUJdyVvOUAvWdtYtyXuvgSykl2qBdTWvSNBg0t6oAmOG9-uO-1E-PsfRTU6uKGLTn6xum2bl2dTpjP6Wyyd0C-OFN7CamEaL1tkV-_dfY5grcRSp5KO9dxHh6OnSrOl8OZk4Pn129vB-4PB0GvDwi-wSGS3OI0A8zV00DCyevLX1Oqwz0uZ33auyaaHTw2BdrhrCePcJ03zmFj4vR0vztbP2_CcHaE5bH7rdbCpifp7e4jytZF4mnpAEbQqe-jznUAytP5-3bEeNmzsIoe66p8IVeERTfIsA3qii9uyCGRzoi1q1kxEOBHjiRBpYBP9gfet9NsbqtwSo7rhESSwC9H5jjrYkRPuWa7XxyfCZPw6Oqx3qdxuIdtjwtSyB2w15dOmbBO99rdyTW6zwNNYOPTZZl-vPYw3ITjvWAOvuuIHgd2d0l1nEQ-0OpQ2svxSPPls9s6pHGvp-XgpHA4JS977tkbP2wPxp0L69QQpwS0mHUjntf8ak_QPoOwymuqV3biLFmjurlBzkGvCWoepeyrweoHvUm6pS2M5R-QcY5iDRO4WJ6Y0RWXL9n4QYHivC15Dfq0Pe6ku1M4bHTBp7h9N68ux7meFUSdgI4dqgVo4FPhlIQ3uq1ZqCkEA77hvjsKqsoSyIASu3Q7HpuIgx1U55XYKme3SfYIEASkpVW6e7qbmOAp5M8Ci0eDi9WMZIyaWcm_DnLy_HV3qpKvS46O3UeidGjPY05wfGKzNlUuFCLMFjl3kw4gorC3d5aX5bZQz4oh8Y7DgY6H5WD3JipZ3IHJBTZrq2C12c1mQLs00nr_K0ifoQ8F4lXNLRA9j7z53sqxJoyDyebIKTl73Ohzvj333W2vKEjx4HzSy6mEzv123_ZwK3WNd6srf5DaEEHgKJHiabXlhJdvVenOpnuHM6edxmsnlrO7PGDqzNbiIIcjOddGTYd4heU2hwnqsE6EcYsb_hecnXD5EoDI9Yc1GAE8AX-K22zn6-spVHKIPWfGHW5mvLDNyy51TzV4dC47XLTILqJ8o7g1-BIS8XWstG8uJeQsLTgV1n5dFVRzGCw4DBkizwNevi3y8UVrJ0vblcEZmjUWwDrPy1ofPApK6f7G4SkMmAm0sPoOHE-VdKLK6aTmheipn-Q7RKpDwpoKj7yYbzJnyYAwLKBEt9leC_MRTenYbZaCpJ0CIsItKcvJi08S3TPgq06PcBJ9NDt151qMd_iFbh3w-Ct53JOiTiKRBJ_dA4-ssYNAo1sRxI1nUR1Bi2-TNdyQsrHZ0XfEmbgBoh08KIerXoXlr_rc6-4fnLUGvqifyTfRFqKlOFE2JWvZL-j7WBhKc-lF8B5R-1Nmh0B08oIN7Phu8d0xZEzHLTnGwrDJHlQySTjUGRiK4Bku07Oz8LjJhvP7Gh1QyYndpCxcC7XX1ALT0ddv94BJ9hiEGho1wLVXllwkLROW4HtyTlgpwePUJMfpIHWC5xOqEwVu--6776ohY3q6VRLH1QVSA98Cn4E-OALT47QW6aDYoBUPCu1lM3S3yoGMAsqqY_ysrnoWHJ6UnB7DY-Rpgxgyc8LX_hq7r981UsBpkvy_QYZYJLN4hMcKviNgmyzgdretOc_BfrHHLp_CQ0wjE79v8ZUCxIhjNx8P8APW0eGl_2V8p0v7j5_9Y_4Xqdo7gQ=="; void SongTest::initTestCase() { Echonest::Config::instance()->setAPIKey( "JGJCRKWLXLBZIFAZB" ); } void SongTest::testSearch1() { Echonest::Song::SearchParams params; params.append( Echonest::Song::SearchParamData( Echonest::Song::Artist, QLatin1String("Modest Mouse") ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::Title, QLatin1String("Float On") ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::Results, 2 ) ); QNetworkReply* reply = Echonest::Song::search( params, Echonest::SongInformation( Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistLocation | Echonest::SongInformation::ArtistFamiliarity ) ); qDebug() << "Test search:" << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVector< Echonest::Song > songs = Echonest::Song::parseSearch( reply ); qDebug() << songs << songs.size(); QVERIFY( !songs.isEmpty() ); for(int i = 0; i < songs.size(); i++) { Echonest::Song song = songs.value(i); QLatin1String title = QLatin1String( "float on" ); QCOMPARE( song.title().toLower(), title ); QCOMPARE( song.artistName().toLower(), QLatin1String( "modest mouse" ) ); QVERIFY( !song.artistLocation().location.isEmpty() ); QCOMPARE( song.hotttnesss(), -1. ); qDebug() << song.hotttnesss() << song.artistHotttnesss() << song.artistFamiliarity() << song.artistLocation(); // now fetch some more info and make sure everything is still sane QNetworkReply* moreInfo = song.fetchInformation( Echonest::SongInformation( Echonest::SongInformation::Hotttnesss ) ); QEventLoop loop; loop.connect( moreInfo, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song.parseInformation( moreInfo ); QCOMPARE( song.title().toLower(), title ); QCOMPARE( song.artistName().toLower(), QLatin1String( "modest mouse" ) ); QVERIFY( !song.artistLocation().location.isEmpty() ); // make sure we got the new info QVERIFY( song.hotttnesss() != -1 ); } params.clear(); params.append( Echonest::Song::SearchParamData( Echonest::Song::Artist, QLatin1String("The Tallest Man On Earth") ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::Title, QLatin1String("The King of Spain") ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::Results, 3 ) ); Echonest::SongInformation info( Echonest::SongInformation( Echonest::SongInformation::AudioSummaryInformation | Echonest::SongInformation::Tracks | Echonest::SongInformation::Hotttnesss ) ); info.setIdSpaces( QStringList() << QLatin1String( "musicbrainz" ) << QLatin1String( "playme" ) ); reply = Echonest::Song::search( params, info); qDebug() << "QUERY:" << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); songs = Echonest::Song::parseSearch( reply ); qDebug() << songs << songs.size(); foreach( const Echonest::Song& song, songs ) { // qDebug() << "SONG TRACKS:" << song.tracks(); foreach( const Echonest::Track& track, song.tracks() ) { // qDebug() << track.catalog() << track.foreignId(); QVERIFY( !track.catalog().isEmpty() ); QVERIFY( !track.foreignId().isEmpty() ); } } QVERIFY( songs.size() > 0 ); QVERIFY( songs[ 0 ].audioSummary().danceability() > 0 ); QVERIFY( songs[ 0 ].audioSummary().energy() > 0 ); QVERIFY( songs[ 0 ].audioSummary().acousticness() > 0 ); QVERIFY( songs[ 0 ].audioSummary().speechiness() > 0 ); QVERIFY( songs[ 0 ].audioSummary().liveness() > 0 ); } void SongTest::testSearch2() { Echonest::Song::SearchParams params; params.append( Echonest::Song::SearchParamData( Echonest::Song::Description, QLatin1String("emo") ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::ArtistStartYearAfter, 1990 ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::ArtistStartYearBefore, 2000 ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::Results, 2 ) ); QNetworkReply* reply = Echonest::Song::search( params, Echonest::SongInformation( Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistLocation | Echonest::SongInformation::ArtistFamiliarity ) ); qDebug() << "Test search:" << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVector< Echonest::Song > songs = Echonest::Song::parseSearch( reply ); qDebug() << songs << songs.size(); QVERIFY( !songs.isEmpty() ); } void SongTest::testSearch3() { Echonest::Song::SearchParams params; params.append( Echonest::Song::SearchParamData( Echonest::Song::Description, QLatin1String("psychedelic") ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::Results, 3 ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::MinAcousticness, 0.1 ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::MaxAcousticness, 0.7 ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::MaxSpeechiness, 0.4 ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::MinLiveness, 0.4 ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::MinValence, 0.3 ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::MaxValence, 0.9 ) ); QNetworkReply* reply = Echonest::Song::search( params, Echonest::SongInformation( Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistLocation | Echonest::SongInformation::ArtistFamiliarity | Echonest::SongInformation::AudioSummaryInformation ) ); qDebug() << "Test search:" << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVector< Echonest::Song > songs = Echonest::Song::parseSearch( reply ); qDebug() << songs << songs.size(); QVERIFY( !songs.isEmpty() ); QVERIFY( songs[ 0 ].audioSummary().acousticness() >= 0.1 ); QVERIFY( songs[ 0 ].audioSummary().acousticness() <= 0.7 ); QVERIFY( songs[ 0 ].audioSummary().speechiness() <= 0.4 ); QVERIFY( songs[ 0 ].audioSummary().liveness() >= 0.4 ); QVERIFY( songs[ 0 ].audioSummary().valence() >= 0.3 ); QVERIFY( songs[ 0 ].audioSummary().valence() <= 0.9 ); QVERIFY( songs[ 1 ].audioSummary().acousticness() >= 0.1 ); QVERIFY( songs[ 1 ].audioSummary().acousticness() <= 0.7 ); QVERIFY( songs[ 1 ].audioSummary().speechiness() <= 0.4 ); QVERIFY( songs[ 1 ].audioSummary().liveness() >= 0.4 ); QVERIFY( songs[ 1 ].audioSummary().valence() >= 0.3 ); QVERIFY( songs[ 1 ].audioSummary().valence() <= 0.9 ); } void SongTest::testProfile() { Echonest::Song song; song.setId( "SOYMZLR127CF8DD122" ); QNetworkReply* reply = song.fetchInformation( Echonest::SongInformation( Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistLocation ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song.parseInformation( reply ); qDebug() << song << song.artistHotttnesss() << song.artistLocation() << song.artistId() << song.artistName(); } void SongTest::testIdentify() { Echonest::Song::IdentifyParams params; params.append( Echonest::Song::IdentifyParamData( Echonest::Song::Code, code ) ); // params.append( Echonest::Song::IdentifyParamData( Echonest::Song::IdentifyArtist, QLatin1String( "Pink Floyd" ) ) ); QNetworkReply* reply = Echonest::Song::identify( params ); qDebug() << "Identifying:" << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::SongList songs = Echonest::Song::parseIdentify( reply ); qDebug() << "Identified:" << songs; QVERIFY( songs.size() > 0 ); QCOMPARE( songs.first().artistName(), QLatin1String( "Bon Iver" ) ); QCOMPARE( songs.first().artistId(), QByteArray( "ARKDTAM1187FB54026" ) ); QCOMPARE( songs.first().title(), QLatin1String( "Flume" ) ); QCOMPARE( songs.first().id(), QByteArray( "SOJEVHC12A8C13C3E5" ) ); } void SongTest::testIdentifyWithData() { Echonest::Song::IdentifyParams params; params.append( Echonest::Song::IdentifyParamData( Echonest::Song::Code, code ) ); QNetworkReply* reply = Echonest::Song::identify( params, Echonest::SongInformation( Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistLocation | Echonest::SongInformation::ArtistFamiliarity ) ); // qDebug() << "Identifying:" << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::SongList songs = Echonest::Song::parseIdentify( reply ); qDebug() << "Identified:" << songs; QVERIFY( songs.size() > 0 ); QCOMPARE( songs.first().artistName(), QLatin1String( "Bon Iver" ) ); QCOMPARE( songs.first().artistId(), QByteArray( "ARKDTAM1187FB54026" ) ); QCOMPARE( songs.first().title(), QLatin1String( "Flume" ) ); QCOMPARE( songs.first().id(), QByteArray( "SOJEVHC12A8C13C3E5" ) ); } void SongTest::testSearchSongType() { Echonest::Song::SearchParams params; params.append( Echonest::Song::SearchParamData( Echonest::Song::SongType, QLatin1String("christmas") ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::SongType, QLatin1String("acoustic") ) ); params.append( Echonest::Song::SearchParamData( Echonest::Song::Results, 30 ) ); QNetworkReply* reply = Echonest::Song::search( params, Echonest::SongInformation( Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistLocation | Echonest::SongInformation::ArtistFamiliarity | Echonest::SongInformation::SongType ) ); qDebug() << "Test search:" << reply->url().toString(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVector< Echonest::Song > songs = Echonest::Song::parseSearch( reply ); qDebug() << songs << songs.size(); foreach( const Echonest::Song& s, songs) { QVERIFY( s.songTypes().size() > 0 ); QVERIFY( s.songTypes().contains( QLatin1String("christmas" ) ) ); QVERIFY( s.songTypes().contains( QLatin1String("acoustic" ) ) ); } } QTEST_MAIN(SongTest) #include "SongTest.moc" libechonest-2.3.1/src/CMakeLists.txt000644 001750 001750 00000003111 12465467260 020155 0ustar00stefanstefan000000 000000 include_directories( ${QJSON_INCLUDE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR} ) set( LIBECHONEST_SRC Track.cpp Song.cpp Artist.cpp Playlist.cpp Config.cpp Parsing.cpp AudioSummary.cpp Util.cpp ArtistTypes.cpp Generator.cpp Catalog.cpp CatalogUpdateEntry.cpp CatalogSong.cpp CatalogArtist.cpp CatalogItem.cpp TypeInformation.cpp Genre.cpp qjsonwrapper/Json.cpp ) set( LIBECHONEST_H echonest_export.h Track.h Song.h Artist.h Playlist.h Config.h AudioSummary.h ArtistTypes.h Util.h Catalog.h CatalogUpdateEntry.h CatalogSong.h CatalogArtist.h CatalogItem.h TypeInformation.h Genre.h CommonTypes.h ) QT_WRAP_CPP( ${LIBECHONEST_H} ) add_library( ${ECHONEST_LIB_TARGET_NAME} SHARED ${LIBECHONEST_SRC} ) target_link_libraries( ${ECHONEST_LIB_TARGET_NAME} ${QT_QTCORE_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QJSON_LIBRARIES} ) set_target_properties( ${ECHONEST_LIB_TARGET_NAME} PROPERTIES VERSION ${ECHONEST_LIB_VERSION} SOVERSION ${ECHONEST_LIB_VERSION_SONAME} ) qt5_use_modules( ${ECHONEST_LIB_TARGET_NAME} Core Network Xml ) install( TARGETS ${ECHONEST_LIB_TARGET_NAME} RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} ) install( FILES ${LIBECHONEST_H} DESTINATION include/echonest${ECHONEST_LIB_VERSION_SUFFIX} COMPONENT Devel ) libechonest-2.3.1/src/CatalogItem.h000644 001750 001750 00000006045 12465467260 017770 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CATALOG_ITEM_H #define ECHONEST_CATALOG_ITEM_H #include "CatalogUpdateEntry.h" #include "echonest_export.h" #include "Util.h" #include #include #include class CatalogItemData; namespace Echonest { /** * Since catalog items can be artists or songs, and we don't know sometimes until after we parse them, * but we need to gather a list of them. A poor man's traits class? A rich man's interface? Far from either. */ class ECHONEST_EXPORT CatalogItem { public: CatalogItem(); CatalogItem( const CatalogItem& other ); CatalogItem& operator=( const CatalogItem& other ); virtual ~CatalogItem(); /** * The type of this item. */ virtual Echonest::CatalogTypes::Type type() const = 0; /** * The foreign id of this item in the catalog. e.g. CAOFUDS12BB066268E:artist:ARUI8651187B9ACF52 * * See The Echo Nest API docs for more information. */ QByteArray foreignId() const; void setForeignId( const QByteArray& id ); /** * The request that generated this catalog item */ CatalogUpdateEntry request() const; void setRequest( const CatalogUpdateEntry& request ); /** * The date and time when this item was added to the catalog */ QDateTime dateAdded() const; void setDateAdded( const QDateTime& dt ); /** * The rating of this item. */ int rating() const; void setRating( int rating ); /** * The play count of this item. */ int playCount() const; void setPlayCount( int count ); protected: QSharedPointer dd; }; typedef QVector< CatalogItem > CatalogItems; } #endif libechonest-2.3.1/cmake/FindQJSON.cmake000644 001750 001750 00000002120 12465467260 020402 0ustar00stefanstefan000000 000000 # Find QJSON - JSON handling library for Qt # # This module defines # QJSON_FOUND - whether the qsjon library was found # QJSON_LIBRARIES - the qjson library # QJSON_INCLUDE_DIR - the include path of the qjson library # if (QJSON_INCLUDE_DIR AND QJSON_LIBRARIES) # Already in cache set (QJSON_FOUND TRUE) else (QJSON_INCLUDE_DIR AND QJSON_LIBRARIES) # if (NOT WIN32) # # use pkg-config to get the values of QJSON_INCLUDE_DIRS # # and QJSON_LIBRARY_DIRS to add as hints to the find commands. # include (FindPkgConfig) # pkg_check_modules (QJSON REQUIRED QJson>=0.5) # endif (NOT WIN32) find_library (QJSON_LIBRARIES NAMES qjson PATHS ${QJSON_LIBRARY_DIRS} ${LIB_INSTALL_DIR} ${KDE4_LIB_DIR} ) find_path (QJSON_INCLUDE_DIR NAMES parser.h PATH_SUFFIXES qjson PATHS ${QJSON_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR} ${KDE4_INCLUDE_DIR} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(QJSON DEFAULT_MSG QJSON_LIBRARIES QJSON_INCLUDE_DIR) endif (QJSON_INCLUDE_DIR AND QJSON_LIBRARIES) libechonest-2.3.1/cmake/000755 001750 001750 00000000000 12465467260 015712 5ustar00stefanstefan000000 000000 libechonest-2.3.1/src/CatalogUpdateEntry.cpp000644 001750 001750 00000012357 12465467260 021674 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "CatalogUpdateEntry.h" #include "CatalogUpdateEntry_p.h" Echonest::CatalogUpdateEntry::CatalogUpdateEntry() : d( new CatalogUpdateEntryData ) { } Echonest::CatalogUpdateEntry::CatalogUpdateEntry( CatalogTypes::Action action ) : d( new CatalogUpdateEntryData ) { d->action = action; } Echonest::CatalogUpdateEntry::CatalogUpdateEntry( const Echonest::CatalogUpdateEntry& other ) : d( other.d ) { } Echonest::CatalogUpdateEntry::~CatalogUpdateEntry() { } Echonest::CatalogUpdateEntry& Echonest::CatalogUpdateEntry::operator=(const Echonest::CatalogUpdateEntry& other) { d = other.d; return *this; } QByteArray Echonest::CatalogUpdateEntry::itemId() const { return d->item_id; } void Echonest::CatalogUpdateEntry::setItemId(const QByteArray& id) { d->item_id = id; } Echonest::CatalogTypes::Action Echonest::CatalogUpdateEntry::action() const { return d->action; } void Echonest::CatalogUpdateEntry::setAction(Echonest::CatalogTypes::Action action) { d->action = action; } QByteArray Echonest::CatalogUpdateEntry::artistId() const { return d->artist_id; } void Echonest::CatalogUpdateEntry::setArtistId(const QByteArray& id) { d->artist_id = id; } QByteArray Echonest::CatalogUpdateEntry::fingerprint() const { return d->fp_code; } void Echonest::CatalogUpdateEntry::setFingerprint(const QByteArray& id) { d->fp_code = id; } void Echonest::CatalogUpdateEntry::setFingerpring(const QByteArray& id) { setFingerprint(id); } QString Echonest::CatalogUpdateEntry::artistName() const { return d->artist_name; } void Echonest::CatalogUpdateEntry::setArtistName(const QString& name) { d->artist_name = name; } bool Echonest::CatalogUpdateEntry::banned() const { return d->banned; } void Echonest::CatalogUpdateEntry::setBanned(bool banned) { d->bannedSet = true; d->banned = banned; } int Echonest::CatalogUpdateEntry::discNumber() const { return d->disc_number; } void Echonest::CatalogUpdateEntry::setDiscNumber(int disc) { d->disc_number = disc; } bool Echonest::CatalogUpdateEntry::favorite() const { return d->favorite; } void Echonest::CatalogUpdateEntry::setFavorite(bool fav) { d->favoriteSet = true; d->favorite = fav; } QString Echonest::CatalogUpdateEntry::genre() const { return d->genre; } void Echonest::CatalogUpdateEntry::setGenre(const QString& genre) { d->genre = genre; } int Echonest::CatalogUpdateEntry::playCount() const { return d->play_count; } void Echonest::CatalogUpdateEntry::setPlayCount(int playCount) { d->play_count = playCount; } int Echonest::CatalogUpdateEntry::rating() const { return d->rating; } void Echonest::CatalogUpdateEntry::setRating(int rating) { d->rating = rating; } QString Echonest::CatalogUpdateEntry::release() const { return d->release; } void Echonest::CatalogUpdateEntry::setRelease(const QString& release) { d->release = release; } int Echonest::CatalogUpdateEntry::skipCount() const { return d->skip_count; } void Echonest::CatalogUpdateEntry::setSkipCount(int skipCount) { d->skip_count = skipCount; } QByteArray Echonest::CatalogUpdateEntry::songId() const { return d->song_id; } void Echonest::CatalogUpdateEntry::setSongId(const QByteArray& id) { d->song_id = id; } QString Echonest::CatalogUpdateEntry::songName() const { return d->song_name; } void Echonest::CatalogUpdateEntry::setSongName(const QString& name) { d->song_name = name; } int Echonest::CatalogUpdateEntry::trackNumber() const { return d->track_number; } void Echonest::CatalogUpdateEntry::setTrackNumber(int trackNum) { d->track_number = trackNum; } QString Echonest::CatalogUpdateEntry::url() const { return d->url; } void Echonest::CatalogUpdateEntry::setUrl(const QString& url) { d->url = url; } bool Echonest::CatalogUpdateEntry::bannedSet() const { return d->bannedSet; } bool Echonest::CatalogUpdateEntry::favoriteSet() const { return d->favoriteSet; } libechonest-2.3.1/src/Genre_p.h000644 001750 001750 00000004211 12465467260 017147 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2014 Leo Franchi * * Copyright (c) 2014 Stefan Derkits * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_GENRE_P_H #define ECHONEST_GENRE_P_H #include "Artist.h" #include #include namespace Echonest { class Genre; } class GenreData : public QSharedData { public: GenreData() {} GenreData(const GenreData& other) : QSharedData(other) { name = other.name; artists = other.artists; similar = other.similar; wikipedia_url = other.wikipedia_url; description = other.description; } // The following exist in all valid Genre objects QString name; //The following are populated on demand, and may not exist Echonest::Artists artists; QVector similar; QUrl wikipedia_url; QString description; }; #endif libechonest-2.3.1/src/qjsonwrapper/Json.h000644 001750 001750 00000003010 12465467260 021230 0ustar00stefanstefan000000 000000 /* Copyright 2014, Uwe L. Korn * * 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. */ #pragma once #ifndef QJSONWRAPPER_JSON_H #define QJSONWRAPPER_JSON_H #include namespace QJsonWrapper { QVariantMap qobject2qvariant( const QObject* object ); void qvariant2qobject( const QVariantMap& variant, QObject* object ); QVariant parseJson( const QByteArray& jsonData, bool* ok = 0 ); QByteArray toJson( const QVariant &variant, bool* ok = 0 ); } #endif // QJSONWRAPPER_JSON_H libechonest-2.3.1/README000644 001750 001750 00000003271 12465467260 015515 0ustar00stefanstefan000000 000000 libechonest =========== libechonest is a collection of C++/Qt classes designed to make a developer's life easy when trying to use the APIs provided by The Echo Nest. More information about the APIs can be found here: http://developer.echonest.com/docs/v4/ libechonest is developed by Leo Franchi (lfranchi@kde.org), and is available at KDE's projects page, https://projects.kde.org/projects/playground/libs/libechonest . Review request are accepted at http://git.reviewboard.kde.org under the compenent libechonest, and API comments, and more are appreciated. Dependencies ============ * Qt, only the QtCore, QtNetwork and QtTest modules (www.qtsoftware.com) * CMake, Kitware's open source build system (http://cmake.org) * QJson (http://qjson.sourceforge.net/). Installing ========== mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX=/path/to/desired/install .. make make install Using libechonest =============== The c++ API is meant to be as close to the Echo Nest API as possible. All Echo Nest API functions have been copied into their respective c++ equivalents, and string values converted to enums where reasonable. See the unit tests in tests/ for examples on how to use the classes. You need an API key from http://developer.echonest.com to use this library. Set your API key with Echonest::Config::instance()->setAPIKey(). You can also set your own custom QNetworkAccessManager-derived class with Config::setNetworkAccessManager(); Your link line needs to include the following: -lechonest -lQtCore -lQtNetwork Thank you for tuning in! leo ------- This README, and libechonest in general, is inspired by Max Howell's liblastfm, at http://www.github.com/mxcl/liblastfm. libechonest-2.3.1/src/Config.h000644 001750 001750 00000010737 12465467260 017007 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CONFIG_H #define ECHONEST_CONFIG_H #include "echonest_export.h" #include #include #include #include class QNetworkAccessManager; namespace Echonest{ /// Creates the base URL for all GET and POST requests QUrl baseUrl(); /// Creates the base URL for GET requests. QUrl baseGetQuery( const QByteArray& type, const QByteArray& method ); enum ErrorType { /** * Echo Nest API errors */ UnknownError = -1, NoError = 0, MissingAPIKey = 1, NotAllowed = 2, RateLimitExceeded = 3, MissingParameter = 4, InvalidParameter = 5, /// libechonest errors UnfinishedQuery = 6, /// An unfinished QNetworkReply* was passed to the parse function EmptyResult = 7, UnknownParseError = 8, /// QNetworkReply errors NetworkError = 9 }; class ECHONEST_EXPORT ParseError : public std::exception { public: explicit ParseError( ErrorType error ); ParseError( ErrorType error, const QString& text ); virtual ~ParseError() throw(); ErrorType errorType() const throw(); /** * If the ErrorType is NetworkError, this value contains the QNetworkReply * error code that was returned. */ void setNetworkError( QNetworkReply::NetworkError error ) throw(); QNetworkReply::NetworkError networkError() const throw(); virtual const char* what() const throw (); private: ErrorType type; QString extraText; QNetworkReply::NetworkError nError; }; class ConfigPrivate; /** * This singleton contains miscellaneous settings used to access The Echo Nest */ class ECHONEST_EXPORT Config { public: static Config* instance(); /** * Set the API key to be used for all API requests. This MUST be set in order * for any web service calls to work! */ void setAPIKey( const QByteArray& apiKey ); QByteArray apiKey() const; /** * Set the QNetworkAccessManager to use to make web service requests to * The Echo Nest. * * This will register the given QNAM for the current thread. If you call this from * the main thread and only make calls to libechonest from the main thread, you don't * have to do any more work. However, if you are using multiple QNAMs in different threads, * you must call this for each QNAM in each thread so that libechonest can use the proper * thread-local QNAM. This function is thread-safe. * * Note that in all threads, if you do not set a QNAM, a default one is created and returned. * In addition, if you set your own QNAM, you are responsible for deleting it. * * This will take over control of the object. */ void setNetworkAccessManager( QNetworkAccessManager* nam ); QNetworkAccessManager* nam() const; private: Config(); ~Config(); static Config* s_instance; ConfigPrivate* d; }; } #endif libechonest-2.3.1/src/Playlist.h000644 001750 001750 00000046353 12465467260 017406 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_PLAYLIST_H #define ECHONEST_PLAYLIST_H #include "echonest_export.h" #include "Song.h" #include #include #include "Artist.h" #include #include "Catalog.h" class QNetworkReply; class DynamicPlaylistData; namespace Echonest{ typedef struct { QByteArray session_id; Artists banned_artists; Artists favorited_artists; SongList banned_songs; SongList favorited_songs; QVariantMap options; QVariantMap ratingsMap; // TODO favorites_map // TODO constraints } SessionInfo; /** * This encapsulates an Echo Nest dynamic playlist. It contains a playlist ID and * the current song, and can fetch the next song. * * See http://developer.echonest.com/docs/v4/playlist.html * for more information */ class ECHONEST_EXPORT DynamicPlaylist { public: /** * The types of playlist that can be generated. Artist plays songs for the given artist, * ArtistRadio takes into account similar artists, and ArtistDescription plays songs matching * the given description. */ enum ArtistTypeEnum { ArtistType, ArtistRadioType, ArtistDescriptionType, CatalogType, CatalogRadioType, SongRadioType, GenreRadioType }; /** * Different ways to sort a generated playlist */ enum SortingType { SortTempoAscending, SortTempoDescending, SortDurationAscending, SortDurationDescending, SortArtistFamiliarityAscending, SortArtistFamiliarityDescending, SortArtistHotttnessAscending, SortArtistHotttnessDescending, SortSongHotttnesssAscending, SortSongHotttnesssDescending, SortLatitudeAscending, SortLatitudeDescending, SortLongitudeAscending, SortLongitudeDescending, SortModeAscending, SortModeDescending, SortKeyAscending, SortKeyDescending, SortLoudnessAscending, SortLoudnessDescending, SortEnergyAscending, SortEnergyDescending, SortDanceabilityAscending, SortDanceabilityDescending, SortAcousticnessAscending, SortAcousticnessDescending, SortSpeechinessAscending, SortSpeechinessDescending, SortLivenessAscending, SortLivenessDescending, SortValenceAscending, SortValenceDescending }; /** * Different ways of picking artists in Artist radios. */ enum ArtistPick { PickSongHotttnesssAscending, PickTempoAscending, PickDurationAscending, PickLoudnessAscending, PickModeAscending, PickKeyAscending, PickSongHotttnesssDescending, PickTempoDescending, PickDurationDescending, PickLoudnessDescending, PickModeDescending, PickKeyDescending }; /** * The various parameters that can be passed to the playlist building * functions. */ enum PlaylistParam { Type, /// The type of playlist to generate. Value is the DynamicPlaylist::ArtistTypeEnum enum Format, /// Either xml (default) or xspf. If the result is xspf, the raw xspf playlist is returned, else the xml is parsed and exposed programmatically. If using XSPF, you must specify a catalog, the tracks bucket, and limit = true Pick, /// How the artists are picked for each artist in ArtistType playlists. Value is Playlist::ArtistPick enum value. Variety, /// 0 < variety < 1 The maximum variety of artists to be represented in the playlist. A higher number will allow for more variety in the artists. ArtistId, /// ID(s) of seed artist(s) for the playlist Artist, /// Artist names of seeds for playlist ArtistSeedCatalog, /// ID of seed artist catalog for the playlist SourceCatalog, /// ID of catalog (artist or song) for catalog type playlists SongId, /// IDs of seed songs for the playlist Description, /// Textual description for sort of songs that can be included in the playlist Results, /// 0-100, how many sonsg to include in the playlist, default 15 MaxTempo, /// 0.0 < tempo < 500.0 (BPM) The maximum tempo for any included songs MinTempo, /// 0.0 < tempo < 500.0 (BPM) the minimum tempo for any included songs MaxDuration, /// 0.0 < duration < 3600.0 (seconds) the maximum duration of any song on the playlist MinDuration, /// 0.0 < duration < 3600.0 (seconds) the minimum duration of any song on the playlist MaxLoudness, /// -100.0 < loudness < 100.0 (dB) the maximum loudness of any song on the playlist MinLoudness, /// -100.0 < loudness < 100.0 (dB) the minimum loudness of any song on the playlist MinDanceability, /// 0 < danceability < 1 a measure of the minimum danceability of the song MaxDanceability, /// 0 < danceability < 1 a measure of the maximum danceability of the song MinEnergy, /// 0 < danceability < 1 a measure of the maximum energy of the song MaxEnergy, /// 0 < danceability < 1 a measure of the maximum energy of the song ArtistMaxFamiliarity, /// 0.0 < familiarity < 1.0 the maximum artist familiarity for songs in the playlist ArtistMinFamiliarity, /// 0.0 < familiarity < 1.0 the minimum artist familiarity for songs in the playlist ArtistMaxHotttnesss, /// 0.0 < hotttnesss < 1.0 the maximum hotttnesss for artists in the playlist ArtistMinHotttnesss, /// 0.0 < hotttnesss < 1.0 the maximum hotttnesss for artists in the playlist SongMaxHotttnesss, /// 0.0 < hotttnesss < 1.0 the maximum hotttnesss for songs in the playlist SongMinHotttnesss, /// 0.0 < hotttnesss < 1.0 the maximum hotttnesss for songs in the playlist ArtistMinLongitude, /// -180.0 < longitude < 180.0 the minimum longitude for the location of artists in the playlist ArtistMaxLongitude, /// -180.0 < longitude < 180.0 the maximum longitude for the location of artists in the playlist ArtistMinLatitude, /// -90.0 < latitude < 90.0 the minimum latitude for the location of artists in the playlist ArtistMaxLatitude, /// -90.0 < latitude < 90.0 the maximum latitude for the location of artists in the playlist Mode, /// (minor, major) 0, 1 the mode of songs in the playlist Key, /// (c, c-sharp, d, e-flat, e, f, f-sharp, g, a-flat, a, b-flat, b) 0 - 11 the key of songs in the playlist SongInformation, /// what sort of song information should be returned. Should be an Echonest::SongInformation object Sort, /// SortingType enum, the type of sorting to use, Limit, /// true, false if true songs will be limited to those that appear in the catalog specified by the id: bucket Audio, /// true, false, if true songs will be limited to those that have associated audio DMCA, /// true, false Only valid for dynamic playlists. Sets if playlist will follow DMCA rules (see web api doc for details) ChainXSPF, /// true, false If true, returns an xspf for this dynamic playlist with 2 items. The second item will be a link to the API call for the next track in the chain. Please note that this sidesteps libechonest's handling of the tracks. Mood, /// A mood to limit this playlist to, for example "happy" or "sad". Multiples of this param are okay. See the method Artist::listTerms for details on what moods are currently available Style, /// A style to limit this playlist to, for example "happy" or "sad". Multiples of this param are okay. See the method Artist::listTerms for details on what styles are currently available Adventurousness, /// A value of 0 means no adventurousness, only known and preferred music will be played. A value of 1 means high adventurousness, mostly unknown music will be played. This parameter only applies to catalog and catalog-radio type playlists. MoreLikeThis, /// When steering: Supply a song id to steer this session towards. Can be boosted from 0-5 like so: SO12341234^2. Default is 1 LessLikeThis, /// When steering: Supply a song id to steer this session away from. Can be boosted from 0-5 like so: SO12341234^2. Default is 1 TargetTempo, /// When steering: 0.0 < tempo < 500. (BPM). Target a desired tempo for the songs in this dynamic playlist session TargetLoudness, /// When steering: -100. < loudness < 100. (BPM)dB. Target a desired loudness for the songs in this dynamic playlist session TargetDanceability, /// When steering: 0.0 < danceability < 1. Target a desired danceability for the songs in this dynamic playlist session TargetEnergy, /// When steering: 0.0 < energy < 1.Target a desired energy for the songs in this dynamic playlist session TargetSongHotttnesss, /// When steering: 0.0 < song_hotttnesss < 1.Target a desired song_hotttnesss for the songs in this dynamic playlist session TargetArtistHotttnesss, /// When steering: 0.0 < artist_hottttnesss < 1.Target a desired artist_hottttnesss for the songs in this dynamic playlist session TargetArtistFamiliarity, /// When steering: 0.0 < artist_familiarity < 1.Target a desired energy for the artist_familiarity in this dynamic playlist session SongType, /// Type of Song (atm: Live, Studio, Christmas, Acoustic, Electric) Genre, /// Genre Parameter needed for GenreRadio Playlist ArtistStartYearBefore, ArtistStartYearAfter, ArtistEndYearBefore, ArtistEndYearAfter, MaxAcousticness, /// 0 < acousticness < 1 a measure of the maximum acousticness of the song MinAcousticness, /// 0 < acousticness < 1 a measure of the minimum acousticness of the song MaxSpeechiness, /// 0 < speechiness < 1 a measure of the maximum speechiness of the song MinSpeechiness, /// 0 < speechiness < 1 a measure of the minimum speechiness of the song MaxLiveness, /// 0 < liveness < 1 a measure of the maximum liveness of the song MinLiveness, /// 0 < liveness < 1 a measure of the minimum liveness of the song MaxValence, /// 0 < valence < 1 a measure of the maximum valence of the song MinValence, /// 0 < valence < 1 a measure of the minimum valence of the song Distribution, /// focused or wandering GenrePreset /// core-best, core-shuffled, in_rotation-best, in_rotation-shuffled, emerging-best or emerging-shuffled }; /** * The types of feedback that can be used to steer a dynamic playlist */ enum DynamicFeedbackParam { BanArtist, /// Ban this artist from this dynamic session. [artist_id, track_id, song_id, "last"] FavoriteArtist, /// Mark this artist as 'liked' for this session. [artist_id, track_id, song_id, "last"] BanSong, /// Ban this song from this dynamic session. [track_id, song_id, "last"] SkipSong, /// Mark this song as skipped by the user. Will not appear for the rest of the session. [track_id, song_id, "last"] FavoriteSong, /// Mark this song as a favorite. [track_id, song_id, "last"] PlaySong, /// Mark this song as played. Unneeded unless you want to pre-seed a station. [track_id, song_id, "last"] UnplaySong, /// Remove a song from a dynamic session's history. Will not blacklist the song. [track_id, song_id, "last"] RateSong, /// Rate the desired song. [track_id, song_id, "last"]^[0-10]. E.g: "last^3" or "TRTLKZV12E5AC92E11^5" }; /** * The parameters that can be passed to GenrePreset */ enum GenrePresetParam { CoreBest, CoreShuffled, InRotationBest, InRotationShuffled, EmergingBest, EmerginShuffled, }; typedef QPair< PlaylistParam, QVariant > PlaylistParamData; typedef QVector< PlaylistParamData > PlaylistParams; typedef QPair< DynamicFeedbackParam, QByteArray > DynamicFeedbackParamData; typedef QVector< DynamicFeedbackParamData > DynamicFeedback; typedef QPair< SongList, SongList > FetchPair; DynamicPlaylist(); virtual ~DynamicPlaylist(); DynamicPlaylist( const DynamicPlaylist& other ); DynamicPlaylist& operator=( const DynamicPlaylist& playlist ); /** * Start a dynamic playlist with the given parameters. * Once the QNetworkReply has finished, pass it to parseStart() * * To fetch tracks, call fetchNextSong(). The info() method can be used * to extract session information */ QNetworkReply* create( const PlaylistParams& params ) const; void parseCreate( QNetworkReply* ) throw( ParseError ); /** * Retart a dynamic playlist with the given parameters. * Once the QNetworkReply has finished, pass it to parseStart() * * This is the same as start(), except it maintains the history from an * already-existing playing station. */ QNetworkReply* restart( const PlaylistParams& params ) const; /** * The session id of this dynamic playlist. If the playlist has ended, or has not been started, * the result is empty. * */ QByteArray sessionId() const; void setSessionId( const QByteArray& id ); /** * The current song of this dynamic playlist. Once this song has been played, * or whenever is desired, call fetchNextSong() to get the next song. */ Song currentSong() const; void setCurrentSong( const Song& song ); /** * Queries The Echo Nest for the next playable song(s) in this * dynamic playlist. * * \param @results How many results to return, 1-5. This lets you * reduce the calls if you need more than one. Default is 1. * \param @lookahead The potential next songs (after the results) * if there is no steering applied. 0-5, default of 0. */ QNetworkReply* next( int results = 1, int lookahead = 0 ) const; /** * Return the result of a dynamic/next API call. This will return two lists: * the "next" list and the "lookahead" list. Consult the \ref next() docs * and The Echo Nest documentation for more information. */ FetchPair parseNext( QNetworkReply* reply ) throw( ParseError ); /** * Returns feedback to The Echo Nest for the currently playing dynamic * playlist. * * See The Echo Nest api documentation for complete details. * * \param feedback A list of feedback items to apply. */ QNetworkReply* feedback(const DynamicFeedback& feedback) const; /** * Parses the result of the feedback call. * * Will throw an exception if the return code is not successful (as other parse methods do). */ void parseFeedback(QNetworkReply* reply) const throw( ParseError ); /** * Modifies the upcoming tracks in this dynamic playlist session by steering it. * * Steering is additive, and can be reset for a dynamic playlist by calling reset. * * \param steerParams The desired steering params. Only use the enum values that correspond to valid steering commands. */ QNetworkReply* steer(const PlaylistParams& steerParams) const; /** * Parses the result of the steer call. * * Will throw an exception if the return code is not successful (as other parse methods do). */ void parseSteer(QNetworkReply* reply) const throw( ParseError ); /** * Returns a description of this dynamic playlist session */ QNetworkReply* fetchInfo() const; SessionInfo parseInfo( QNetworkReply* reply ) throw( ParseError ); /** * Deletes a currently active playlist session. A non-commercial API can have, at most 1,000 active playlist sessions. */ QNetworkReply* deleteSession() const; void parseDeleteSession(QNetworkReply* reply); /** * Generate a static playlist, according to the desired criteria. Use parseXSPFPlaylist if * you pass format=xspf to \c staticPlaylist(). */ static QNetworkReply* staticPlaylist( const PlaylistParams& params ); static SongList parseStaticPlaylist( QNetworkReply* reply ) throw( ParseError ); /** * Parse an xspf playlist. Returns the full xspf content with no modifications. */ static QByteArray parseXSPFPlaylist( QNetworkReply* reply ) throw( ParseError ); private: static QByteArray playlistParamToString( PlaylistParam param ); static QNetworkReply* generateInternal( const PlaylistParams& params, const QByteArray& type ); static QByteArray playlistSortToString(SortingType sorting); static QByteArray playlistArtistPickToString(ArtistPick pick); static QByteArray playlistGenrePresetToString(GenrePresetParam param); static QByteArray dynamicFeedbackToString(DynamicFeedbackParam param); QSharedDataPointer d; }; ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::DynamicPlaylist& playlist); } // namespace Q_DECLARE_METATYPE( Echonest::DynamicPlaylist ) #endif libechonest-2.3.1/tests/CMakeLists.txt000644 001750 001750 00000002114 12465467260 020532 0ustar00stefanstefan000000 000000 ########### next target ############### # QTEST_MAIN is using QApplication when QT_GUI_LIB is defined remove_definitions(-DQT_GUI_LIB) add_definitions( -DDATA_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/test_data\" ) remove_definitions( -Werror ) set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../src) macro(add_echonest_test _source) get_filename_component(_name ${_source} NAME_WE) # qt4_automoc(${_name} ${_source}) set(CMAKE_AUTOMOC ON) add_executable(${_name} ${_source}) target_link_libraries(${_name} ${ECHONEST_LIB_TARGET_NAME} ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY} ${QTCORE_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QJSON_LIBRARIES} ) add_test(${_name}-test ${EXECUTABLE_OUTPUT_PATH}/${_name}) qt5_use_modules(${_name} Network Test) endmacro(add_echonest_test) add_echonest_test( ArtistTest.cpp ) add_echonest_test( SongTest.cpp ) add_echonest_test( TrackTest.cpp ) add_echonest_test( PlaylistTest.cpp ) add_echonest_test( CatalogTest.cpp ) add_echonest_test( GenreTest.cpp ) libechonest-2.3.1/src/CatalogItem.cpp000644 001750 001750 00000005137 12465467260 020324 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "CatalogItem.h" #include "CatalogItem_p.h" // just an interface. Echonest::CatalogItem::CatalogItem() : dd( new CatalogItemData ) { } Echonest::CatalogItem::CatalogItem(const Echonest::CatalogItem& other) : dd( other.dd ) { } Echonest::CatalogItem::~CatalogItem() { } Echonest::CatalogItem& Echonest::CatalogItem::operator=(const Echonest::CatalogItem& other) { dd = other.dd; return *this; } QDateTime Echonest::CatalogItem::dateAdded() const { return dd->date_added; } void Echonest::CatalogItem::setDateAdded(const QDateTime& dt) { dd->date_added = dt; } QByteArray Echonest::CatalogItem::foreignId() const { return dd->foreign_id; } void Echonest::CatalogItem::setForeignId(const QByteArray& id) { dd->foreign_id = id; } Echonest::CatalogUpdateEntry Echonest::CatalogItem::request() const { return dd->request; } void Echonest::CatalogItem::setRequest(const Echonest::CatalogUpdateEntry& request) { dd->request = request; } int Echonest::CatalogItem::rating() const { return dd->rating; } void Echonest::CatalogItem::setRating(int rating) { dd->rating = rating; } int Echonest::CatalogItem::playCount() const { return dd->play_count; } void Echonest::CatalogItem::setPlayCount(int count) { dd->play_count = count; } libechonest-2.3.1/src/Song.h000644 001750 001750 00000017645 12465467260 016515 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_SONG_H #define ECHONEST_SONG_H #include "echonest_export.h" #include "Track.h" #include "TypeInformation.h" #include #include #include #include #include #include "Config.h" class QNetworkReply; class SongData; namespace Echonest{ class DynamicPlaylist; // forward declare for friend declaration class Catalog; class AudioSummary; /** * This encapsulates an Echo Nest song---use it if you wish to get information about a song, * search for a song, etc. * * This class is implicitly shared. */ class ECHONEST_EXPORT Song { public: enum SearchParam { Title, Artist, Combined, Description, ArtistId, Start, Results, MaxTempo, MinTempo, MaxDanceability, MinDanceability, MaxComplexity, MinComplexity, MaxDuration, MinDuration, MaxLoudness, MinLoudness, MaxFamiliarity, MinFamiliarity, MaxHotttnesss, MinHotttnesss, MaxLongitude, MinLongitude, MaxEnergy, MinEnergy, Mode, Key, Sort, SongType, ArtistStartYearBefore, ArtistStartYearAfter, ArtistEndYearBefore, ArtistEndYearAfter, MaxAcousticness, MinAcousticness, MaxSpeechiness, MinSpeechiness, MaxLiveness, MinLiveness, MaxValence, MinValence }; typedef QPair< Echonest::Song::SearchParam, QVariant > SearchParamData; typedef QVector< SearchParamData > SearchParams; enum IdentifyParam { Code, IdentifyArtist, IdentifyTitle, IdentifyRelease, IdentifyDuration, IdentifyGenre }; typedef QPair< Echonest::Song::IdentifyParam, QVariant > IdentifyParamData; typedef QVector< IdentifyParamData > IdentifyParams; Song(); Song( const QByteArray& id, const QString& title, const QByteArray& artistId, const QString& artistName ); Song( const QByteArray& id ); Song( const Song& other ); Song& operator=(const Song& song); virtual ~Song(); /** * The following pieces of data are present in all Song objects, and do not require * on-demand fetching. */ QByteArray id() const; void setId( const QByteArray& id ); QString title() const; void setTitle( const QString& title ); QString artistName() const; void setArtistName( const QString& artistName ); QByteArray artistId() const; void setArtistId( const QByteArray& artistId ); QString release() const; void setRelease( const QString& release ); /** * The following require fetching from The Echo Nest, so call * fetchInformation() with the type of data you want first. * * If you ask for this information before calling parseInformation() * with the respective data, the result is undefined. */ /** * The full audio summary and analysis of this song. * * NOTE: This will return a copy of the AudioSummary object, which * is implicitly shared. If you make modifications to the returned * summary, for example by calling parseFullAnalysis(), it will detach * and you will have to call setAudioSummary() to save the changes back * to this Song object. */ AudioSummary audioSummary() const; void setAudioSummary( const AudioSummary& summary ); /** * The associated Track objects with acoustic track information */ QVector< Track > tracks() const; void setTracks( const QVector< Track >& tracks ); /** * The "hotttnesss" metric of this song. */ qreal hotttnesss() const; void setHotttnesss( qreal hotttnesss ); /** * The "hotttnesss" metric of this song's artist. */ qreal artistHotttnesss() const; void setArtistHotttnesss( qreal artistHotttnesss ); /** * The familiarity metric of this song's artist. */ qreal artistFamiliarity() const; void setArtistFamiliarity( qreal artistFamiliarity ); /** * The location of this artist. */ ArtistLocation artistLocation() const; void setArtistLocation( const ArtistLocation& artistLocation ); /** * The type of the song (atm: christmas, studio, live) */ QList< QString > songTypes() const; void setSongTypes( const QList< QString >& songTypes ); void addSongType( const QString& songType ); /** * This fetches the data from The Echo Nest for the requested data, so it * returns a QNetworkReply*. When the finished() signal is emitted * from the QNetworkReply object call parseInformation() to save the * data back to this Song object. * */ QNetworkReply* fetchInformation( SongInformation information = SongInformation() ) const; /** * Search for a song from The Echo Nest with the given search parameters. See * http://developer.echonest.com/docs/v4/song.html#search for a description of the * parameters and data types. * * The result will contain the requested information from the SongInformation flags, and * can be extracted in the parseSearch() function. * */ static QNetworkReply* search( const SearchParams& params, SongInformation information = SongInformation() ); /** * Identify a song from a given Echo Nest fingerprint hash code. * NOTE: SongInformation is currently not parsed yet. * */ static QNetworkReply* identify( const IdentifyParams& params, const SongInformation& information = SongInformation() ); /** * Identify a song from the Echoprint hash code, this time using the output of the 'echoprint-codegen' command-line * tool */ // static QNetworkReply* identify( const QByteArray& jsonData ); /** * Parses the reply of the identify call and returns a list of songs found. * */ static QVector< Song > parseIdentify( QNetworkReply* ) throw( ParseError ); /** * Parse the result of the fetchInformation() call. * For each requested SongInformationFlag in the original request, the respective * data will be saved to this Song object. */ void parseInformation( QNetworkReply* reply ) throw( ParseError ); /** * Parse the result of the search() call. */ static QVector parseSearch( QNetworkReply* reply ) throw( ParseError ); QString toString() const; friend class DynamicPlaylist; friend class Catalog; // for access to searchParamToString private: static QByteArray searchParamToString( SearchParam param ); static QByteArray identifyParamToString( IdentifyParam param ); static void addQueryInformation( QUrl& url, SongInformation information ); QSharedDataPointer d; }; typedef QVector< Song > SongList; ECHONEST_EXPORT QDebug operator<<(QDebug d, const Song &song); } // namespace Q_DECLARE_METATYPE( Echonest::Song ) #endif libechonest-2.3.1/cmake_uninstall.cmake.in000644 001750 001750 00000001654 12465467260 021420 0ustar00stefanstefan000000 000000 IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") IF(EXISTS "$ENV{DESTDIR}${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF(NOT "${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") ENDIF(NOT "${rm_retval}" STREQUAL 0) ELSE(EXISTS "$ENV{DESTDIR}${file}") MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") ENDIF(EXISTS "$ENV{DESTDIR}${file}") ENDFOREACH(file) libechonest-2.3.1/tests/CatalogTest.h000644 001750 001750 00000003461 12465467260 020363 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_CATALOG_TEST_H #define ECHONEST_CATALOG_TEST_H #include #include class CatalogTest : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testCreateUpdateDeleteSong(); void testCreateUpdateDeleteArtist(); void testList(); private: void testProfile(); void testRead(); void testStatus(); Echonest::Catalog m_songC; Echonest::Catalog m_artistC; }; #endif libechonest-2.3.1/src/Song_p.h000644 001750 001750 00000005167 12465467260 017030 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_SONG_P_H #define ECHONEST_SONG_P_H #include "Track.h" #include "AudioSummary.h" #include #include #include class SongData : public QSharedData { public: SongData() : hotttnesss( -1 ), artistHotttnesss( -1 ), artistFamiliarity( -1 ) { artistLocation.latitude = -1; artistLocation.longitude = -1; } SongData(const SongData& other) : QSharedData( other ) { id = other.id; title = other.title; artistName = other.artistName; artistId = other.artistId; audioSummary = other.audioSummary; tracks = other.tracks; hotttnesss = other.hotttnesss; artistHotttnesss = other.artistHotttnesss; artistFamiliarity = other.artistFamiliarity; artistLocation = other.artistLocation; songTypes = other.songTypes; } ~SongData() {} QByteArray id; QString title; QString artistName; QByteArray artistId; QString release; // The rest are optional that require manual fetching to populate Echonest::AudioSummary audioSummary; QVector tracks; qreal hotttnesss; qreal artistHotttnesss; qreal artistFamiliarity; Echonest::ArtistLocation artistLocation; QList songTypes; }; #endif libechonest-2.3.1/tests/PlaylistTest.h000644 001750 001750 00000003612 12465467260 020610 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_PLAYLIST_TEST_H #define ECHONEST_PLAYLIST_TEST_H #include class PlaylistTest : public QObject { Q_OBJECT private slots: void initTestCase(); void testStatic1(); void testStatic2(); void testStaticArtistYears(); void testStaticWithSongType(); void testStaticXSPF(); void testDynamic1(); void testDynamic2(); void testNewDynamicAPI(); void testDynamicChainXSPF(); void testGenreRadio(); void testAudioSummaryAttributes(); void testGenrePresets(); void testDistribution(); }; #endif libechonest-2.3.1/src/Artist.cpp000644 001750 001750 00000047232 12465467260 017403 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Artist.h" #include "Artist_p.h" #include "ArtistTypes.h" #include "Parsing_p.h" #include #include Echonest::Artist::Artist() : d( new ArtistData ) { init(); } Echonest::Artist::Artist( const QByteArray& id, const QString& name ) : d( new ArtistData ) { init(); d->id = id; d->name = name; } Echonest::Artist::Artist( const QString& name ) : d( new ArtistData ) { init(); setName( name ); } Echonest::Artist::Artist( const QByteArray& id ) : d( new ArtistData ) { init(); setId( id ); } Echonest::Artist::Artist(const Echonest::Artist& other) : d( other.d ) { init(); } Echonest::Artist& Echonest::Artist::operator=(const Echonest::Artist& artist) { d = artist.d; return *this; } Echonest::Artist::~Artist() { } void Echonest::Artist::init() { qRegisterMetaType("Echonest::Artist"); } QByteArray Echonest::Artist::id() const { return d->id; } QString Echonest::Artist::name() const { return d->name; } void Echonest::Artist::setId(const QByteArray& id) { d->id = id; } void Echonest::Artist::setName(const QString& name) { d->name = name; } Echonest::AudioList Echonest::Artist::audio() const { return d->audio; } void Echonest::Artist::setAudio(const Echonest::AudioList& audio) { d->audio = audio; } Echonest::BiographyList Echonest::Artist::biographies() const { return d->biographies; } void Echonest::Artist::setBiographies(const Echonest::BiographyList& bios ) { d->biographies = bios; } Echonest::BlogList Echonest::Artist::blogs() const { return d->blogs; } void Echonest::Artist::setBlogs(const Echonest::BlogList& blogs ) { d->blogs = blogs; } qreal Echonest::Artist::familiarity() const { return d->familiarity; } void Echonest::Artist::setFamiliarity(qreal familiar) { d->familiarity = familiar; } qreal Echonest::Artist::hotttnesss() const { return d->hotttnesss; } void Echonest::Artist::setHotttnesss(qreal hotttnesss) { d->hotttnesss = hotttnesss; } Echonest::ArtistImageList Echonest::Artist::images() const { return d->images; } void Echonest::Artist::setImages(const Echonest::ArtistImageList& imgs) { d->images = imgs; } Echonest::NewsList Echonest::Artist::news() const { return d->news; } void Echonest::Artist::setNews(const Echonest::NewsList& news) { d->news = news; } Echonest::ReviewList Echonest::Artist::reviews() const { return d->reviews; } void Echonest::Artist::setReviews(const Echonest::ReviewList& reviews) { d->reviews = reviews; } Echonest::SongList Echonest::Artist::songs() const { return d->songs; } void Echonest::Artist::setSongs(const Echonest::SongList& songs) { d->songs = songs; } Echonest::TermList Echonest::Artist::terms() const { return d->terms; } void Echonest::Artist::setTerms(const Echonest::TermList& terms) { d->terms = terms; } QString Echonest::Artist::twitter() const { return d->twitter; } void Echonest::Artist::setTwitter(const QString& twitter) { d->twitter = twitter; } Echonest::Genres Echonest::Artist::genres() const { return d->genres; } void Echonest::Artist::setGenres(const Echonest::Genres& genres) { d->genres = genres; } QUrl Echonest::Artist::amazonUrl() const { return d->amazon_url; } void Echonest::Artist::setVideos(const Echonest::VideoList& videos) { d->videos = videos; } void Echonest::Artist::setAmazonUrl(const QUrl& url) { d->amazon_url = url; } QUrl Echonest::Artist::aolMusicUrl() const { return d->aolmusic_url; } void Echonest::Artist::setAolMusicUrl(const QUrl& url) { d->aolmusic_url = url; } QUrl Echonest::Artist::itunesUrl() const { return d->itunes_url; } void Echonest::Artist::setItunesUrl( const QUrl& url ) { d->itunes_url = url; } QUrl Echonest::Artist::lastFmUrl() const { return d->lastfm_url; } void Echonest::Artist::setLastFmUrl(const QUrl& url ) { d->lastfm_url = url; } QUrl Echonest::Artist::myspaceUrl() const { return d->myspace_url; } void Echonest::Artist::setMyspaceUrl( const QUrl& url ) { d->myspace_url = url; } QUrl Echonest::Artist::musicbrainzUrl() const { return d->mb_url; } void Echonest::Artist::setMusicbrainzUrl(const QUrl& url) { d->mb_url = url; } Echonest::VideoList Echonest::Artist::videos() const { return d->videos; } Echonest::ForeignIds Echonest::Artist::foreignIds() const { return d->foreign_ids; } void Echonest::Artist::setForeignIds(const Echonest::ForeignIds& ids) { d->foreign_ids = ids; } QNetworkReply* Echonest::Artist::fetchAudio(int numResults, int offset) const { QUrl url = setupQuery( "audio", numResults, offset ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchBiographies(const QString& license, int numResults, int offset) const { QUrl url = setupQuery( "biographies", numResults, offset ); if( !license.isEmpty() ) urlAddQueryItem( url, QLatin1String( "license" ), license ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchBlogs( bool highRelevanceOnly, int numResults, int offset ) const { QUrl url = setupQuery( "blogs", numResults, offset ); if( highRelevanceOnly ) // false is default urlAddQueryItem( url, QLatin1String( "high_relevance" ), QLatin1String( "true" ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchFamiliarity() const { QUrl url = setupQuery( "familiarity", 0, -1 ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchHotttnesss(const QString& type) const { QUrl url = setupQuery( "hotttnesss", 0, -1 ); if( type != QLatin1String( "normal" ) ) urlAddQueryItem( url, QLatin1String( "type" ), type ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchImages( const QString& license, int numResults, int offset ) const { QUrl url = setupQuery( "images", numResults, offset ); if( !license.isEmpty() ) urlAddQueryItem( url, QLatin1String( "license" ), license ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchProfile(Echonest::ArtistInformation information) const { QUrl url = setupQuery( "profile", 0, -1 ); addQueryInformation( url, information ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchNews( bool highRelevanceOnly, int numResults, int offset ) const { QUrl url = setupQuery( "news", numResults, offset ); if( highRelevanceOnly ) // false is default urlAddQueryItem( url, QLatin1String( "high_relevance" ), QLatin1String( "true" ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchReviews(int numResults, int offset) const { QUrl url = setupQuery( "reviews", numResults, offset ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchSimilar(const Echonest::Artist::SearchParams& params, Echonest::ArtistInformation information, int numResults, int offset ) { QUrl url = Echonest::baseGetQuery( "artist", "similar" ); addQueryInformation( url, information ); if( numResults > 0 ) urlAddQueryItem( url, QLatin1String( "results" ), QString::number( numResults ) ); if( offset >= 0 ) urlAddQueryItem( url, QLatin1String( "start" ), QString::number( offset ) ); Echonest::Artist::SearchParams::const_iterator iter = params.constBegin(); for( ; iter < params.constEnd(); ++iter ) urlAddQueryItem( url, QString::fromLatin1( searchParamToString( iter->first ) ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( iter->second.toString() ) ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchSongs( int numResults, int offset ) const { QUrl url = setupQuery( "songs", numResults, offset ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchTerms( Echonest::Artist::TermSorting sorting ) const { QUrl url = setupQuery( "terms", 0, -1 ); if( sorting == Echonest::Artist::Weight ) urlAddQueryItem( url, QLatin1String( "sort" ), QLatin1String( "weight" ) ); else if( sorting == Echonest::Artist::Frequency ) urlAddQueryItem( url, QLatin1String( "sort" ), QLatin1String( "frequency" ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchTwitter() const { QUrl url = setupQuery( "twitter", 0, -1 ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchUrls() const { QUrl url = setupQuery( "urls", 0, -1 ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::fetchVideo(int numResults, int offset) const { QUrl url = setupQuery( "video", numResults, offset ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply *Echonest::Artist::fetchGenres() { QUrl url = baseGetQuery( "artist", "list_genres" ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::search(const Echonest::Artist::SearchParams& params, Echonest::ArtistInformation information, bool limit) { QUrl url = Echonest::baseGetQuery( "artist", "search" ); Echonest::Artist::SearchParams::const_iterator iter = params.constBegin(); for( ; iter < params.constEnd(); ++iter ) urlAddQueryItem( url, QString::fromLatin1( searchParamToString( iter->first ) ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( iter->second.toString() ) ) ); urlAddQueryItem( url, QLatin1String( "limit" ), QLatin1String( limit ? "true" : "false" ) ); addQueryInformation( url, information ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::topHottt(Echonest::ArtistInformation information, int numResults, int offset, bool limit) { QUrl url = Echonest::baseGetQuery( "artist", "top_hottt" ); addQueryInformation( url, information ); if( numResults > 0 ) urlAddQueryItem( url, QLatin1String( "results" ), QString::number( numResults ) ); if( offset >= 0 ) urlAddQueryItem( url, QLatin1String( "start" ), QString::number( offset ) ); urlAddQueryItem( url, QLatin1String( "limit" ), QLatin1String( limit ? "true" : "false" ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::topTerms(int numResults) { QUrl url = Echonest::baseGetQuery( "artist", "top_terms" ); urlAddQueryItem( url, QLatin1String( "results" ), QString::number( numResults ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::listTerms( const QString& type ) { QUrl url = Echonest::baseGetQuery( "artist", "list_terms" ); urlAddQueryItem( url, QLatin1String( "type" ), type ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Artist::suggest( const QString& name, int results ) { QUrl url = Echonest::baseGetQuery( "artist", "suggest" ); QString realname = name; urlAddQueryItem( url, QLatin1String( "name" ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( realname ) ) ); urlAddQueryItem( url, QLatin1String( "results" ), QString::number( results ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } int Echonest::Artist::parseProfile( QNetworkReply* reply ) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); int numResults = Echonest::Parser::parseArtistInfoOrProfile( xml, *this ); reply->deleteLater(); return numResults; } Echonest::Artists Echonest::Artist::parseSearch( QNetworkReply* reply ) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QByteArray data = reply->readAll(); QXmlStreamReader xml( data ); Echonest::Parser::readStatus( xml ); Echonest::Artists artists = Echonest::Parser::parseArtists( xml ); reply->deleteLater(); return artists; } Echonest::Artists Echonest::Artist::parseSimilar( QNetworkReply* reply ) throw( Echonest::ParseError ) { return parseSearch( reply ); } Echonest::Artists Echonest::Artist::parseTopHottt( QNetworkReply* reply ) throw( Echonest::ParseError ) { return parseSearch( reply ); } Echonest::TermList Echonest::Artist::parseTopTerms( QNetworkReply* reply ) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); Echonest::TermList terms = Echonest::Parser::parseTopTermList( xml ); reply->deleteLater(); return terms; } Echonest::Artists Echonest::Artist::parseSuggest( QNetworkReply* reply ) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); Echonest::Artists artists = Echonest::Parser::parseArtistSuggestList( xml ); reply->deleteLater(); return artists; } QVector< QString > Echonest::Artist::parseTermList( QNetworkReply* reply ) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); QVector< QString > terms = Echonest::Parser::parseTermList( xml ); reply->deleteLater(); return terms; } QVector< QString > Echonest::Artist::parseGenreList( QNetworkReply* reply ) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); QVector< QString > genres = Echonest::Parser::parseGenreListStrings( xml ); reply->deleteLater(); return genres; } QUrl Echonest::Artist::setupQuery( const QByteArray& methodName, int numResults, int start ) const { QUrl url = Echonest::baseGetQuery( "artist", methodName ); if( !d->id.isEmpty() ) urlAddQueryItem( url, QLatin1String( "id" ), QString::fromLatin1( d->id ) ); else if( !d->name.isEmpty() ) { urlAddQueryItem( url, QLatin1String( "name" ), QString::fromLatin1( Echonest::escapeSpacesAndPluses( d->name ) ) ); } else if ( methodName != "terms" ) { qWarning() << "Artist method" << methodName << "called on an artist object without name or id!"; return QUrl(); } if( numResults > 0 ) urlAddQueryItem( url, QLatin1String( "results" ), QString::number( numResults ) ); if( start >= 0 ) urlAddQueryItem( url, QLatin1String( "start" ), QString::number( start ) ); return url; } QByteArray Echonest::Artist::searchParamToString(Echonest::Artist::SearchParam param) { switch( param ) { case Id: return "id"; case Name: return "name"; case Results: return "results"; case Description: return "description"; case FuzzyMatch: return "fuzzy_match"; case MaxFamiliarity: return "max_familiarity"; case MinFamiliarity: return "min_familiarity"; case MaxHotttnesss: return "max_hotttnesss"; case MinHotttnesss: return "min_hotttnesss"; case Reverse: return "reverse"; case Sort: return "sort"; case Mood: return "mood"; default: return ""; } } void Echonest::Artist::addQueryInformation(QUrl& url, Echonest::ArtistInformation information) { if( information.flags().testFlag( Echonest::ArtistInformation::Audio ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "audio" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Biographies ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "biographies" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Blogs ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "blogs" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Familiarity ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "familiarity" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Hotttnesss ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "hotttnesss" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Images ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "images" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::News ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "news" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Reviews ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "reviews" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Terms ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "terms" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Urls ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "urls" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Videos ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "video" ) ); if( information.flags().testFlag( Echonest::ArtistInformation::Genre ) ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "genre" ) ); if( !information.idSpaces().isEmpty() ) { foreach( const QString& idSpace, information.idSpaces() ) urlAddQueryItem( url, QLatin1String( "bucket" ), QLatin1String( "id:" + idSpace.toUtf8() ) ); } } QDebug Echonest::operator<<(QDebug d, const Echonest::Artist& artist) { return d.maybeSpace() << QString::fromLatin1( "Artist(%1, %2)" ).arg( artist.name() ).arg( QString::fromLatin1(artist.id()) ); } libechonest-2.3.1/src/Generator.cpp000644 001750 001750 00000010632 12465467260 020055 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Generator_p.h" #include "CatalogUpdateEntry.h" // JSON Parsing #include "qjsonwrapper/Json.h" #include #include #include QByteArray Echonest::Generator::catalogEntriesToJson( const Echonest::CatalogUpdateEntries& items ) { QVariant itms = catalogEntriesToVariant( items ); QByteArray serialized = QJsonWrapper::toJson( itms ); // qDebug() << "Serialized:" << serialized; return serialized; } QByteArray Echonest::Generator::catalogEntryToJson( const Echonest::CatalogUpdateEntry& item ) { QVariant itm = catalogEntryToVariant( item ); QByteArray serialized = QJsonWrapper::toJson( itm ); // qDebug() << "Serialized:" << serialized; return serialized; } QVariantList Echonest::Generator::catalogEntriesToVariant( const Echonest::CatalogUpdateEntries& items ) { // copy the catalog item into a QVariant QVariantList itemList; foreach( const Echonest::CatalogUpdateEntry& item, items ) itemList << catalogEntryToVariant( item ); qDebug() << "Generated " << itemList.size() << "entries to catalog variant!"; return itemList; } QVariant Echonest::Generator::catalogEntryToVariant( const Echonest::CatalogUpdateEntry& item ) { QVariantMap itemMap; QVariantMap itm; itemMap[ QLatin1String( "action" ) ] = Echonest::catalogUpdateActionToLiteral( item.action() ); if( item.itemId().isEmpty() ) itm[ QLatin1String( "item_id" ) ] = QUuid::createUuid().toString().replace( QLatin1Char( '{' ), QString() ).replace( QLatin1Char( '}' ), QString() ); else itm[ QLatin1String( "item_id" ) ] = item.itemId(); if( !item.fingerprint().isEmpty() ) itm[ QLatin1String( "fp_code" ) ] = item.fingerprint(); if( !item.songId().isEmpty() ) itm[ QLatin1String( "song_id" ) ] = item.songId(); if( !item.songName().isEmpty() ) itm[ QLatin1String( "song_name" ) ] = item.songName(); if( !item.artistId().isEmpty() ) itm[ QLatin1String( "artist_id" ) ] = item.artistId(); if( !item.artistName().isEmpty() ) itm[ QLatin1String( "artist_name" ) ] = item.artistName(); if( !item.release().isEmpty() ) itm[ QLatin1String( "release" ) ] = item.release(); if( !item.genre().isEmpty() ) itm[ QLatin1String( "genre" ) ] = item.genre(); if( item.trackNumber() > -1 ) itm[ QLatin1String( "track_number" ) ] = item.trackNumber(); if( item.discNumber() > -1 ) itm[ QLatin1String( "disc_number" ) ] = item.discNumber(); if( !item.url().isEmpty() ) itm[ QLatin1String( "url" ) ] = item.url(); if( item.favoriteSet() ) itm[ QLatin1String( "favorite" ) ] = item.favorite(); if( item.bannedSet() ) itm[ QLatin1String( "banned" ) ] = item.banned(); if( item.playCount() > -1 ) itm[ QLatin1String( "play_count" ) ] = item.playCount(); if( item.skipCount() > -1 ) itm[ QLatin1String( "skip_count" ) ] = item.skipCount(); if( item.rating() > -1 ) itm[ QLatin1String( "rating" ) ] = item.rating(); itemMap[ QLatin1String( "item" ) ] = itm; return itemMap; } libechonest-2.3.1/tests/PlaylistTest.cpp000644 001750 001750 00000104433 12465467260 021146 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "PlaylistTest.h" #include "Config.h" #include "Playlist.h" #include #include #include #include #include using namespace Echonest; void PlaylistTest::initTestCase() { Config::instance()->setAPIKey( "JGJCRKWLXLBZIFAZB" ); } void PlaylistTest::testStatic1() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "tallest man on earth" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "bon iver" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "mumford and sons" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistRadioType ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Results, 10 ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toString(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/playlist/static?api_key=JGJCRKWLXLBZIFAZB&format=xml&artist=tallest+man+on+earth&artist=bon+iver&artist=mumford+and+sons&type=artist-radio&results=10" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); SongList songs = DynamicPlaylist::parseStaticPlaylist( reply ); QVERIFY( songs.size() == 10 ); Q_FOREACH( const Song& song, songs ) QVERIFY( !song.id().isEmpty() ); } void PlaylistTest::testStatic2() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "tallest man on earth" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "bon iver" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "mumford and sons" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "Florence + The Machine" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::ArtistMaxFamiliarity, 0.4 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::MinDanceability, 0.7 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( Echonest::SongInformation( Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity ) ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 4 ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toEncoded(); QCOMPARE( QLatin1String( reply->url().toEncoded() ), QLatin1String( "http://developer.echonest.com/api/v4/playlist/static?api_key=JGJCRKWLXLBZIFAZB&format=xml&artist=tallest+man+on+earth&artist=bon+iver&artist=mumford+and+sons&artist=Florence+%252B+The+Machine&artist_max_familiarity=0.4&min_danceability=0.7&type=artist&bucket=song_hotttnesss&bucket=artist_hotttnesss&bucket=artist_familiarity&results=4" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); SongList songs = DynamicPlaylist::parseStaticPlaylist( reply ); // qDebug() << "Got songs;" << songs; QVERIFY( songs.size() > 1 ); Q_FOREACH( const Song& song, songs ) { QVERIFY( !song.id().isEmpty() ); } p.clear(); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Description, QLatin1String( "70s" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Description, QLatin1String( "folk^2" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::ArtistMinFamiliarity, 0.4 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::MaxTempo, 100 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Mode, 0 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistDescriptionType ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Style, QLatin1String( "acoustic" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( Echonest::SongInformation( Echonest::SongInformation::AudioSummaryInformation | Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity ) ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 7 ) ); reply = DynamicPlaylist::staticPlaylist( p ); // qDebug() << reply->url().toString(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/playlist/static?api_key=JGJCRKWLXLBZIFAZB&format=xml&description=70s&description=folk^2&artist_min_familiarity=0.4&max_tempo=100&mode=0&type=artist-description&style=acoustic&bucket=audio_summary&bucket=song_hotttnesss&bucket=artist_hotttnesss&bucket=artist_familiarity&results=7" ) ); QEventLoop loop2; loop2.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop2.exec(); songs = DynamicPlaylist::parseStaticPlaylist( reply ); QVERIFY( songs.size() == 7 ); Q_FOREACH( const Song& song, songs ) { QVERIFY( !song.id().isEmpty() ); // qDebug() << song << song.audioSummary(); QVERIFY( song.audioSummary().duration() > 0 ); } } void PlaylistTest::testStaticArtistYears() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Type, DynamicPlaylist::ArtistDescriptionType ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Description, QLatin1String( "rock" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::ArtistStartYearBefore, 1970 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::ArtistEndYearBefore, 1980 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( Echonest::SongInformation( Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity ) ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 4 ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toEncoded(); QCOMPARE( QLatin1String( reply->url().toEncoded() ), QLatin1String( "http://developer.echonest.com/api/v4/playlist/static?api_key=JGJCRKWLXLBZIFAZB&format=xml&type=artist-description&description=rock&artist_start_year_before=1970&artist_end_year_before=1980&bucket=song_hotttnesss&bucket=artist_hotttnesss&bucket=artist_familiarity&results=4" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); SongList songs = DynamicPlaylist::parseStaticPlaylist( reply ); // qDebug() << "Got songs;" << songs; QCOMPARE( songs.size(), 4 ); Q_FOREACH( const Song& song, songs ) { QVERIFY( !song.id().isEmpty() ); } } void PlaylistTest::testStaticWithSongType() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "The Beatles" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongType, QLatin1String( "live" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( Echonest::SongInformation( Echonest::SongInformation::AudioSummaryInformation | Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity | Echonest::SongInformation::SongType ) ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Sort, DynamicPlaylist::SortLivenessAscending ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 20 ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toEncoded(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); SongList songs = DynamicPlaylist::parseStaticPlaylist( reply ); Q_FOREACH( const Song& song, songs ) { QVERIFY( song.songTypes().contains( QLatin1String("live" ) ) ); } } void PlaylistTest::testStaticXSPF() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "balmorhea" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "tallest man on earth" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "explosions in the sky" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::ArtistMaxFamiliarity, 0.4 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Format, QLatin1String( "xspf" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::MaxDanceability, 0.5 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) ); Echonest::SongInformation info( Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity | Echonest::SongInformation::Tracks ); info.setIdSpaces( QStringList() << QLatin1String( "musicbrainz" ) << QLatin1String( "7digital" ) << QLatin1String( "playme" ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( info ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Limit, true ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 40 ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toString(); QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/playlist/static?api_key=JGJCRKWLXLBZIFAZB&artist=balmorhea&artist=tallest+man+on+earth&artist=explosions+in+the+sky&artist_max_familiarity=0.4&format=xspf&max_danceability=0.5&type=artist&bucket=tracks&bucket=song_hotttnesss&bucket=artist_hotttnesss&bucket=artist_familiarity&bucket=id:musicbrainz&bucket=id:7digital&bucket=id:playme&limit=true&results=40" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QByteArray xspf = DynamicPlaylist::parseXSPFPlaylist( reply ); // verify it's valid QDomDocument doc; QVERIFY( doc.setContent( xspf ) ); QVERIFY( !xspf.isEmpty() ); } void PlaylistTest::testDynamic1() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "tallest man on earth" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "fleet foxes" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "johnny cash" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistRadioType ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ArtistMinHotttnesss, .6 ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ArtistMaxFamiliarity, .4 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Mood, QLatin1String( "sad" ) ) ); DynamicPlaylist playlist; QNetworkReply* reply = playlist.create( p ); qDebug() << reply->url().toString(); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/playlist/dynamic/create?api_key=JGJCRKWLXLBZIFAZB&format=xml&artist=tallest+man+on+earth&artist=fleet+foxes&artist=johnny+cash&type=artist-radio&artist_min_hotttnesss=0.6&artist_max_familiarity=0.4&mood=sad" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseCreate( reply ); reply = playlist.next( 1 ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Song song = playlist.parseNext( reply ).first.first(); // qDebug() << "next:" << song; QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); reply = playlist.next( 1 ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song = playlist.parseNext( reply ).first.first(); // qDebug() << "next:" << song; QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); reply = playlist.next( 1 ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song = playlist.parseNext( reply ).first.first(); // qDebug() << "next:" << song; QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); } void PlaylistTest::testDynamic2() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "pink floyd^-1" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "the who" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "queen" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "led zeppelin^2" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "-the beatles" ) ) ); //exclude p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ArtistMinHotttnesss, .7 ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ArtistMaxFamiliarity, .3 ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::MinLoudness, -10 ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Mode, 1 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( Echonest::SongInformation( Echonest::SongInformation::AudioSummaryInformation | Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity ) ) ) ); DynamicPlaylist playlist; QNetworkReply* reply = playlist.create( p ); qDebug() << reply->url().toString(); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/playlist/dynamic/create?api_key=JGJCRKWLXLBZIFAZB&format=xml&artist=pink+floyd^-1&artist=the+who&artist=queen&artist=led+zeppelin^2&artist=-the+beatles&type=artist&artist_min_hotttnesss=0.7&artist_max_familiarity=0.3&min_loudness=-10&mode=1&bucket=audio_summary&bucket=song_hotttnesss&bucket=artist_hotttnesss&bucket=artist_familiarity" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseCreate( reply ); reply = playlist.next( 1 ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::Song song = playlist.parseNext( reply ).first.first(); // qDebug() << "next:" << song; QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); // GET NEXT reply = playlist.next( 1 ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song = playlist.parseNext( reply ).first.first(); // qDebug() << "next:" << song; QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); // GET NEXT reply = playlist.next( 1 ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song = playlist.parseNext( reply ).first.first(); // qDebug() << "next:" << song; QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); // GET NEXT DynamicPlaylist::PlaylistParams steering; steering.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::TargetEnergy, QLatin1String( ".8" ) ) ); steering.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ArtistMinFamiliarity, QLatin1String( ".5" ) ) ); reply = playlist.steer( steering ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseSteer( reply ); reply = playlist.next( 1 ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song = playlist.parseNext( reply ).first.first(); // qDebug() << "next steered:" << song; QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); // GET NEXT steering.clear(); steering.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::TargetLoudness, QLatin1String( ".9" ) ) ); //steering.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::MoreLikeThis, QLatin1String( "last" ) ) ); //BUG: this no longer seems to work reply = playlist.steer( steering ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseSteer( reply ); playlist.next(); reply = playlist.next( 1 ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song = playlist.parseNext( reply ).first.first(); // qDebug() << "next steered:" << song; QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); // GET INFO TODO /*reply = playlist.fetchSessionInfo(); qDebug() << "session info URL:" << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::SessionInfo info = playlist.parseSessionInfo( reply ); QVERIFY( !info.banned_artists.isEmpty() ); QVERIFY( !info.banned_songs.isEmpty() ); QVERIFY( !info.banned_songs.at( 0 ).artist_id.isEmpty() ); QVERIFY( !info.banned_songs.at( 0 ).artist_name.isEmpty() ); QVERIFY( !info.banned_songs.at( 0 ).id.isEmpty() ); QVERIFY( !info.banned_songs.at( 0 ).title.isEmpty() ); QVERIFY( !info.skipped_songs.isEmpty() ); QVERIFY( !info.skipped_songs.at( 0 ).artist_id.isEmpty() ); QVERIFY( !info.skipped_songs.at( 0 ).artist_name.isEmpty() ); QVERIFY( !info.skipped_songs.at( 0 ).id.isEmpty() ); QVERIFY( !info.skipped_songs.at( 0 ).title.isEmpty() ); QVERIFY( !info.rated_songs.isEmpty() ); QVERIFY( !info.rated_songs.at( 0 ).artist_id.isEmpty() ); QVERIFY( !info.rated_songs.at( 0 ).artist_name.isEmpty() ); QVERIFY( !info.rated_songs.at( 0 ).id.isEmpty() ); QVERIFY( !info.rated_songs.at( 0 ).title.isEmpty() ); QVERIFY( !info.history.isEmpty() ); QVERIFY( !info.history.at( 0 ).artist_id.isEmpty() ); QVERIFY( !info.history.at( 0 ).artist_name.isEmpty() ); QVERIFY( !info.history.at( 0 ).id.isEmpty() ); QVERIFY( !info.history.at( 0 ).title.isEmpty() ); QVERIFY( !info.playlist_type.isEmpty() ); QVERIFY( !info.rules.isEmpty() ); QVERIFY( !info.session_id.isEmpty() ); QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() );*/ QByteArray oldId = playlist.sessionId(); // now reset it p.clear(); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistRadioType ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "pink floyd" ) ) ); reply = playlist.create( p ); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseCreate( reply ); reply = playlist.next(); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); song = playlist.parseNext( reply ).first.first(); // qDebug() << "new:" << song << song.artistFamiliarity() << song.artistHotttnesss(); QVERIFY( !song.id().isEmpty() ); QVERIFY( !song.title().isEmpty() ); QVERIFY( song.artistHotttnesss() == -1 ); // make sure we are in a new playlist, and we didn't ask for this info so it shouldn't be there QVERIFY( song.artistFamiliarity() == -1 ); // make sure we are in a new playlist, and we didn't ask for this info so it shouldn't be there reply = playlist.deleteSession(); qDebug() << reply->url().toString(); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseDeleteSession(reply); Q_ASSERT(playlist.sessionId().isEmpty()); } void PlaylistTest::testNewDynamicAPI() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "frightened rabbit" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "wilco" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "the tallest man on earth" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "noah and the whale" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ArtistMaxFamiliarity, QLatin1String( ".4" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Key, QLatin1String( "3" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistRadioType ) ); DynamicPlaylist playlist; QNetworkReply* reply = playlist.create( p ); // qDebug() << reply->url().toString(); QCOMPARE( reply->url().toString(), QLatin1String( "http://developer.echonest.com/api/v4/playlist/dynamic/create?api_key=JGJCRKWLXLBZIFAZB&format=xml&artist=frightened+rabbit&artist=wilco&artist=the+tallest+man+on+earth&artist=noah+and+the+whale&artist_max_familiarity=.4&key=3&type=artist-radio" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseCreate( reply ); reply = playlist.next( 2 ); // qDebug() << reply->url().toString(); QCOMPARE( reply->url().toString(), QString( QLatin1String( "http://developer.echonest.com/api/v4/playlist/dynamic/next?api_key=JGJCRKWLXLBZIFAZB&format=xml&session_id=%1&results=2&lookahead=0" ) ).arg( QString::fromLatin1( playlist.sessionId() ) ) ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QPair< SongList, SongList > ret; ret = playlist.parseNext( reply ); QCOMPARE( ret.first.size(), 2 ); QCOMPARE( ret.second.size(), 0 ); QVERIFY( !ret.first.at( 0 ).id().isNull() ); QVERIFY( !ret.first.at( 0 ).title().isNull() ); QVERIFY( !ret.first.at( 1 ).id().isNull() ); QVERIFY( !ret.first.at( 1 ).title().isNull() ); DynamicPlaylist::DynamicFeedback feedback; feedback.append( DynamicPlaylist::DynamicFeedbackParamData( DynamicPlaylist::FavoriteArtist, "last" ) ); feedback.append( DynamicPlaylist::DynamicFeedbackParamData( DynamicPlaylist::FavoriteSong, "last" ) ); reply = playlist.feedback( feedback ); // qDebug() << reply->url().toString(); QString u = QString::fromUtf8( "http://developer.echonest.com/api/v4/playlist/dynamic/feedback?api_key=JGJCRKWLXLBZIFAZB&format=xml&session_id=%1&favorite_artist=last&favorite_song=last" ); QCOMPARE( reply->url().toString(), u.arg( QString::fromUtf8( playlist.sessionId() ) ) ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseFeedback( reply ); DynamicPlaylist::PlaylistParams steering; steering.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::MoreLikeThis, QLatin1String( "SONRAMP13775DE8214^4" ) ) ); steering.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Mood, QLatin1String( "happy^2" ) ) ); reply = playlist.steer( steering ); // qDebug() << reply->url().toString(); u = QString::fromUtf8( "http://developer.echonest.com/api/v4/playlist/dynamic/steer?api_key=JGJCRKWLXLBZIFAZB&format=xml&session_id=%1&more_like_this=SONRAMP13775DE8214^4&mood=happy^2" ); QCOMPARE( reply->url().toString(), u.arg( QString::fromUtf8( playlist.sessionId() ) ) ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); playlist.parseSteer( reply ); reply = playlist.next( 3, 3 ); // qDebug() << reply->url().toString(); u = QString::fromUtf8( "http://developer.echonest.com/api/v4/playlist/dynamic/next?api_key=JGJCRKWLXLBZIFAZB&format=xml&session_id=%1&results=3&lookahead=3" ); QCOMPARE( reply->url().toString(), u.arg( QString::fromUtf8( playlist.sessionId() ) ) ); loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); ret = playlist.parseNext( reply ); QCOMPARE( ret.first.size(), 3 ); QCOMPARE( ret.second.size(), 3 ); QVERIFY( !ret.first.at( 0 ).id().isNull() ); QVERIFY( !ret.first.at( 0 ).title().isNull() ); QVERIFY( !ret.first.at( 1 ).id().isNull() ); QVERIFY( !ret.first.at( 1 ).title().isNull() ); QVERIFY( !ret.first.at( 2 ).id().isNull() ); QVERIFY( !ret.first.at( 2 ).title().isNull() ); QVERIFY( !ret.second.at( 0 ).id().isNull() ); QVERIFY( !ret.second.at( 0 ).title().isNull() ); QVERIFY( !ret.second.at( 1 ).id().isNull() ); QVERIFY( !ret.second.at( 1 ).title().isNull() ); QVERIFY( !ret.second.at( 2 ).id().isNull() ); QVERIFY( !ret.second.at( 2 ).title().isNull() ); } void PlaylistTest::testDynamicChainXSPF() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "pink floyd^-1" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "the who" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "queen" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "led zeppelin^2" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "-the beatles" ) ) ); //exclude p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ArtistMinHotttnesss, .7 ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ArtistMaxFamiliarity, .3 ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::MinLoudness, -10 ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Mode, 1 ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::ChainXSPF, true ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Format, QLatin1String( "xspf" ) ) ); Echonest::SongInformation info( Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity | Echonest::SongInformation::Tracks ); info.setIdSpaces( QStringList() << QLatin1String( "musicbrainz" ) << QLatin1String( "7digital" ) << QLatin1String( "playme" ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( info ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Limit, true ) ); DynamicPlaylist playlist; QNetworkReply* reply = playlist.create( p ); qDebug() << reply->url().toString(); // QVERIFY( reply->url().toString() == QLatin1String( "http://developer.echonest.com/api/v4/playlist/dynamic?api_key=JGJCRKWLXLBZIFAZB&artist=pink+floyd^-1&artist=the+who&artist=queen&artist=led+zeppelin^2&artist=-the+beatles&type=artist&artist_min_hotttnesss=0.7&artist_max_familiarity=0.3&min_loudness=-10&mode=1&chain_xspf=true&format=xspf&bucket=audio_summary&bucket=song_hotttnesss&bucket=artist_hotttnesss&bucket=artist_familiarity" ) ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QByteArray xspf = Echonest::DynamicPlaylist::parseXSPFPlaylist( reply ); // qDebug() << "xspf:" << xspf; // verify it's valid QDomDocument doc; QVERIFY( doc.setContent( xspf ) ); QVERIFY( !xspf.isEmpty() ); } void PlaylistTest::testGenreRadio() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Genre, QLatin1String( "dance pop" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::GenreRadioType ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( Echonest::SongInformation( Echonest::SongInformation::AudioSummaryInformation | Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity | Echonest::SongInformation::SongType ) ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 20 ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toEncoded(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); SongList songs = DynamicPlaylist::parseStaticPlaylist( reply ); QVERIFY( songs.size() == 20 ); /*Q_FOREACH( const Song& song, songs ) { //no usefull test code can be inserted here, because genres are not (yet?) supported in songs (e.g. to check via SongInformation if a song is genre "dance pop") qDebug() << song; }*/ } void PlaylistTest::testAudioSummaryAttributes() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Artist, QLatin1String( "The Doors" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::MinLiveness, 0.7 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::MaxValence, 0.7 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Sort, DynamicPlaylist::SortValenceDescending ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::SongInformation, QVariant::fromValue( Echonest::SongInformation( Echonest::SongInformation::AudioSummaryInformation | Echonest::SongInformation::Hotttnesss | Echonest::SongInformation::ArtistHotttnesss | Echonest::SongInformation::ArtistFamiliarity | Echonest::SongInformation::SongType ) ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 10 ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toEncoded(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); SongList songs = DynamicPlaylist::parseStaticPlaylist( reply ); Q_FOREACH( const Song& song, songs ) { QVERIFY( song.audioSummary().liveness() >= 0.7 ); } } void PlaylistTest::testGenrePresets() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Genre, QLatin1String( "classic rock" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::GenreRadioType ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::GenrePreset, DynamicPlaylist::CoreBest ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 30 ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toEncoded(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); SongList songs = DynamicPlaylist::parseStaticPlaylist( reply ); QVERIFY( songs.size() == 30 ); } void PlaylistTest::testDistribution() { DynamicPlaylist::PlaylistParams p; p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Artist, QLatin1String( "the doors" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistRadioType ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Results, 50 ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::Distribution, QLatin1String( "wandering" ) ) ); p.append( DynamicPlaylist::PlaylistParamData( DynamicPlaylist::GenrePreset, DynamicPlaylist::CoreBest ) ); QNetworkReply* reply = DynamicPlaylist::staticPlaylist( p ); qDebug() << reply->url().toEncoded(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); SongList songs = DynamicPlaylist::parseStaticPlaylist( reply ); Q_FOREACH( const Song& song, songs) { qDebug() << song; } QVERIFY( songs.size() == 50 ); } QTEST_MAIN( PlaylistTest ) #include "PlaylistTest.moc" libechonest-2.3.1/libechonest.pc.in000644 001750 001750 00000001023 12465467260 020056 0ustar00stefanstefan000000 000000 prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=@LIB_INSTALL_DIR@ includedir=@INCLUDE_INSTALL_DIR@ Name: libechonest@ECHONEST_LIB_VERSION_SUFFIX@ Description: libechonest is a qt-based library that makes the Echo Nest APIs easily accessible Version: @ECHONEST_LIB_MAJOR_VERSION@.@ECHONEST_LIB_MINOR_VERSION@.@ECHONEST_LIB_PATCH_VERSION@ Requires: Qt@ECHONEST_QT_MAJOR_VERSION@Core Qt@ECHONEST_QT_MAJOR_VERSION@Network @ECHONEST_PC_DEPS@ Libs: -L${libdir} -lechonest@ECHONEST_LIB_VERSION_SUFFIX@ Cflags: -I${includedir} libechonest-2.3.1/src/qjsonwrapper/000755 001750 001750 00000000000 12465467260 020154 5ustar00stefanstefan000000 000000 libechonest-2.3.1/src/Catalog.cpp000644 001750 001750 00000024323 12465467260 017503 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Catalog.h" #include "Catalog_p.h" #include "Parsing_p.h" #include "Generator_p.h" #include "Track_p.h" Echonest::Catalog::Catalog() : d( new CatalogData ) { } Echonest::Catalog::Catalog( const QByteArray& id ) : d( new CatalogData ) { d->id = id; } Echonest::Catalog::Catalog( const Echonest::Catalog& catalog ) : d( catalog.d ) { } Echonest::Catalog& Echonest::Catalog::operator=( const Echonest::Catalog& other ) { d = other.d; return *this; } Echonest::Catalog::~Catalog() { } Echonest::CatalogArtists Echonest::Catalog::artists() const { return d->artists; } void Echonest::Catalog::setArtists(const Echonest::CatalogArtists& artists) { d->artists = artists; } QByteArray Echonest::Catalog::id() const { return d->id; } void Echonest::Catalog::setId(const QByteArray& id) { d->id = id; } QString Echonest::Catalog::name() const { return d->name; } void Echonest::Catalog::setName(const QString& name) { d->name = name; } int Echonest::Catalog::resolved() const { return d->resolved; } void Echonest::Catalog::setResolved(int resolved) { d->resolved = resolved; } int Echonest::Catalog::pendingTickets() const { // return d->pending_tickets; return 0; } void Echonest::Catalog::setPendingTickets(int pending) { // d->pending_tickets = pending; } Echonest::CatalogSongs Echonest::Catalog::songs() const { return d->songs; } void Echonest::Catalog::setSongs(const Echonest::CatalogSongs& songs) { d->songs = songs; } int Echonest::Catalog::total() const { return d->total; } void Echonest::Catalog::setTotal(int total) { d->total = total; } Echonest::CatalogTypes::Type Echonest::Catalog::type() const { return d->type; } void Echonest::Catalog::setType(Echonest::CatalogTypes::Type type) { d->type = type; } QNetworkReply* Echonest::Catalog::create(const QString& name, Echonest::CatalogTypes::Type type) { QUrl url = Echonest::baseGetQuery( "tasteprofile", "create" ); urlAddQueryItem( url, QLatin1String( "name" ), name ); urlAddQueryItem( url, QLatin1String( "type" ), QString::fromLatin1( Echonest::catalogTypeToLiteral( type ) ) ); QNetworkRequest request = QNetworkRequest( url ); request.setHeader( QNetworkRequest::ContentTypeHeader, QLatin1String( "multipart/form-data" ) ); qDebug() << "Sending create url:" << url.toString(); return Echonest::Config::instance()->nam()->post( request, QByteArray() ); } QNetworkReply* Echonest::Catalog::deleteCatalog() const { QUrl url = Echonest::baseGetQuery( "tasteprofile", "delete" ); Q_ASSERT( !d->id.isEmpty() ); urlAddQueryItem( url, QLatin1String( "id" ), QString::fromLatin1( d->id ) ); return Echonest::doPost( url ); } QNetworkReply* Echonest::Catalog::list(int results, int start) { QUrl url = Echonest::baseGetQuery( "tasteprofile", "list" ); addLimits( url, results, start ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Catalog::profile() const { QUrl url = Echonest::baseGetQuery( "tasteprofile", "profile" ); if( !d->id.isEmpty() ) urlAddQueryItem( url, QLatin1String( "id" ), QString::fromLatin1( d->id ) ); else if( !d->name.isEmpty() ) urlAddQueryItem( url, QLatin1String( "name" ), d->name ); else Q_ASSERT_X( false, "Catalog", "Not enough information!" ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Catalog::status(const QByteArray& ticket) { QUrl url = Echonest::baseGetQuery( "tasteprofile", "status" ); urlAddQueryItem( url, QLatin1String( "ticket" ), QString::fromLatin1( ticket ) ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QNetworkReply* Echonest::Catalog::update(const Echonest::CatalogUpdateEntries& entries) const { QUrl url = Echonest::baseGetQuery( "tasteprofile", "update" ); Q_ASSERT( !d->id.isEmpty() ); urlAddQueryItem( url, QLatin1String( "id" ), QString::fromLatin1( d->id ) ); return Echonest::Catalog::updatePrivate( url, entries ); } QNetworkReply* Echonest::Catalog::updateAndCreate(const Echonest::CatalogUpdateEntries& entries) { QUrl url = Echonest::baseGetQuery( "tasteprofile", "update" ); return Echonest::Catalog::updatePrivate( url, entries ); } QNetworkReply* Echonest::Catalog::readArtistCatalog(Echonest::ArtistInformation info, int results, int start) const { QUrl url = Echonest::baseGetQuery( "tasteprofile", "read" ); Artist::addQueryInformation( url, info ); return readPrivate( url, results, start ); } QNetworkReply* Echonest::Catalog::readSongCatalog(Echonest::SongInformation info, int results, int start) const { QUrl url = Echonest::baseGetQuery( "tasteprofile", "read" ); Song::addQueryInformation( url, info ); return readPrivate( url, results, start ); } QPair< QString, QByteArray > Echonest::Catalog::parseDelete( QNetworkReply* reply ) throw( Echonest::ParseError ) { QByteArray data = reply->readAll(); // qDebug() << "DATA:" << data; QPair< QString, QByteArray > asd; Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( data ); Echonest::Parser::readStatus( xml ); // TODO, after create works :) reply->deleteLater(); return asd; } Echonest::Catalogs Echonest::Catalog::parseList(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); Echonest::Catalogs catalogs = Echonest::Parser::parseCatalogList( xml ); reply->deleteLater(); return catalogs; } void Echonest::Catalog::parseProfile(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); Echonest::Catalog catalog = Echonest::Parser::parseCatalog( xml, true ); d = catalog.d; reply->deleteLater(); } void Echonest::Catalog::parseRead(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( reply->readAll() ); Echonest::Parser::readStatus( xml ); Echonest::Catalog catalog = Echonest::Parser::parseCatalog( xml, true ); d = catalog.d; reply->deleteLater(); } Echonest::CatalogStatus Echonest::Catalog::parseStatus(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QByteArray data = reply->readAll(); // qDebug() << data; QXmlStreamReader xml( data ); Echonest::Parser::readStatus( xml ); Echonest::CatalogStatus status = Echonest::Parser::parseCatalogStatus( xml ); reply->deleteLater(); return status; } QByteArray Echonest::Catalog::parseTicket(QNetworkReply* reply) throw( Echonest::ParseError ) { Echonest::Parser::checkForErrors( reply ); QByteArray data = reply->readAll(); // qDebug() << data; QXmlStreamReader xml( data ); Echonest::Parser::readStatus( xml ); QByteArray ticket = Echonest::Parser::parseCatalogTicket( xml ); reply->deleteLater(); return ticket; } Echonest::Catalog Echonest::Catalog::parseCreate(QNetworkReply* reply) throw( Echonest::ParseError ) { QByteArray data = reply->readAll(); // qDebug() << data; Echonest::Parser::checkForErrors( reply ); QXmlStreamReader xml( data ); Echonest::Parser::readStatus( xml ); Catalog c = Echonest::Parser::parseNewCatalog( xml ); reply->deleteLater(); return c; } QNetworkReply* Echonest::Catalog::updatePrivate( QUrl& url, const Echonest::CatalogUpdateEntries& entries) { urlAddQueryItem( url, QLatin1String( "data_type" ), QLatin1String( "json" ) ); QByteArray payload = Generator::catalogEntriesToJson( entries ); urlAddQueryItem( url, QLatin1String( "data" ), QString::fromLatin1( payload ) ); return Echonest::doPost( url ); } void Echonest::Catalog::addLimits(QUrl& url, int results, int start) { if( results != 30 ) urlAddQueryItem( url, QLatin1String( "results" ), QString::number( results ) ); if( start > -1 ) urlAddQueryItem( url, QLatin1String( "start" ), QString::number( start ) ); } QNetworkReply* Echonest::Catalog::readPrivate(QUrl& url, int results, int start) const { Q_ASSERT( !d->id.isEmpty() ); urlAddQueryItem( url, QLatin1String( "id" ), QString::fromLatin1( d->id ) ); addLimits( url, results, start ); return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); } QDebug Echonest::operator<<(QDebug d, const Echonest::Catalog& catalog) { return d.maybeSpace() << QString::fromLatin1( "Catalog(%1, %2, %3, %4)" ).arg( catalog.name() ).arg( QLatin1String( catalog.id() ) ) .arg( QString::fromLatin1( Echonest::catalogTypeToLiteral( catalog.type() ) ) ).arg( catalog.total() ) << catalog.artists() << catalog.songs(); } libechonest-2.3.1/src/Genre.h000644 001750 001750 00000007357 12465467260 016646 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2014 Leo Franchi * * Copyright (c) 2014 Stefan Derkits * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_GENRE_H #define ECHONEST_GENRE_H #include "echonest_export.h" #include "Config.h" #include "TypeInformation.h" #include "CommonTypes.h" #include #include class QNetworkReply; class GenreData; namespace Echonest{ class ECHONEST_EXPORT Genre { public: Genre(); Genre( const Genre& other ); Genre& operator=( const Genre& genre ); virtual ~Genre(); Genre( const QString& name ); void init(); QString name() const; void setName( const QString& name ); Artists artists() const; void setArtists( const Artists& artists ); QUrl wikipediaUrl() const; void setWikipediaUrl( const QUrl& url ); QString description() const; void setDescription( const QString& description ); QNetworkReply* fetchArtists( ArtistInformation information = ArtistInformation(), int numResults = 0, bool limit = false ); QNetworkReply* fetchSimilar( GenreInformation information = GenreInformation(), int numResults = 0, int start = -1 ); static QNetworkReply* fetchProfile( const Genres& genres, GenreInformation information = GenreInformation() ); static QNetworkReply* fetchList( GenreInformation information = GenreInformation(), int numResults = 0 ); static QNetworkReply* fetchSearch( const QString& name, GenreInformation information = GenreInformation(), int numResults = 0, int start = -1 ); static Artists parseArtists( QNetworkReply* ) throw( ParseError ); static Genres parseSimilar( QNetworkReply* ) throw( ParseError ); static Genres parseProfile( QNetworkReply* ) throw( ParseError ); static Genres parseList( QNetworkReply* ) throw( ParseError ); static Genres parseSearch( QNetworkReply* ) throw( ParseError ); private: static QUrl setupStaticQuery( const QByteArray& methodName, int numResults = 0, int start = -1 ); QUrl setupQuery( const QByteArray& methodName, int numResults = 0, int start = -1 ) const; static void addQueryInformation( QUrl& url, GenreInformation information ); QSharedDataPointer d; }; ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Genre& genre); } // namespace Q_DECLARE_METATYPE( Echonest::Genre ) Q_DECLARE_METATYPE( Echonest::Genres ); #endif libechonest-2.3.1/tests/TrackTest.cpp000644 001750 001750 00000022771 12465467260 020415 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * Copyright (c) 2011 Jeff Mitchell * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "TrackTest.h" #include #include #include void TrackTest::initTestCase() { Echonest::Config::instance()->setAPIKey( "JGJCRKWLXLBZIFAZB" ); s_mainThread = QThread::currentThread(); } void TrackTest::testUploadLocalFile() { QFile f( QString::fromLatin1( DATA_DIR "/01 - Cellule.mp3" ) ); QVERIFY( f.exists() ); QVERIFY( f.open( QIODevice::ReadOnly ) ); QByteArray data = f.readAll(); QVERIFY( !data.isEmpty() ); QUrl path( f.fileName() ); QNetworkReply* reply = Echonest::Track::uploadLocalFile( path, data, true ); qDebug() << "Uploading.."; QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); qDebug() << "Verifying uploaded."; QVERIFY( reply->error() == QNetworkReply::NoError ); Echonest::Track track = Echonest::Track::parseProfile( reply ); verifyTrack1( track ); } void TrackTest::testProfileFromMD5() { QByteArray md5 = "2aceceb60f38e24c3e41365bf26fc7db"; QNetworkReply* reply = Echonest::Track::profileFromMD5( md5 ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVERIFY( reply->error() == QNetworkReply::NoError ); Echonest::Track track = Echonest::Track::parseProfile( reply ); verifyTrack1( track ); } void TrackTest::testProfileFromId() { QByteArray id = "TRMEQQH12B048DE985"; QNetworkReply* reply = Echonest::Track::profileFromTrackId( id ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVERIFY( reply->error() == QNetworkReply::NoError ); Echonest::Track track = Echonest::Track::parseProfile( reply ); verifyTrack1( track ); } void TrackTest::testAnalyzeFromMD5() { QByteArray md5 = "d41baf84c0d507bf7f861b7820844e05"; QNetworkReply* reply = Echonest::Track::analyzeTrackMD5( md5, true ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVERIFY( reply->error() == QNetworkReply::NoError ); try { Echonest::Track track = Echonest::Track::parseProfile( reply ); verifyTrack2( track ); } catch( const Echonest::ParseError& e ) { } } void TrackTest::testAnalyzerFromId() { QByteArray id = "TROICNF12B048DE990"; QNetworkReply* reply = Echonest::Track::analyzeTrackId( id, true ); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); QVERIFY( reply->error() == QNetworkReply::NoError ); Echonest::Track track = Echonest::Track::parseProfile( reply ); verifyTrack2( track ); } void TrackTest::testThreads() { QSet< QThread* > threadSet; for ( int i = 0; i < 4; ++i ) { QThread *newThread = new QThread(); threadSet.insert( newThread ); TrackTestThreadObject *newTestObject = new TrackTestThreadObject(); newTestObject->moveToThread( newThread ); newThread->start(); QMetaObject::invokeMethod( newTestObject, "go" ); } while ( !threadSet.isEmpty() ) { Q_FOREACH( QThread* thread, threadSet.values() ) { if ( thread->isRunning() ) QCoreApplication::processEvents( QEventLoop::AllEvents, 200 ); else threadSet.remove( thread ); } } } void TrackTest::verifyTrack1(const Echonest::Track& track) { QVERIFY( track.status() == Echonest::Analysis::Complete ); // Echo Nest doesn't have track info for this Jamendo track QVERIFY( track.artist().isEmpty() ); QVERIFY( track.release().isEmpty() ); QVERIFY( track.title().isEmpty() ); QVERIFY( track.analyzerVersion() == QLatin1String( "3.01a" ) ); QVERIFY( track.id() == "TRMEQQH12B048DE985" ); QVERIFY( track.bitrate() == 160 ); QVERIFY( track.audioMD5() == "2ae41a12e469ba388791b82c11338932" ); qDebug() << "md5:" << track.md5(); QVERIFY( track.md5() == "2aceceb60f38e24c3e41365bf26fc7db" ); QVERIFY( track.samplerate() == 44100 ); // AudioSummary QVERIFY( track.audioSummary().key() == 1 ); QVERIFY( track.audioSummary().tempo() == 160.003 ); QVERIFY( track.audioSummary().mode() == 0 ); QVERIFY( track.audioSummary().timeSignature() == 4 ); QVERIFY( track.audioSummary().duration() == 220.86485 ); QVERIFY( track.audioSummary().loudness() == -11.839 ); QVERIFY( track.audioSummary().danceability() > 0 ); QVERIFY( track.audioSummary().energy() > 0 ); // Detailed audiosummary QNetworkReply* reply =track.audioSummary().fetchFullAnalysis(); QEventLoop loop; loop.connect( reply, SIGNAL(finished()), SLOT(quit()) ); loop.exec(); Echonest::AudioSummary summary = track.audioSummary(); summary.parseFullAnalysis( reply ); qDebug() << "track detailed summary num segments:" << summary.segments().size(); qDebug() << "track detailed summary num bars:" << summary.bars().size(); qDebug() << "track detailed summary num beats:" << summary.beats().size(); qDebug() << "track detailed summary num sections:" << summary.sections().size(); qDebug() << "track detailed summary num tatums:" << summary.tatums().size(); // qDebug() << "track detailed analysis_time:" << summary.analysisTime(); // qDebug() << "track detailed analyzer_version:" << summary.analyzerVersion(); // qDebug() << "track detailed detailed_status:" << summary.detailedStatus(); // qDebug() << "track detailed status:" << summary.analysisStatus(); // qDebug() << "track detailed timestamp:" << summary.timestamp(); // qDebug() << "track detailed end_of_fade_in:" << summary.endOfFadeIn(); // qDebug() << "track detailed key_confidence:" << summary.keyConfidence(); // qDebug() << "track detailed mode_confidence:" << summary.modeConfidence(); // qDebug() << "track detailed num_samples:" << summary.numSamples(); // qDebug() << "track detailed sample_md5:" << summary.sampleMD5(); // qDebug() << "track detailed start_of_fade_out:" << summary.startOfFadeOut(); // qDebug() << "track detailed tempo_confidence:" << summary.tempoConfidence(); // qDebug() << "track detailed time_signature_confidence:" << summary.timeSignatureConfidence(); QVERIFY( summary.analysisTime() ); QVERIFY( !summary.analyzerVersion().isEmpty() ); QVERIFY( !summary.detailedStatus().isEmpty() ); QCOMPARE( summary.analysisStatus(), 0 ); QVERIFY( summary.timestamp() ); QVERIFY( summary.endOfFadeIn() ); QVERIFY( summary.keyConfidence() > -1 ); QVERIFY( summary.modeConfidence() > -1 ); QVERIFY( summary.numSamples() ); QVERIFY( !summary.sampleMD5().isEmpty() ); QVERIFY( summary.startOfFadeOut() ); QVERIFY( summary.tempoConfidence() > -1 ); QVERIFY( summary.timeSignatureConfidence() > -1 ); QVERIFY( summary.segments().size() ); QVERIFY( summary.bars().size() ); QVERIFY( summary.beats().size() ); QVERIFY( summary.sections().size() ); QVERIFY( summary.tatums().size() ); } void TrackTest::verifyTrack2(const Echonest::Track& track) { QVERIFY( track.status() == Echonest::Analysis::Complete ); // Echo Nest doesn't have track info for this Jamendo track QVERIFY( track.artist().isEmpty() ); QVERIFY( track.release().isEmpty() ); QVERIFY( track.title().isEmpty() ); QVERIFY( track.analyzerVersion() == QLatin1String( "3.01a" ) ); QVERIFY( track.id() == "TROICNF12B048DE990" ); QVERIFY( track.bitrate() == 160 ); QVERIFY( track.audioMD5() == "360e25b8f205cb5c730b2b73562f5ebd" ); qDebug() << "md5:" << track.md5(); QVERIFY( track.md5() == "d41baf84c0d507bf7f861b7820844e05" ); QVERIFY( track.samplerate() == 44100 ); // AudioSummary QVERIFY( track.audioSummary().key() == 1 ); QVERIFY( track.audioSummary().tempo() == 135.032 ); QVERIFY( track.audioSummary().mode() == 1 ); QVERIFY( track.audioSummary().timeSignature() == 4 ); QVERIFY( track.audioSummary().duration() == 231.65342 ); QVERIFY( track.audioSummary().loudness() == -13.473 ); } QTEST_MAIN(TrackTest) #include "TrackTest.moc" libechonest-2.3.1/src/ArtistTypes_p.h000644 001750 001750 00000010575 12465467260 020414 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #ifndef ECHONEST_ARTISTTYPES_P_H #define ECHONEST_ARTISTTYPES_P_H #include "Util.h" #include #include #include #include #include class AudioFileData : public QSharedData { public: AudioFileData() {} AudioFileData( const AudioFileData& other ) : QSharedData( other ) { title = other.title; artist = other.artist; url = other.url; length = other.length; link = other.link; date = other.date; id = other.id; release = other.release; } QString title; QString artist; QUrl url; qreal length; QUrl link; QDateTime date; QString release; QByteArray id; }; class BiographyData : public QSharedData { public: BiographyData() {} BiographyData( const BiographyData& other ) : QSharedData( other ) { url = other.url; text = other.text; site = other.site; license = other.license; } QUrl url; QString text; QString site; Echonest::License license; }; class BlogData : public QSharedData { public: BlogData() {} BlogData( const BlogData& other ) : QSharedData( other ) { name = other.name; url = other.url; date_posted = other.date_posted; date_found = other.date_found; summary = other.summary; id = other.id; } QString name; QUrl url; QDateTime date_posted; QDateTime date_found; QString summary; QByteArray id; }; class ArtistImageData : public QSharedData { public: ArtistImageData() {} ArtistImageData( const ArtistImageData& other ) : QSharedData( other ) { url = other.url; license = other.license; } QUrl url; Echonest::License license; }; class ReviewData : public QSharedData { public: ReviewData() {} ReviewData( const ReviewData& other ) : QSharedData( other ) { name = other.name; url = other.url; summary = other.summary; date_reviewed = other.date_reviewed; date_found = other.date_found; image_url = other.image_url; release = other.release; id = other.id; } QString name; QUrl url; QString summary; QDateTime date_reviewed; QDateTime date_found; QUrl image_url; QString release; QByteArray id; }; class TermData : public QSharedData { public: TermData() {} TermData( const TermData& other ) : QSharedData( other ) { name = other.name; frequency = other.frequency; weight = other.weight; } QString name; qreal frequency; qreal weight; }; class VideoData : public QSharedData { public: VideoData() {} VideoData( const VideoData& other ) : QSharedData( other ) { title = other.title; url = other.url; site = other.site; date_found = other.date_found; image_url = other.image_url; id = other.id; } QString title; QUrl url; QString site; QDateTime date_found; QUrl image_url; QByteArray id; }; #endif libechonest-2.3.1/src/Util.cpp000644 001750 001750 00000013054 12465467260 017045 0ustar00stefanstefan000000 000000 /**************************************************************************************** * Copyright (c) 2010-2012 Leo Franchi * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #include "Util.h" #include #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) #include #endif QByteArray Echonest::escapeSpacesAndPluses(const QString& in) { // Echonest wants " " treated as "+", so we force QUrl to encode spaces as +es rather than %20 QByteArray escaped = QUrl::toPercentEncoding( in, " " ); escaped.replace( " ", "+" ); return escaped; } Echonest::Analysis::AnalysisStatus Echonest::statusToEnum(const QString& status) { if( status == QLatin1String("unknown") ) { return Echonest::Analysis::Unknown; } else if( status == QLatin1String("pending") ) { return Echonest::Analysis::Pending; } else if( status == QLatin1String("complete") ) { return Echonest::Analysis::Complete; } else if( status == QLatin1String("error" )) { return Echonest::Analysis::Error; } return Echonest::Analysis::Unknown; } QString Echonest::statusToString(Echonest::Analysis::AnalysisStatus status) { switch( status ) { case Echonest::Analysis::Unknown: return QLatin1String( "unknown" ); case Echonest::Analysis::Pending: return QLatin1String( "pending" ); case Echonest::Analysis::Complete: return QLatin1String( "complete" ); case Echonest::Analysis::Error: return QLatin1String( "error" ); } return QString(); } QByteArray Echonest::catalogTypeToLiteral(Echonest::CatalogTypes::Type type) { switch( type ) { case Echonest::CatalogTypes::Artist: return "artist"; case Echonest::CatalogTypes::Song: return "song"; default: return ""; } } Echonest::CatalogTypes::Type Echonest::literalToCatalogType( const QByteArray& type ) { if( type == "artist" ) return Echonest::CatalogTypes::Artist; else if( type == "song" ) return Echonest::CatalogTypes::Song; else return Echonest::CatalogTypes::Artist; } QByteArray Echonest::catalogStatusToLiteral(Echonest::CatalogTypes::TicketStatus status) { return statusToString( static_cast( status ) ).toLatin1(); } Echonest::CatalogTypes::TicketStatus Echonest::literalToCatalogStatus(const QByteArray& type) { return static_cast( statusToEnum( QLatin1String( type ) ) ); } /** * Delete, U pda*te, Play, Skip */ QByteArray Echonest::catalogUpdateActionToLiteral(Echonest::CatalogTypes::Action action) { switch( action ) { case Echonest::CatalogTypes::Delete: return "delete"; case Echonest::CatalogTypes::Play: return "play"; case Echonest::CatalogTypes::Update: return "update"; case Echonest::CatalogTypes::Skip: return "skip"; default: return ""; } } Echonest::CatalogTypes::Action Echonest::literalToCatalogUpdateAction(const QByteArray& type) { if( type == "delete" ) return Echonest::CatalogTypes::Delete; else if( type == "play" ) return Echonest::CatalogTypes::Play; else if( type == "update" ) return Echonest::CatalogTypes::Update; else if( type == "skip" ) return Echonest::CatalogTypes::Skip; return Echonest::CatalogTypes::Update; } QDebug Echonest::operator<<(QDebug d, const Echonest::ForeignId& id) { return d.maybeSpace() << QString::fromLatin1( "Foreign Id(%1, %2)" ).arg( id.catalog ).arg( id.foreign_id ); } QDebug Echonest::operator<<(QDebug d, const Echonest::ArtistLocation& loc) { return d.maybeSpace() << QString::fromLatin1( "Artist Location(%1, %2, %3)" ).arg( loc.location ).arg( loc.latitude ).arg( loc.longitude ); } void Echonest::urlAddQueryItem(QUrl& url, const QString& key, const QString& value) { #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) QUrlQuery urlQuery( url ); urlQuery.addQueryItem( key, value ); url.setQuery( urlQuery ); #else url.addQueryItem( key, value ); #endif } void Echonest::urlRemoveQueryItem(QUrl& url, const QString& key ) { #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) QUrlQuery urlQuery( url ); urlQuery.removeQueryItem( key ); url.setQuery( urlQuery ); #else url.removeEncodedQueryItem( key.toLatin1() ); #endif }