From d035b1abc2d21fd24ef95edd095ec3a487dbb117 Mon Sep 17 00:00:00 2001 From: David Sansome Date: Sat, 2 Oct 2010 16:23:33 +0000 Subject: [PATCH] Add libechonest into 3rdparty, and add a basic echonest artist info fetcher. --- 3rdparty/libechonest/Artist.cpp | 545 ++++++++++++++++++++ 3rdparty/libechonest/Artist.h | 370 +++++++++++++ 3rdparty/libechonest/ArtistTypes.cpp | 572 ++++++++++++++++++++ 3rdparty/libechonest/ArtistTypes.h | 390 ++++++++++++++ 3rdparty/libechonest/ArtistTypes_p.h | 166 ++++++ 3rdparty/libechonest/Artist_p.h | 71 +++ 3rdparty/libechonest/AudioSummary.cpp | 314 +++++++++++ 3rdparty/libechonest/AudioSummary.h | 227 ++++++++ 3rdparty/libechonest/AudioSummary_p.h | 100 ++++ 3rdparty/libechonest/CMakeLists.txt | 58 +++ 3rdparty/libechonest/Config.cpp | 127 +++++ 3rdparty/libechonest/Config.h | 115 +++++ 3rdparty/libechonest/Parsing.cpp | 601 ++++++++++++++++++++++ 3rdparty/libechonest/Parsing_p.h | 87 ++++ 3rdparty/libechonest/Playlist.cpp | 315 ++++++++++++ 3rdparty/libechonest/Playlist.h | 200 +++++++ 3rdparty/libechonest/Playlist_p.h | 34 ++ 3rdparty/libechonest/Song.cpp | 311 +++++++++++ 3rdparty/libechonest/Song.h | 217 ++++++++ 3rdparty/libechonest/Song_p.h | 66 +++ 3rdparty/libechonest/Track.cpp | 247 +++++++++ 3rdparty/libechonest/Track.h | 185 +++++++ 3rdparty/libechonest/Track_p.h | 59 +++ 3rdparty/libechonest/Util.cpp | 49 ++ 3rdparty/libechonest/Util.h | 80 +++ 3rdparty/libechonest/echonest_export.h | 28 + 3rdparty/libechonest/libechonest_export.h | 0 CMakeLists.txt | 3 + data/data.qrc | 1 + data/songinfo.css | 8 + src/CMakeLists.txt | 12 + src/main.cpp | 5 + src/songinfo/artistinfofetcher.cpp | 39 ++ src/songinfo/artistinfofetcher.h | 46 ++ src/songinfo/artistinfoprovider.cpp | 22 + src/songinfo/artistinfoprovider.h | 36 ++ src/songinfo/artistinfoview.cpp | 95 ++++ src/songinfo/artistinfoview.h | 54 ++ src/songinfo/collapsibleinfopane.cpp | 99 ++++ src/songinfo/collapsibleinfopane.h | 47 ++ src/songinfo/echonestartistinfo.cpp | 89 ++++ src/songinfo/echonestartistinfo.h | 49 ++ src/translations/ar.po | 4 + src/translations/bg.po | 4 + src/translations/ca.po | 4 + src/translations/cs.po | 4 + src/translations/da.po | 4 + src/translations/de.po | 4 + src/translations/el.po | 4 + src/translations/en_CA.po | 4 + src/translations/en_GB.po | 4 + src/translations/es.po | 4 + src/translations/fi.po | 4 + src/translations/fr.po | 4 + src/translations/gl.po | 4 + src/translations/hu.po | 4 + src/translations/it.po | 4 + src/translations/kk.po | 4 + src/translations/lt.po | 4 + src/translations/nb.po | 4 + src/translations/nl.po | 4 + src/translations/oc.po | 4 + src/translations/pl.po | 4 + src/translations/pt.po | 4 + src/translations/pt_BR.po | 4 + src/translations/ro.po | 4 + src/translations/ru.po | 4 + src/translations/sk.po | 4 + src/translations/sl.po | 4 + src/translations/sr.po | 4 + src/translations/sv.po | 4 + src/translations/tr.po | 4 + src/translations/translations.pot | 4 + src/translations/uk.po | 4 + src/translations/zh_CN.po | 4 + src/translations/zh_TW.po | 4 + src/ui/mainwindow.cpp | 21 +- src/ui/mainwindow.h | 3 +- 78 files changed, 6287 insertions(+), 12 deletions(-) create mode 100644 3rdparty/libechonest/Artist.cpp create mode 100644 3rdparty/libechonest/Artist.h create mode 100644 3rdparty/libechonest/ArtistTypes.cpp create mode 100644 3rdparty/libechonest/ArtistTypes.h create mode 100644 3rdparty/libechonest/ArtistTypes_p.h create mode 100644 3rdparty/libechonest/Artist_p.h create mode 100644 3rdparty/libechonest/AudioSummary.cpp create mode 100644 3rdparty/libechonest/AudioSummary.h create mode 100644 3rdparty/libechonest/AudioSummary_p.h create mode 100644 3rdparty/libechonest/CMakeLists.txt create mode 100644 3rdparty/libechonest/Config.cpp create mode 100644 3rdparty/libechonest/Config.h create mode 100644 3rdparty/libechonest/Parsing.cpp create mode 100644 3rdparty/libechonest/Parsing_p.h create mode 100644 3rdparty/libechonest/Playlist.cpp create mode 100644 3rdparty/libechonest/Playlist.h create mode 100644 3rdparty/libechonest/Playlist_p.h create mode 100644 3rdparty/libechonest/Song.cpp create mode 100644 3rdparty/libechonest/Song.h create mode 100644 3rdparty/libechonest/Song_p.h create mode 100644 3rdparty/libechonest/Track.cpp create mode 100644 3rdparty/libechonest/Track.h create mode 100644 3rdparty/libechonest/Track_p.h create mode 100644 3rdparty/libechonest/Util.cpp create mode 100644 3rdparty/libechonest/Util.h create mode 100644 3rdparty/libechonest/echonest_export.h create mode 100644 3rdparty/libechonest/libechonest_export.h create mode 100644 data/songinfo.css create mode 100644 src/songinfo/artistinfofetcher.cpp create mode 100644 src/songinfo/artistinfofetcher.h create mode 100644 src/songinfo/artistinfoprovider.cpp create mode 100644 src/songinfo/artistinfoprovider.h create mode 100644 src/songinfo/artistinfoview.cpp create mode 100644 src/songinfo/artistinfoview.h create mode 100644 src/songinfo/collapsibleinfopane.cpp create mode 100644 src/songinfo/collapsibleinfopane.h create mode 100644 src/songinfo/echonestartistinfo.cpp create mode 100644 src/songinfo/echonestartistinfo.h diff --git a/3rdparty/libechonest/Artist.cpp b/3rdparty/libechonest/Artist.cpp new file mode 100644 index 000000000..5044f30d5 --- /dev/null +++ b/3rdparty/libechonest/Artist.cpp @@ -0,0 +1,545 @@ +/**************************************************************************************** + * Copyright (c) 2010 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" + +Echonest::Artist::Artist() + : d( new ArtistData ) +{ +} + +Echonest::Artist::Artist( const QByteArray& id, const QString& name ) + : d( new ArtistData ) +{ + d->id = id; + d->name = name; +} + +Echonest::Artist::Artist(const Echonest::Artist& other) + : d( other.d ) +{} + +Echonest::Artist& Echonest::Artist::operator=(const Echonest::Artist& artist) +{ + d = artist.d; + return *this; +} + +Echonest::Artist::~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; +} + +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; +} + + +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() ) + url.addQueryItem( 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 + url.addEncodedQueryItem( "high_relevance", "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" ) ) + url.addQueryItem( 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() ) + url.addQueryItem( QLatin1String( "license" ), license ); + + return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); +} + +QNetworkReply* Echonest::Artist::fetchProfile(Echonest::Artist::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 + url.addEncodedQueryItem( "high_relevance", "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::Artist::ArtistInformation information, int numResults, int offset ) +{ + QUrl url = Echonest::baseGetQuery( "artist", "similar" ); + addQueryInformation( url, information ); + + if( numResults > 0 ) + url.addEncodedQueryItem( "results", QByteArray::number( numResults ) ); + if( offset >= 0 ) + url.addEncodedQueryItem( "start", QByteArray::number( offset ) ); + + Echonest::Artist::SearchParams::const_iterator iter = params.constBegin(); + for( ; iter < params.constEnd(); ++iter ) + url.addQueryItem( QLatin1String( searchParamToString( iter->first ) ), iter->second.toString().replace( QLatin1Char( ' ' ), QLatin1Char( '+' ) ) ); + + return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); +} + +QNetworkReply* Echonest::Artist::fetchSongs( Echonest::Artist::ArtistInformation idspace, bool limitToIdSpace, int numResults, int offset ) const +{ + QUrl url = setupQuery( "songs", numResults, offset ); + addQueryInformation( url, idspace ); + if( limitToIdSpace ) + url.addEncodedQueryItem( "limit", "true" ); + + 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 ) + url.addEncodedQueryItem( "sort", "weight" ); + else if( sorting == Echonest::Artist::Frequency ) + url.addEncodedQueryItem( "sort", "frequency" ); + + 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::search(const Echonest::Artist::SearchParams& params, Echonest::Artist::ArtistInformation information, bool limit) +{ + QUrl url = Echonest::baseGetQuery( "artist", "search" ); + + Echonest::Artist::SearchParams::const_iterator iter = params.constBegin(); + for( ; iter < params.constEnd(); ++iter ) + url.addQueryItem( QLatin1String( searchParamToString( iter->first ) ), iter->second.toString().replace( QLatin1Char( ' ' ), QLatin1Char( '+' ) ) ); + url.addEncodedQueryItem( "limit", limit ? "true" : "false" ); + addQueryInformation( url, information ); + + return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); +} + +QNetworkReply* Echonest::Artist::topHottt(Echonest::Artist::ArtistInformation information, int numResults, int offset, bool limit) +{ + QUrl url = Echonest::baseGetQuery( "artist", "top_hottt" ); + addQueryInformation( url, information ); + + if( numResults > 0 ) + url.addEncodedQueryItem( "results", QByteArray::number( numResults ) ); + if( offset >= 0 ) + url.addEncodedQueryItem( "start", QByteArray::number( offset ) ); + + url.addEncodedQueryItem( "limit", limit ? "true" : "false" ); + + return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); +} + +QNetworkReply* Echonest::Artist::topTerms(int numResults) +{ + QUrl url = Echonest::baseGetQuery( "artist", "top_terms" ); + url.addEncodedQueryItem( "results", QByteArray::number( numResults ) ); + + 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 ); + + return numResults; +} + +Echonest::Artists Echonest::Artist::parseSearch( QNetworkReply* reply ) throw( Echonest::ParseError ) +{ + Echonest::Parser::checkForErrors( reply ); + + QXmlStreamReader xml( reply->readAll() ); + + Echonest::Parser::readStatus( xml ); + + Echonest::Artists artists = Echonest::Parser::parseArtists( xml ); + + 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::parseTermList( xml ); + + return terms; +} + + +QUrl Echonest::Artist::setupQuery( const QByteArray& methodName, int numResults, int start ) const +{ + QUrl url = Echonest::baseGetQuery( "artist", methodName ); + if( !d->id.isEmpty() ) + url.addEncodedQueryItem( "id", d->id ); + else if( !d->name.isEmpty() ) { + QString name = d->name; + name.replace( QLatin1Char( ' ' ), QLatin1Char( '+' ) ); + url.addQueryItem( QLatin1String( "name" ), name ); + } else { + qWarning() << "Artist method" << methodName << "called on an artist object without name or id!"; + return QUrl(); + } + if( numResults > 0 ) + url.addEncodedQueryItem( "results", QByteArray::number( numResults ) ); + if( start >= 0 ) + url.addEncodedQueryItem( "start", QByteArray::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"; + default: + return ""; + } +} + +void Echonest::Artist::addQueryInformation(QUrl& url, Echonest::Artist::ArtistInformation parts) +{ + if( parts.testFlag( Echonest::Artist::Audio ) ) + url.addEncodedQueryItem( "bucket", "audio" ); + if( parts.testFlag( Echonest::Artist::Biographies ) ) + url.addEncodedQueryItem( "bucket", "biographies" ); + if( parts.testFlag( Echonest::Artist::Blogs ) ) + url.addEncodedQueryItem( "bucket", "blogs" ); + if( parts.testFlag( Echonest::Artist::Familiarity ) ) + url.addEncodedQueryItem( "bucket", "familiarity" ); + if( parts.testFlag( Echonest::Artist::Hotttnesss ) ) + url.addEncodedQueryItem( "bucket", "hotttnesss" ); + if( parts.testFlag( Echonest::Artist::Images ) ) + url.addEncodedQueryItem( "bucket", "images" ); + if( parts.testFlag( Echonest::Artist::News ) ) + url.addEncodedQueryItem( "bucket", "news" ); + if( parts.testFlag( Echonest::Artist::Reviews ) ) + url.addEncodedQueryItem( "bucket", "reviews" ); + if( parts.testFlag( Echonest::Artist::Terms ) ) + url.addEncodedQueryItem( "bucket", "terms" ); + if( parts.testFlag( Echonest::Artist::Urls ) ) + url.addEncodedQueryItem( "bucket", "urls" ); + if( parts.testFlag( Echonest::Artist::Videos ) ) + url.addEncodedQueryItem( "bucket", "video" ); + if( parts.testFlag( Echonest::Artist::MusicBrainzEntries ) ) + url.addEncodedQueryItem( "bucket", "id:musicbrainz" ); + if( parts.testFlag( Echonest::Artist::SevenDigitalEntries ) ) + url.addEncodedQueryItem( "bucket", "id:7digital" ); + if( parts.testFlag( Echonest::Artist::PlaymeEntries ) ) + url.addEncodedQueryItem( "bucket", "id:playme" ); +} + + +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()) ); +} + diff --git a/3rdparty/libechonest/Artist.h b/3rdparty/libechonest/Artist.h new file mode 100644 index 000000000..9abaccb4d --- /dev/null +++ b/3rdparty/libechonest/Artist.h @@ -0,0 +1,370 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 "echonest_export.h" + +#include +#include +#include +#include "Config.h" +#include "ArtistTypes.h" +#include "Song.h" + +class QNetworkReply; +class ArtistData; +class Term; +namespace Echonest{ + + class Biography; + + class Artist; + typedef QVector< Artist > Artists; + + /** + * 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 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, + MusicBrainzEntries = 0x0800, + SevenDigitalEntries = 0x1000, + PlaymeEntries = 0x2000 + }; + Q_DECLARE_FLAGS( ArtistInformation, ArtistInformationFlag ) + + 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 + }; + typedef QPair< Echonest::Artist::SearchParam, QVariant > SearchParamEntry; + typedef QVector< SearchParamEntry > SearchParams; + + Artist(); + Artist( const QByteArray& id, const QString& name ); + Artist( const Artist& other ); + Artist& operator=( const Artist& artist ); + ~Artist(); + + 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. + * + */ + + /** + * 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& ); + + /** + * 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& ); + + /** + * 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 ) 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. + * + * The idspace can be used to specify what idspace to return the results in, if none is specifed, The Echo Nest song identifiers + * are used. If limitToIdSpace is true, then only results in the requested idspace are returned. + */ + QNetworkReply* fetchSongs( ArtistInformation idspace = NoInformation, bool limitToIdSpace = false, 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 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 = NoInformation, 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 = NoInformation, 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 = NoInformation, int numResults = 0, int offset = -1, bool limit = false ); + + /** + * Fetch a list of the top overall terms. + */ + static QNetworkReply* topTerms( int numResults = 15 ); + + /** + * 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 ); + + private: + QUrl setupQuery( const QByteArray& methodName, int numResults = 0, int start = -1 ) const; + + static QByteArray searchParamToString( SearchParam param ); + static void addQueryInformation( QUrl& url, ArtistInformation parts ); + + QSharedDataPointer d; + }; + + ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Artist& artist); + + Q_DECLARE_OPERATORS_FOR_FLAGS(Artist::ArtistInformation) +} // namespace +#endif diff --git a/3rdparty/libechonest/ArtistTypes.cpp b/3rdparty/libechonest/ArtistTypes.cpp new file mode 100644 index 000000000..43ff794a1 --- /dev/null +++ b/3rdparty/libechonest/ArtistTypes.cpp @@ -0,0 +1,572 @@ +/**************************************************************************************** + * Copyright (c) 2010 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() ); +} diff --git a/3rdparty/libechonest/ArtistTypes.h b/3rdparty/libechonest/ArtistTypes.h new file mode 100644 index 000000000..420604905 --- /dev/null +++ b/3rdparty/libechonest/ArtistTypes.h @@ -0,0 +1,390 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 diff --git a/3rdparty/libechonest/ArtistTypes_p.h b/3rdparty/libechonest/ArtistTypes_p.h new file mode 100644 index 000000000..e15e27969 --- /dev/null +++ b/3rdparty/libechonest/ArtistTypes_p.h @@ -0,0 +1,166 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 ) { + 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 ) { + 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 ) { + 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 ) { + url = other.url; + license = other.license; + } + + QUrl url; + Echonest::License license; +}; + +class ReviewData : public QSharedData +{ +public: + ReviewData() {} + ReviewData( const ReviewData& 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 ) { + 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 ) { + 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 diff --git a/3rdparty/libechonest/Artist_p.h b/3rdparty/libechonest/Artist_p.h new file mode 100644 index 000000000..ab5d13e5f --- /dev/null +++ b/3rdparty/libechonest/Artist_p.h @@ -0,0 +1,71 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +#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) + { + id = other.id; + name = other.name; + } + + // 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; + + QUrl lastfm_url; + QUrl aolmusic_url; + QUrl myspace_url; + QUrl amazon_url; + QUrl itunes_url; + QUrl mb_url; +}; + +#endif + \ No newline at end of file diff --git a/3rdparty/libechonest/AudioSummary.cpp b/3rdparty/libechonest/AudioSummary.cpp new file mode 100644 index 000000000..b9fb9d651 --- /dev/null +++ b/3rdparty/libechonest/AudioSummary.cpp @@ -0,0 +1,314 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +#include "Config.h" + +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; +} + +Echonest::Analysis::AnalysisStatus Echonest::AudioSummary::detailedStatus() const +{ + return Echonest::statusToEnum( d->detailed_status ); +} + +void Echonest::AudioSummary::setDetailedStatus(Echonest::Analysis::AnalysisStatus status) +{ + d->detailed_status = Echonest::statusToString( 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) +{ + +} + +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 QString& analysisUrl) +{ + d->analysis_url = analysisUrl; +} + +void Echonest::AudioSummary::setMode(int mode) +{ + d->mode = mode; +} diff --git a/3rdparty/libechonest/AudioSummary.h b/3rdparty/libechonest/AudioSummary.h new file mode 100644 index 000000000..dc52e5291 --- /dev/null +++ b/3rdparty/libechonest/AudioSummary.h @@ -0,0 +1,227 @@ +/**************************************************************************************** + * Copyright (c) 2010 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" + +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 ); + + /** + * 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 ); + + /// 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 + */ + Analysis::AnalysisStatus detailedStatus() const; + void setDetailedStatus( Analysis::AnalysisStatus 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 QString& analysisUrl ); + + private: + QSharedDataPointer d; + }; + + ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::AudioSummary& summary); + + +} // namespace +#endif diff --git a/3rdparty/libechonest/AudioSummary_p.h b/3rdparty/libechonest/AudioSummary_p.h new file mode 100644 index 000000000..99c6ca4ea --- /dev/null +++ b/3rdparty/libechonest/AudioSummary_p.h @@ -0,0 +1,100 @@ +/**************************************************************************************** + * Copyright (c) 2010 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() {} + AudioSummaryData(const AudioSummaryData& other) + { + key = other.key; + tempo = other.tempo; + mode = other.mode; + time_signature = other.time_signature; + duration = other.duration; + loudness = other.loudness; + samplerate = other.samplerate; + + 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; + + 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; + +}; diff --git a/3rdparty/libechonest/CMakeLists.txt b/3rdparty/libechonest/CMakeLists.txt new file mode 100644 index 000000000..210959eba --- /dev/null +++ b/3rdparty/libechonest/CMakeLists.txt @@ -0,0 +1,58 @@ + +add_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII) + +set(ECHONEST_LIB_MAJOR_VERSION "0") +set(ECHONEST_LIB_MINOR_VERSION "1") +set(ECHONEST_LIB_PATCH_VERSION "0") +set(ECHONEST_LIB_VERSION "${ECHONEST_LIB_MAJOR_VERSION}.${ECHONEST_LIB_MINOR_VERSION}.${ECHONEST_LIB_PATCH_VERSION}" ) + + +set( ECHONEST_LIB_VERSION_STRING "${ECHONEST_LIB_MAJOR_VERSION}.${ECHONEST_LIB_MINOR_VERSION}.${ECHONEST_LIB_PATCH_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 -Werror ) + add_definitions( -fvisibility=hidden ) +# 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) + +include_directories( ${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 +) + +set( LIBECHONEST_H + Track.h + Song.h + Artist.h + Playlist.h + Config.h + AudioSummary.h + ArtistTypes.h +) + +QT4_WRAP_CPP( ${LIBECHONEST_H} ) + +add_library( echonest STATIC ${LIBECHONEST_SRC} ) +target_link_libraries( echonest ${QT_QTCORE_LIBRARY} ${QT_QTNETWORK_LIBRARY} ) +set_target_properties( echonest PROPERTIES VERSION ${ECHONEST_LIB_VERSION} SOVERSION ${ECHONEST_LIB_VERSION} ) + +# Clementine: "install" the headers into the binary directory +foreach(file ${LIBECHONEST_H} echonest_export.h Util.h) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${file} + ${CMAKE_CURRENT_BINARY_DIR}/echonest/${file} + COPYONLY) +endforeach(file ${LIBECHONEST_H}) diff --git a/3rdparty/libechonest/Config.cpp b/3rdparty/libechonest/Config.cpp new file mode 100644 index 000000000..bdc558be2 --- /dev/null +++ b/3rdparty/libechonest/Config.cpp @@ -0,0 +1,127 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 "Config.h" + +#include +#include + +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 ) ) ); + url.addEncodedQueryItem( "api_key", Echonest::Config::instance()->apiKey() ); + url.addEncodedQueryItem( "format", "xml" ); + return url; +} + +Echonest::ParseError::ParseError(Echonest::ErrorType error): exception() +{ + type = error; +} + +Echonest::ParseError::~ParseError() throw() +{} + +Echonest::ErrorType Echonest::ParseError::errorType() const +{ + return type; +} + +void Echonest::ParseError::setNetworkError( QNetworkReply::NetworkError error ) +{ + nError = error; +} + +QNetworkReply::NetworkError Echonest::ParseError::networkError() const +{ + return nError; +} + + + +class Echonest::ConfigPrivate { +public: + ConfigPrivate() : nam( new QNetworkAccessManager ) + { + } + + ~ConfigPrivate() + { + delete nam; + nam = 0; + } + + QNetworkAccessManager* nam; + 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; + + if( d->nam ) { + delete d->nam; + } + d->nam = nam; +} + +QNetworkAccessManager* Echonest::Config::nam() const +{ + return d->nam; +} + +Echonest::Config* Echonest::Config::instance() { + if ( !s_instance ) { + s_instance = new Config; + } + + return s_instance; +} diff --git a/3rdparty/libechonest/Config.h b/3rdparty/libechonest/Config.h new file mode 100644 index 000000000..eb80a6e9c --- /dev/null +++ b/3rdparty/libechonest/Config.h @@ -0,0 +1,115 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 ParseError : public std::exception + { + public: + ParseError( ErrorType error ); + virtual ~ParseError() throw(); + + ErrorType errorType() const; + + /** + * If the ErrorType is NetworkError, this value contains the QNetworkReply + * error code that was returned. + */ + void setNetworkError( QNetworkReply::NetworkError error ); + QNetworkReply::NetworkError networkError() const; + + private: + ErrorType type; + 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 take over control of the object. + */ + void setNetworkAccessManager( QNetworkAccessManager* nam ); + QNetworkAccessManager* nam() const; + + + private: + Config(); + ~Config(); + + static Config* s_instance; + + ConfigPrivate* d; + }; + +} + +#endif diff --git a/3rdparty/libechonest/Parsing.cpp b/3rdparty/libechonest/Parsing.cpp new file mode 100644 index 000000000..a0f4805f8 --- /dev/null +++ b/3rdparty/libechonest/Parsing.cpp @@ -0,0 +1,601 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 "Util.h" + +#include +#include "Artist.h" +#include + +void Echonest::Parser::checkForErrors( QNetworkReply* reply ) throw( Echonest::ParseError ) +{ + if( !reply ) + throw new ParseError( Echonest::UnknownError ); + + if( !reply->isFinished() ) + throw new ParseError( Echonest::UnfinishedQuery ); + + if( reply->error() != QNetworkReply::NoError ) { + ParseError* err = new ParseError( Echonest::NetworkError ); + err->setNetworkError( reply->error() ); + + throw err; + } +} + +void Echonest::Parser::readStatus( QXmlStreamReader& xml ) throw( Echonest::ParseError ) +{ + if( xml.readNextStartElement() ) { + // sanity checks + if( xml.name() != QLatin1String( "response" ) ) + throw new ParseError( UnknownParseError ); + + if( xml.readNextStartElement() ) { + if( xml.name() != "status" ) + throw new ParseError( UnknownParseError ); + + // only check the error code for now + 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 ) { + throw new ParseError( code ); + } + + xml.readNext(); + } + + } else { + throw new ParseError( UnknownParseError ); + } +} + +QVector< Echonest::Song > Echonest::Parser::parseSongList( QXmlStreamReader& xml ) throw( Echonest::ParseError ) +{ + QVector< Echonest::Song > songs; + + xml.readNext(); + while( !( xml.name() == "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.name() != "song" ) + throw new ParseError( Echonest::UnknownParseError ); + + Echonest::Song song; + while( !( xml.name() == "song" && xml.tokenType() == QXmlStreamReader::EndElement ) ) { + if( xml.name() == "id" && xml.isStartElement() ) + song.setId( xml.readElementText().toLatin1() ); + else if( xml.name() == "title" && xml.isStartElement() ) + song.setTitle( xml.readElementText() ); + else if( xml.name() == "artist_id" && xml.isStartElement() ) + song.setArtistId( xml.readElementText().toLatin1() ); + else if( xml.name() == "artist_name" && xml.isStartElement() ) + song.setArtistName( xml.readElementText() ); + else if( xml.name() == "song_hotttnesss" && xml.isStartElement() ) + song.setHotttnesss( xml.readElementText().toDouble() ); + else if( xml.name() == "artist_hotttnesss" && xml.isStartElement() ) + song.setArtistHotttnesss( xml.readElementText().toDouble() ); + else if( xml.name() == "artist_familiarity" && xml.isStartElement() ) + song.setArtistFamiliarity( xml.readElementText().toDouble() ); + else if( xml.name() == "artist_location" && xml.isStartElement() ) { + while( !( xml.name() == "location" && xml.tokenType() == QXmlStreamReader::EndElement ) ) { + xml.readNextStartElement(); + if( xml.name() == "location" ) + song.setArtistLocation( xml.readElementText() ); + } + xml.readNext(); + } else if( xml.name() == "audio_summary" && xml.isStartElement() ) { + song.setAudioSummary( parseAudioSummary( xml ) ); + } + xml.readNext(); + } + xml.readNext(); // skip past the last + + return song; +} + +Echonest::Track Echonest::Parser::parseTrack( QXmlStreamReader& xml ) throw( Echonest::ParseError ) +{ + if( xml.name() != "track" ) { + throw new ParseError( Echonest::UnknownParseError ); + } + + Echonest::Track track; + while( !( xml.name() == "track" && xml.tokenType() == QXmlStreamReader::EndElement ) ) { + if( xml.name() == "id" && xml.isStartElement() ) + track.setId( xml.readElementText().toLatin1() ); + else if( xml.name() == "title" && xml.isStartElement() ) + track.setTitle( xml.readElementText() ); + else if( xml.name() == "artist" && xml.isStartElement() ) + track.setArtist( xml.readElementText() ); + else if( xml.name() == "status" && xml.isStartElement() ) + track.setStatus( Echonest::statusToEnum( xml.readElementText() ) ); + else if( xml.name() == "analyzer_version" && xml.isStartElement() ) + track.setAnalyzerVersion( xml.readElementText() ); + else if( xml.name() == "release" && xml.isStartElement() ) + track.setRelease( xml.readElementText() ); + else if( xml.name() == "audio_md5" && xml.isStartElement() ) + track.setAudioMD5( xml.readElementText().toLatin1() ); + else if( xml.name() == "bitrate" && xml.isStartElement() ) + track.setBitrate( xml.readElementText().toInt() ); + else if( xml.name() == "samplerate" && xml.isStartElement() ) + track.setSamplerate( xml.readElementText().toInt() ); + else if( xml.name() == "md5" && xml.isStartElement() ) + track.setMD5( xml.readElementText().toLatin1() ); + else if( xml.name() == "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.name() != "audio_summary" ) { + throw new ParseError( Echonest::UnknownParseError ); + } + + Echonest::AudioSummary summary; + while( !( xml.name() == "audio_summary" && xml.tokenType() == QXmlStreamReader::EndElement ) ) { + if( xml.name() == "key" && xml.isStartElement() ) + summary.setKey( xml.readElementText().toInt() ); + else if( xml.name() == "analysis_url" && xml.isStartElement() ) + summary.setAnalysisUrl( xml.readElementText() ); + else if( xml.name() == "tempo" && xml.isStartElement() ) + summary.setTempo( xml.readElementText().toDouble() ); + else if( xml.name() == "mode" && xml.isStartElement() ) + summary.setMode( xml.readElementText().toInt() ); + else if( xml.name() == "time_signature" && xml.isStartElement() ) + summary.setTimeSignature( xml.readElementText().toInt() ); + else if( xml.name() == "duration" && xml.isStartElement() ) + summary.setDuration( xml.readElementText().toDouble() ); + else if( xml.name() == "loudness" && xml.isStartElement() ) + summary.setLoudness( xml.readElementText().toDouble() ); + + xml.readNext(); + } + + return summary; +} + + +Echonest::Artists Echonest::Parser::parseArtists( QXmlStreamReader& xml ) +{ + // we expect to be in an start element + if( xml.name() != "artists" || !xml.isStartElement() ) + throw new ParseError( Echonest::UnknownParseError ); + + xml.readNextStartElement(); + + Echonest::Artists artists; + while( xml.name() != "artists" || !xml.isEndElement() ) { + if( xml.name() != "artist" || !xml.isStartElement() ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + Echonest::Artist artist; + while( xml.name() != "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() == "start" ) { // this is an individual info query, so lets read it + xml.readNextStartElement(); + xml.readNext(); + + int results = -1; + if( xml.name() == "total" ) { + results = xml.readElementText().toInt(); + xml.readNextStartElement(); + } + + parseArtistInfo( xml, artist ); + + return results; + } else if( xml.name() == "songs" ) { + parseArtistSong( xml, artist ); + } else if( xml.name() == "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() == "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() == "audio" ) { + parseAudio( xml, artist ); + } else if( xml.name() == "biographies" ) { + parseBiographies( xml, artist ); + } else if( xml.name() == "familiarity" ) { + artist.setFamiliarity( xml.readElementText().toDouble() ); + } else if( xml.name() == "hotttnesss" ) { + artist.setHotttnesss( xml.readElementText().toDouble() ); + } else if( xml.name() == "images" ) { + parseImages( xml, artist ); + } else if( xml.name() == "news" ) { + parseNewsOrBlogs( xml, artist, true ); + } else if( xml.name() == "blogs" ) { + parseNewsOrBlogs( xml, artist, false ); + } else if( xml.name() == "reviews" ) { + parseReviews( xml, artist ); + } else if( xml.name() == "terms" ) { + parseTerms( xml, artist ); + } else if( xml.name() == "urls" ) { + parseTerms( xml, artist ); + } else if( xml.name() == "songs" ) { + parseArtistSong( xml, artist ); + } else if( xml.name() == "video" ) { + parseVideos( xml, artist ); + } else if( xml.name() == "foreign_ids" ) { + parseForeignIds( xml, artist ); + } else if( xml.name() == "name" ) { + artist.setName( xml.readElementText() ); + } else if( xml.name() == "id" ) { + artist.setId( xml.readElementText().toLatin1() ); + } +} + + +// parse each type of artist attribute + +void Echonest::Parser::parseAudio( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) +{ + if( xml.name() != "audio" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + xml.readNextStartElement(); + Echonest::AudioList audioList; + while( xml.name() != "audio" || xml.tokenType() != QXmlStreamReader::EndElement ) { + Echonest::AudioFile audio; + do { + xml.readNext(); + + if( xml.name() == "title" ) + audio.setTitle( xml.readElementText() ); + else if( xml.name() == "url" ) + audio.setUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "artist" ) + audio.setArtist( xml.readElementText() ); + else if( xml.name() == "date" ) + audio.setDate( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); + else if( xml.name() == "length" ) + audio.setLength( xml.readElementText().toDouble() ); + else if( xml.name() == "link" ) + audio.setLink( QUrl( xml.readElementText() ) ); + else if( xml.name() == "release" ) + audio.setRelease( xml.readElementText() ); + else if( xml.name() == "id" ) + audio.setId( xml.readElementText().toLatin1() ); + + } while( xml.name() != "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.name() != "biographies" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + xml.readNextStartElement(); + Echonest::BiographyList bios; + while( xml.name() != "biographies" || xml.tokenType() != QXmlStreamReader::EndElement ) { + Echonest::Biography bio; + do { + xml.readNext(); + + if( xml.name() == "text" ) + bio.setText( xml.readElementText() ); + else if( xml.name() == "site" ) + bio.setSite( xml.readElementText() ); + else if( xml.name() == "url" ) + bio.setUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "license" ) + bio.setLicense( parseLicense( xml) ); + + } while( xml.name() != "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.name() != "images" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + xml.readNextStartElement(); + Echonest::ArtistImageList imgs; + while( xml.name() != "images" || xml.tokenType() != QXmlStreamReader::EndElement ) { + Echonest::ArtistImage img; + do { + xml.readNext(); + + if( xml.name() == "url" ) + img.setUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "license" ) + img.setLicense( parseLicense( xml) ); + + } while( xml.name() != "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.name() != "news" || xml.tokenType() != QXmlStreamReader::StartElement ) ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + else if( !news && ( xml.name() != "blogs" || xml.tokenType() != QXmlStreamReader::StartElement ) ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + xml.readNextStartElement(); + Echonest::NewsList newsList; + while( !( ( xml.name() == "news" || xml.name() == "blogs" ) && xml.tokenType() == QXmlStreamReader::EndElement ) ) { + Echonest::NewsArticle news; + do { + xml.readNextStartElement(); + + if( xml.name() == "name" ) + news.setName( xml.readElementText() ); + else if( xml.name() == "url" ) + news.setUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "summary" ) + news.setSummary( xml.readElementText() ); + else if( xml.name() == "date_found" ) + news.setDateFound( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); + else if( xml.name() == "id" ) + news.setId( xml.readElementText().toLatin1() ); + else if( xml.name() == "date_posted" ) + news.setDatePosted( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); + + } while( !( ( xml.name() == "news" || xml.name() == "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.name() != "reviews" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + xml.readNextStartElement(); + Echonest::ReviewList reviews; + while( xml.name() != "reviews" || xml.tokenType() != QXmlStreamReader::EndElement ) { + Echonest::Review review; + do { + xml.readNextStartElement(); + + if( xml.name() == "url" ) + review.setUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "name" ) + review.setName( xml.readElementText() ); + else if( xml.name() == "summary" ) + review.setSummary( xml.readElementText() ); + else if( xml.name() == "date_found" ) + review.setDateFound( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); + else if( xml.name() == "image" ) + review.setImageUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "release" ) + review.setRelease( xml.readElementText() ); + else if( xml.name() == "id" ) + review.setId( xml.readElementText().toLatin1() ); + + } while( xml.name() != "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.name() != "songs" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + xml.readNextStartElement(); + Echonest::SongList songs; + while( xml.name() != "songs" || xml.tokenType() != QXmlStreamReader::EndElement ) { + if( xml.name() == "song" && xml.isStartElement() ) + { + Echonest::Song song; + while( xml.name() != "song" || !xml.isEndElement() ) { + if( xml.name() == "id" && xml.isStartElement() ) + song.setId( xml.readElementText().toLatin1() ); + else if( xml.name() == "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.name() != "terms" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + artist.setTerms( parseTermList( xml ) ); +} + +void Echonest::Parser::parseUrls( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) +{ + if( xml.name() != "urls" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + xml.readNextStartElement(); + xml.readNextStartElement(); + + while( xml.name() != "urls" || !xml.isEndElement() ) { + if( xml.name() == "lastfm_url" ) + artist.setLastFmUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "aolmusic_url" ) + artist.setAolMusicUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "myspace_url" ) + artist.setMyspaceUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "amazon_url" ) + artist.setAmazonUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "itunes_url" ) + artist.setItunesUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "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.name() != "video" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + Echonest::VideoList videos; + while( xml.name() == "video" && xml.isStartElement() ) { + + Echonest::Video video; + + while( xml.name() != "video" || !xml.isEndElement() ) { + if( xml.name() == "title" ) + video.setTitle( xml.readElementText() ); + else if( xml.name() == "url" ) + video.setUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "site" ) + video.setSite( xml.readElementText() ); + else if( xml.name() == "date_found" ) + video.setDateFound( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) ); + else if( xml.name() == "image_url" ) + video.setImageUrl( QUrl( xml.readElementText() ) ); + else if( xml.name() == "id" ) + video.setId( xml.readElementText().toLatin1() ); + + xml.readNextStartElement(); + } + videos.append( video ); + + xml.readNextStartElement(); + } + artist.setVideos( videos ); +} + +Echonest::TermList Echonest::Parser::parseTermList( QXmlStreamReader& xml ) +{ + if( xml.name() != "terms" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + Echonest::TermList terms; + while( xml.name() == "terms" && xml.isStartElement() ) { + + Echonest::Term term; + + while( xml.name() != "terms" || !xml.isEndElement() ) { + if( xml.name() == "frequency" ) + term.setFrequency( xml.readElementText().toDouble() ); + else if( xml.name() == "name" ) + term.setName( xml.readElementText() ); + else if( xml.name() == "weight" ) + term.setWeight( xml.readElementText().toDouble() ); + + xml.readNextStartElement(); + } + terms.append( term ); + + xml.readNextStartElement(); + } + return terms; +} + +void Echonest::Parser::parseForeignIds( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError ) +{ + +} + +Echonest::License Echonest::Parser::parseLicense( QXmlStreamReader& xml ) throw( Echonest::ParseError ) +{ + if( xml.name() != "license" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + Echonest::License license; + while( xml.name() != "license" || xml.tokenType() != QXmlStreamReader::EndElement ) { + if( xml.name() == "type" ) + license.type = xml.readElementText(); + else if( xml.name() == "attribution" ) + license.attribution = xml.readElementText(); + else if( xml.name() == "url" ) + license.url = QUrl( xml.readElementText() ); + + xml.readNext(); + } + + xml.readNextStartElement(); + return license; +} + +QByteArray Echonest::Parser::parsePlaylistSessionId( QXmlStreamReader& xml ) throw( ParseError ) +{ + if( xml.name() != "session_id" || xml.tokenType() != QXmlStreamReader::StartElement ) + throw new Echonest::ParseError( Echonest::UnknownParseError ); + + QByteArray sessionId = xml.readElementText().toLatin1(); + xml.readNext(); //read to next start element + return sessionId; +} diff --git a/3rdparty/libechonest/Parsing_p.h b/3rdparty/libechonest/Parsing_p.h new file mode 100644 index 000000000..009bd214a --- /dev/null +++ b/3rdparty/libechonest/Parsing_p.h @@ -0,0 +1,87 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +#include "Song.h" +#include "Artist.h" + +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 + Track parseTrack( QXmlStreamReader& xml ) throw( ParseError ); + + // parses an chunk + AudioSummary parseAudioSummary( QXmlStreamReader& xml ) throw( ParseError ); + + // parses a list of artists in an block + Echonest::Artists parseArtists( QXmlStreamReader& xml ); + + // 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 parseForeignIds( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( ParseError ); + + // parse a list of terms + TermList parseTermList( QXmlStreamReader& xml ); + + License parseLicense( QXmlStreamReader& xml ) throw( ParseError ); + + QByteArray parsePlaylistSessionId( QXmlStreamReader& xml ) throw( ParseError ); +} +} + +#endif \ No newline at end of file diff --git a/3rdparty/libechonest/Playlist.cpp b/3rdparty/libechonest/Playlist.cpp new file mode 100644 index 000000000..6ff36784c --- /dev/null +++ b/3rdparty/libechonest/Playlist.cpp @@ -0,0 +1,315 @@ +/**************************************************************************************** + * Copyright (c) 2010 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" + +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::start(const Echonest::DynamicPlaylist::PlaylistParams& params) +{ + // params are the same, if user passes in format parsing will throw, but it should be expected.. + return generateInternal( params, "dynamic" ); +} + +Echonest::Song Echonest::DynamicPlaylist::parseStart(QNetworkReply* reply) throw( Echonest::ParseError ) +{ + Echonest::Parser::checkForErrors( reply ); + + QXmlStreamReader xml( reply->readAll() ); + + Echonest::Parser::readStatus( xml ); + d->sessionId = Echonest::Parser::parsePlaylistSessionId( xml ); + Echonest::SongList songs = Echonest::Parser::parseSongList( xml ); + if( !songs.size() == 1 ) + throw new Echonest::ParseError( UnknownParseError ); + + d->currentSong = songs.front(); + + return d->currentSong; +} + +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::fetchNextSong(int rating) +{ + QUrl url = Echonest::baseGetQuery( "playlist", "dynamic" ); + url.addEncodedQueryItem( "session_id", d->sessionId ); + + if( rating > 0 ) + url.addEncodedQueryItem( "rating", QByteArray::number( rating ) ); + + return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); + +} + + +Echonest::Song Echonest::DynamicPlaylist::parseNextSong(QNetworkReply* reply) +{ + return parseStart( reply ); +} + +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 ); + return songs; +} + +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 + url.removeEncodedQueryItem( "format" ); + + if( iter->first == Type ) { // convert type enum to string + switch( static_cast( iter->second.toInt() ) ) + { + case ArtistType: + url.addEncodedQueryItem( playlistParamToString( iter->first ), "artist" ); + break; + case ArtistRadioType: + url.addEncodedQueryItem( playlistParamToString( iter->first ), "artist-radio" ); + break; + case ArtistDescriptionType: + url.addEncodedQueryItem( playlistParamToString( iter->first ), "artist-description" ); + break; + } + + } else if( iter->first == Sort ) { + url.addEncodedQueryItem( playlistParamToString( iter->first ), playlistSortToString( static_cast( iter->second.toInt() ) ) ); + } else if( iter->first == Pick ) { + url.addEncodedQueryItem( playlistParamToString( iter->first ), playlistArtistPickToString( static_cast( iter->second.toInt() ) ) ); + } else if( iter->first == SongInformation ){ + Echonest::Song::addQueryInformation( url, Echonest::Song::SongInformation( iter->second.value< Echonest::Song::SongInformation >() ) ); + } else { + url.addQueryItem( QLatin1String( playlistParamToString( iter->first ) ), iter->second.toString().replace( QLatin1Char( ' ' ), QLatin1Char( '+' ) ) ); + } + } + + qDebug() << "Creating playlist URL" << url; + 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::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::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 "artist_min_longitude"; + case Echonest::DynamicPlaylist::ArtistMaxLongitude : + return "artist_max_longitude"; + case Echonest::DynamicPlaylist::ArtistMinLatitude : + return "artist_min_latitude"; + case Echonest::DynamicPlaylist::ArtistMaxLatitude : + return "artist_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"; + } + 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 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"; + } + return QByteArray(); +} + + +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(); +} diff --git a/3rdparty/libechonest/Playlist.h b/3rdparty/libechonest/Playlist.h new file mode 100644 index 000000000..a8f322612 --- /dev/null +++ b/3rdparty/libechonest/Playlist.h @@ -0,0 +1,200 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 + +class QNetworkReply; +class DynamicPlaylistData; + +namespace Echonest{ + + /** + * 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#dynamic + * 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, adn ArtistDescription plays songs matching + * the given description. + */ + enum ArtistTypeEnum { + ArtistType, + ArtistRadioType, + ArtistDescriptionType + }; + + /** + * 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 + }; + + /** + * 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. + 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 + 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 + 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::Song::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) + }; + + typedef QPair< PlaylistParam, QVariant > PlaylistParamData; + typedef QVector< PlaylistParamData > PlaylistParams; + + DynamicPlaylist(); + ~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() + * and the inital song will be populated and returned. The sessionId(), currentSong(), + * and fetchNextSong() methods will then be useful. + */ + QNetworkReply* start( const PlaylistParams& params ); + Song parseStart( QNetworkReply* ) throw( ParseError ); + + /** + * 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 in this + * dynamic playlist. + * + * Once the query has emitted the finished() signal, pass it to parseNextSong(), which will + * return the new song to play. It will also set the current song to the newly parsed song. + * + * If the playlist has no more songs, the returned song object will be have no name nor id. + * + * @param rating The rating for the song that was just played. Ranges from 1 (lowest) to 5 (highest) + * + */ + QNetworkReply* fetchNextSong( int rating = -1); + Song parseNextSong( QNetworkReply* reply ); + + /** + * Generate a static playlist, according to the desired criteria. + */ + static QNetworkReply* staticPlaylist( const PlaylistParams& params ); + static SongList parseStaticPlaylist( 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); + + QSharedDataPointer d; + }; + + ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::DynamicPlaylist& playlist); + + +} // namespace +#endif diff --git a/3rdparty/libechonest/Playlist_p.h b/3rdparty/libechonest/Playlist_p.h new file mode 100644 index 000000000..7458a048c --- /dev/null +++ b/3rdparty/libechonest/Playlist_p.h @@ -0,0 +1,34 @@ +/**************************************************************************************** + * Copyright (c) 2010 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) + { + sessionId = other.sessionId; + currentSong = other.currentSong; + } + + QByteArray sessionId; + Echonest::Song currentSong; +}; + diff --git a/3rdparty/libechonest/Song.cpp b/3rdparty/libechonest/Song.cpp new file mode 100644 index 000000000..0ea9ee1dc --- /dev/null +++ b/3rdparty/libechonest/Song.cpp @@ -0,0 +1,311 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +#include + +#include +#include "Parsing_p.h" +#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 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; +} + +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; +} + +QString Echonest::Song::artistLocation() const +{ + return d->artistLocation; +} + +void Echonest::Song::setArtistLocation(const QString& artistLocation) +{ + d->artistLocation = artistLocation; +} + +QNetworkReply* Echonest::Song::fetchInformation( Echonest::Song::SongInformation parts ) const +{ + QUrl url = Echonest::baseGetQuery( "song", "profile" ); + url.addEncodedQueryItem( "id", d->id ); + addQueryInformation( url, parts ); + + qDebug() << "Creating fetchInformation URL" << url; + return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); +} + +QNetworkReply* Echonest::Song::search( const Echonest::Song::SearchParams& params, Echonest::Song::SongInformation parts ) +{ + QUrl url = Echonest::baseGetQuery( "song", "search" ); + addQueryInformation( url, parts ); + + SearchParams::const_iterator iter = params.constBegin(); + for( ; iter < params.constEnd(); ++iter ) + url.addQueryItem( QLatin1String( searchParamToString( iter->first ) ), iter->second.toString() ); + + qDebug() << "Creating search URL" << url; + return Echonest::Config::instance()->nam()->get( QNetworkRequest( url ) ); +} + +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().isEmpty() ) + setArtistLocation( newSong.artistLocation() ); + +} + +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 ); + + 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::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::Mode: + return "mode"; + case Echonest::Song::Key: + return "key"; + case Echonest::Song::Sort: + return "sort"; + } + return QByteArray(); +} + +void Echonest::Song::addQueryInformation(QUrl& url, Echonest::Song::SongInformation parts) +{ + if( parts.testFlag( Echonest::Song::AudioSummaryInformation ) ) + url.addEncodedQueryItem( "bucket", "audio_summary" ); + if( parts.testFlag( Echonest::Song::Tracks ) ) + url.addEncodedQueryItem( "bucket", "tracks" ); + if( parts.testFlag( Echonest::Song::Hotttnesss ) ) + url.addEncodedQueryItem( "bucket", "song_hotttnesss" ); + if( parts.testFlag( Echonest::Song::ArtistHotttnesss ) ) + url.addEncodedQueryItem( "bucket", "artist_hotttnesss" ); + if( parts.testFlag( Echonest::Song::ArtistFamiliarity ) ) + url.addEncodedQueryItem( "bucket", "artist_familiarity" ); + if( parts.testFlag( Echonest::Song::ArtistLocation ) ) + url.addEncodedQueryItem( "bucket", "artist_location" ); +} + + +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(); +} + diff --git a/3rdparty/libechonest/Song.h b/3rdparty/libechonest/Song.h new file mode 100644 index 000000000..ab037bb8d --- /dev/null +++ b/3rdparty/libechonest/Song.h @@ -0,0 +1,217 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +#include +#include +#include +#include +#include "Config.h" + +class QNetworkReply; +class SongData; +namespace Echonest{ + + class DynamicPlaylist; // forward declare for friend declaration + 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 SongInformationFlag { + AudioSummaryInformation = 0x01, + Tracks = 0x02, + Hotttnesss = 0x04, + ArtistHotttnesss = 0x08, + ArtistFamiliarity = 0x10, + ArtistLocation = 0x20 + }; + Q_DECLARE_FLAGS( SongInformation, SongInformationFlag ) + + enum SearchParam { + Title, + Artist, + Combined, + Description, + ArtistId, + Results, + MaxTempo, + MinTempo, + MaxDanceability, + MinDanceability, + MaxComplexity, + MinComplexity, + MaxDuration, + MinDuration, + MaxLoudness, + MinLoudness, + MaxFamiliarity, + MinFamiliarity, + MaxHotttnesss, + MinHotttnesss, + MaxLongitude, + MinLongitude, + Mode, + Key, + Sort + }; + typedef QPair< Echonest::Song::SearchParam, QVariant > SearchParamData; + typedef QVector< SearchParamData > SearchParams; + + Song(); + Song( const QByteArray& id, const QString& title, const QByteArray& artistId, const QString& artistName ); + Song( const Song& other ); + Song& operator=(const Song& song); + ~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 ); + + + /** + * 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. + */ + 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. + */ + QString artistLocation() const; + void setArtistLocation( const QString& artistLocation ); + + /** + * This fetch 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 parts ) 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 parts ); + + /** + * 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 ); + + /** + * Identify a song from a given Echo Nest fingerprint hash code + * + * NOTE this is currently not supported, as the Echo Nest hash code + * generator is not currently open source, so I don't care much + * for it. + * + * static QNetworkReply* identify( ) const; + */ + + QString toString() const; + + friend class DynamicPlaylist; +private: + static QByteArray searchParamToString( SearchParam param ); + static void addQueryInformation( QUrl& url, SongInformation parts ); + + QSharedDataPointer d; +}; + +typedef QVector< Song > SongList; + +ECHONEST_EXPORT QDebug operator<<(QDebug d, const Song &song); + +Q_DECLARE_OPERATORS_FOR_FLAGS(Song::SongInformation) + +} // namespace + +Q_DECLARE_METATYPE( Echonest::Song::SongInformation ) + +#endif diff --git a/3rdparty/libechonest/Song_p.h b/3rdparty/libechonest/Song_p.h new file mode 100644 index 000000000..3625ffe16 --- /dev/null +++ b/3rdparty/libechonest/Song_p.h @@ -0,0 +1,66 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 ) {} + + SongData(const SongData& 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; + } + + ~SongData() {} + + QByteArray id; + QString title; + QString artistName; + QByteArray artistId; + + // The rest are optional that require manual fetching to populate + Echonest::AudioSummary audioSummary; + QVector tracks; + qreal hotttnesss; + qreal artistHotttnesss; + qreal artistFamiliarity; + QString artistLocation; + + +}; + +#endif diff --git a/3rdparty/libechonest/Track.cpp b/3rdparty/libechonest/Track.cpp new file mode 100644 index 000000000..a924daaf9 --- /dev/null +++ b/3rdparty/libechonest/Track.cpp @@ -0,0 +1,247 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +#include "Config.h" +#include +#include "Parsing_p.h" + +Echonest::Track::Track() + : d( new TrackData ) +{ +} + +Echonest::Track::Track(const Echonest::Track& other) + : d( other.d ) +{} + +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; +} + +QNetworkReply* Echonest::Track::profileFromTrackId( const QByteArray& id ) +{ + QUrl url = Echonest::baseGetQuery( "track", "profile" ); + url.addEncodedQueryItem( "id", id ); + url.addEncodedQueryItem( "bucket", "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" ); + url.addEncodedQueryItem( "md5", md5 ); + url.addEncodedQueryItem( "bucket", "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() ); + url.addQueryItem( QLatin1String( "filetype" ), info.suffix() ); + url.addEncodedQueryItem( "bucket", "audio_summary" ); + url.addEncodedQueryItem( "wait", ( 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" ); + url.addEncodedQueryItem( "url", remoteURL.toEncoded() ); + url.addEncodedQueryItem( "bucket", "audio_summary" ); + url.addEncodedQueryItem( "wait", ( waitForResult ? "true" : "false" ) ); + + qDebug() << "Uploading URL:" << url; + return Echonest::Config::instance()->nam()->post( QNetworkRequest( url ), QByteArray() ); +} + +QNetworkReply* Echonest::Track::analyzeTrackId( const QByteArray& id, bool wait ) +{ + QUrl url = Echonest::baseGetQuery( "track", "analyze" ); + url.addEncodedQueryItem( "id", id ); + url.addEncodedQueryItem( "bucket", "audio_summary" ); + url.addEncodedQueryItem( "wait", ( wait ? "true" : "false" ) ); + + qDebug() << "Creating analyzeTrackId URL" << 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" ); + url.addEncodedQueryItem( "md5", md5 ); + url.addEncodedQueryItem( "bucket", "audio_summary" ); + url.addEncodedQueryItem( "wait", ( wait ? "true" : "false" ) ); + + qDebug() << "Creating analyzeTrackMD5 URL" << url; + return Echonest::Config::instance()->nam()->post( QNetworkRequest( url ), QByteArray() ); +} + +Echonest::Track Echonest::Track::parseProfile( QNetworkReply* finishedReply ) throw( Echonest::ParseError ) +{ + Echonest::Parser::checkForErrors( finishedReply ); + + QByteArray data = finishedReply->readAll(); + qDebug() << data; + QXmlStreamReader xml( data ); + + Echonest::Parser::readStatus( xml ); + Echonest::Track track = Echonest::Parser::parseTrack( xml ); + + return track; +} + + + +QDebug Echonest::operator<<(QDebug d, const Echonest::Track& track) +{ + d << QString::fromLatin1( "Track(%1, %2, %3" ).arg( track.title() ).arg( track.artist() ).arg( track.release() ); + return d.maybeSpace(); +} + diff --git a/3rdparty/libechonest/Track.h b/3rdparty/libechonest/Track.h new file mode 100644 index 000000000..43a654cd3 --- /dev/null +++ b/3rdparty/libechonest/Track.h @@ -0,0 +1,185 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +{ + +/** + * 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(); + 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 ); + + /** + * 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 + */ + 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; +}; + +ECHONEST_EXPORT QDebug operator<<(QDebug d, const Echonest::Track& track); + + +} // namespace + +#endif diff --git a/3rdparty/libechonest/Track_p.h b/3rdparty/libechonest/Track_p.h new file mode 100644 index 000000000..a08d785ba --- /dev/null +++ b/3rdparty/libechonest/Track_p.h @@ -0,0 +1,59 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +#include + +class TrackData : public QSharedData +{ +public: + TrackData() {} + + TrackData(const TrackData& 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; + + } + + QString artist; + QString analyzer_version; + int bitrate; + QByteArray id; + QByteArray md5; + QString release; + QByteArray audio_md5; + int samplerate; + QString status; + QString title; + + Echonest::AudioSummary audio_summary; + +}; + +#endif diff --git a/3rdparty/libechonest/Util.cpp b/3rdparty/libechonest/Util.cpp new file mode 100644 index 000000000..996adf141 --- /dev/null +++ b/3rdparty/libechonest/Util.cpp @@ -0,0 +1,49 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 + +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(); +} \ No newline at end of file diff --git a/3rdparty/libechonest/Util.h b/3rdparty/libechonest/Util.h new file mode 100644 index 000000000..e62a35d97 --- /dev/null +++ b/3rdparty/libechonest/Util.h @@ -0,0 +1,80 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 +#include + +/** + * Some shared declarations + */ + +namespace Echonest +{ + namespace Analysis + { + enum AnalysisStatus { + 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; + + Analysis::AnalysisStatus statusToEnum( const QString& status ); + QString statusToString( Analysis::AnalysisStatus status ); +} + +#endif diff --git a/3rdparty/libechonest/echonest_export.h b/3rdparty/libechonest/echonest_export.h new file mode 100644 index 000000000..7acf7c6df --- /dev/null +++ b/3rdparty/libechonest/echonest_export.h @@ -0,0 +1,28 @@ +/**************************************************************************************** + * Copyright (c) 2010 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 + +#ifdef Q_CC_MSVC + #define ECHONEST_EXPORT __declspec(dllimport) +#elif __GNUC__ >= 4 + #define ECHONEST_EXPORT __attribute__ ((visibility("default"))) +#else + #define ECHONEST_EXPORT +#endif + +#endif diff --git a/3rdparty/libechonest/libechonest_export.h b/3rdparty/libechonest/libechonest_export.h new file mode 100644 index 000000000..e69de29bb diff --git a/CMakeLists.txt b/CMakeLists.txt index 67dc6fbb8..66fa16d40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,8 @@ else (USE_SYSTEM_QXT) endif (NOT APPLE) endif (USE_SYSTEM_QXT) +set(ECHONEST_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/3rdparty/libechonest) + # Subdirectories add_subdirectory(src) if (WIN32) @@ -215,6 +217,7 @@ if (WIN32) endif (WIN32) add_subdirectory(3rdparty/universalchardet) add_subdirectory(3rdparty/fancytabwidget) +add_subdirectory(3rdparty/libechonest) add_subdirectory(tests) add_subdirectory(dist) add_subdirectory(tools/ultimate_lyrics_parser) diff --git a/data/data.qrc b/data/data.qrc index fbb5ec854..1ac53717f 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -1,6 +1,7 @@ mainwindow.css + songinfo.css schema.sql volumeslider-handle_glow.png volumeslider-handle.png diff --git a/data/songinfo.css b/data/songinfo.css new file mode 100644 index 000000000..dfbc3516f --- /dev/null +++ b/data/songinfo.css @@ -0,0 +1,8 @@ +QScrollArea { + background: palette(base); +} + +QTextEdit { + border: 0px; +} + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b4fd366ba..db85fff97 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,6 +17,7 @@ include_directories(${LIBPROJECTM_INCLUDE_DIRS}) include_directories(${QTSINGLEAPPLICATION_INCLUDE_DIRS}) include_directories(${QTIOCOMPRESSOR_INCLUDE_DIRS}) include_directories(${QXT_INCLUDE_DIRS}) +include_directories(${ECHONEST_INCLUDE_DIRS}) cmake_policy(SET CMP0011 NEW) include(../cmake/AddEngine.cmake) @@ -127,6 +128,11 @@ set(SOURCES radio/savedradio.cpp radio/somafmservice.cpp + songinfo/artistinfofetcher.cpp + songinfo/artistinfoprovider.cpp + songinfo/artistinfoview.cpp + songinfo/collapsibleinfopane.cpp + songinfo/echonestartistinfo.cpp songinfo/htmlscraper.cpp songinfo/lyricfetcher.cpp songinfo/lyricprovider.cpp @@ -257,6 +263,11 @@ set(HEADERS radio/savedradio.h radio/somafmservice.h + songinfo/artistinfofetcher.h + songinfo/artistinfoprovider.h + songinfo/artistinfoview.h + songinfo/collapsibleinfopane.h + songinfo/echonestartistinfo.h songinfo/htmlscraper.h songinfo/lyricfetcher.h songinfo/lyricprovider.h @@ -681,6 +692,7 @@ add_dependencies(clementine_lib pot) target_link_libraries(clementine_lib chardet fancytabwidget + echonest ${GOBJECT_LIBRARIES} ${GLIB_LIBRARIES} ${TAGLIB_LIBRARIES} diff --git a/src/main.cpp b/src/main.cpp index c9314f9ce..fc15546d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,8 @@ #include #include +#include + #ifdef Q_WS_X11 # include # include @@ -198,6 +200,9 @@ int main(int argc, char *argv[]) { NetworkAccessManager network; + Echonest::Config::instance()->setAPIKey("DFLFLJBUF4EGTXHIG"); + Echonest::Config::instance()->setNetworkAccessManager(network.network()); + // MPRIS DBus interface. #ifdef Q_WS_X11 qDBusRegisterMetaType(); diff --git a/src/songinfo/artistinfofetcher.cpp b/src/songinfo/artistinfofetcher.cpp new file mode 100644 index 000000000..2a7f1407a --- /dev/null +++ b/src/songinfo/artistinfofetcher.cpp @@ -0,0 +1,39 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "artistinfofetcher.h" +#include "echonestartistinfo.h" + +ArtistInfoFetcher::ArtistInfoFetcher(QObject* parent) + : QObject(parent), + next_id_(1) +{ + AddProvider(new EchoNestArtistInfo(this)); +} + +void ArtistInfoFetcher::AddProvider(ArtistInfoProvider* provider) { + providers_ << provider; + connect(provider, SIGNAL(ImageReady(int,QUrl)), SIGNAL(ImageReady(int,QUrl))); + connect(provider, SIGNAL(InfoReady(int,QString,QWidget*)), SIGNAL(InfoReady(int,QString,QWidget*))); +} + +int ArtistInfoFetcher::FetchInfo(const QString& artist) { + const int id = next_id_ ++; + foreach (ArtistInfoProvider* provider, providers_) { + provider->FetchInfo(id, artist); + } + return id; +} diff --git a/src/songinfo/artistinfofetcher.h b/src/songinfo/artistinfofetcher.h new file mode 100644 index 000000000..82e8076d8 --- /dev/null +++ b/src/songinfo/artistinfofetcher.h @@ -0,0 +1,46 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef ARTISTINFOFETCHER_H +#define ARTISTINFOFETCHER_H + +#include +#include + +class ArtistInfoProvider; + +class ArtistInfoFetcher : public QObject { + Q_OBJECT + +public: + ArtistInfoFetcher(QObject* parent = 0); + + int FetchInfo(const QString& artist); + +signals: + void ImageReady(int id, const QUrl& url); + void InfoReady(int id, const QString& title, QWidget* widget); + +private: + void AddProvider(ArtistInfoProvider* provider); + +private: + QList providers_; + + int next_id_; +}; + +#endif // ARTISTINFOFETCHER_H diff --git a/src/songinfo/artistinfoprovider.cpp b/src/songinfo/artistinfoprovider.cpp new file mode 100644 index 000000000..af0b21329 --- /dev/null +++ b/src/songinfo/artistinfoprovider.cpp @@ -0,0 +1,22 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "artistinfoprovider.h" + +ArtistInfoProvider::ArtistInfoProvider(QObject* parent) + : QObject(parent) +{ +} diff --git a/src/songinfo/artistinfoprovider.h b/src/songinfo/artistinfoprovider.h new file mode 100644 index 000000000..9bcbd6458 --- /dev/null +++ b/src/songinfo/artistinfoprovider.h @@ -0,0 +1,36 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef ARTISTINFOPROVIDER_H +#define ARTISTINFOPROVIDER_H + +#include +#include + +class ArtistInfoProvider : public QObject { + Q_OBJECT + +public: + ArtistInfoProvider(QObject* parent = 0); + + virtual void FetchInfo(int id, const QString& artist) = 0; + +signals: + void ImageReady(int id, const QUrl& url); + void InfoReady(int id, const QString& title, QWidget* widget); +}; + +#endif // ARTISTINFOPROVIDER_H diff --git a/src/songinfo/artistinfoview.cpp b/src/songinfo/artistinfoview.cpp new file mode 100644 index 000000000..dbac89ab2 --- /dev/null +++ b/src/songinfo/artistinfoview.cpp @@ -0,0 +1,95 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "artistinfofetcher.h" +#include "artistinfoview.h" +#include "collapsibleinfopane.h" + +#include +#include +#include +#include +#include + +ArtistInfoView::ArtistInfoView(QWidget *parent) + : SongInfoBase(parent), + fetcher_(new ArtistInfoFetcher(this)), + current_request_id_(-1), + scroll_area_(new QScrollArea), + container_(new QVBoxLayout) +{ + connect(fetcher_, SIGNAL(ImageReady(int,QUrl)), SLOT(ImageReady(int,QUrl))); + connect(fetcher_, SIGNAL(InfoReady(int,QString,QWidget*)), SLOT(InfoReady(int,QString,QWidget*))); + + // Add the top-level scroll area + setLayout(new QVBoxLayout); + layout()->setContentsMargins(0, 0, 0, 0); + layout()->addWidget(scroll_area_); + + // Add a container widget to the scroll area + QWidget* container_widget = new QWidget; + container_widget->setLayout(container_); + container_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + container_->setSizeConstraint(QLayout::SetNoConstraint); + scroll_area_->setWidget(container_widget); + scroll_area_->setWidgetResizable(true); + + // Add a spacer to the bottom of the container + container_->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding)); + + // Set stylesheet + QFile stylesheet(":/songinfo.css"); + stylesheet.open(QIODevice::ReadOnly); + setStyleSheet(QString::fromAscii(stylesheet.readAll())); +} + +ArtistInfoView::~ArtistInfoView() { +} + +void ArtistInfoView::AddChild(QWidget* widget) { + children_ << widget; + container_->insertWidget(container_->count() - 1, widget); + widget->show(); +} + +void ArtistInfoView::Clear() { + qDeleteAll(children_); + children_.clear(); +} + +void ArtistInfoView::Update(const Song& metadata) { + Clear(); + current_request_id_ = fetcher_->FetchInfo(metadata.artist()); +} + +void ArtistInfoView::ImageReady(int id, const QUrl& url) { + if (id != current_request_id_) + return; + + qDebug() << "Image" << url; +} + +void ArtistInfoView::InfoReady(int id, const QString& title, QWidget* widget) { + if (id != current_request_id_) { + delete widget; + return; + } + + CollapsibleInfoPane* pane = new CollapsibleInfoPane(this); + pane->SetTitle(title); + pane->SetWidget(widget); + AddChild(pane); +} diff --git a/src/songinfo/artistinfoview.h b/src/songinfo/artistinfoview.h new file mode 100644 index 000000000..620bb8e6d --- /dev/null +++ b/src/songinfo/artistinfoview.h @@ -0,0 +1,54 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef ARTISTINFOVIEW_H +#define ARTISTINFOVIEW_H + +#include "songinfobase.h" + +class ArtistInfoFetcher; + +class QScrollArea; +class QVBoxLayout; + +class ArtistInfoView : public SongInfoBase { + Q_OBJECT + +public: + ArtistInfoView(QWidget* parent = 0); + ~ArtistInfoView(); + +protected: + void Update(const Song& metadata); + +private: + void AddChild(QWidget* widget); + void Clear(); + +private slots: + void ImageReady(int id, const QUrl& url); + void InfoReady(int id, const QString& title, QWidget* widget); + +private: + ArtistInfoFetcher* fetcher_; + int current_request_id_; + + QScrollArea* scroll_area_; + QVBoxLayout* container_; + QList children_; +}; + +#endif // ARTISTINFOVIEW_H diff --git a/src/songinfo/collapsibleinfopane.cpp b/src/songinfo/collapsibleinfopane.cpp new file mode 100644 index 000000000..75d7df07c --- /dev/null +++ b/src/songinfo/collapsibleinfopane.cpp @@ -0,0 +1,99 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "collapsibleinfopane.h" + +#include +#include +#include + +const int CollapsibleInfoPane::kMargin = 6; +const int CollapsibleInfoPane::kTitleHeight = 20; + +CollapsibleInfoPane::CollapsibleInfoPane(QWidget* parent) + : QWidget(parent), + widget_(NULL), + expanded_(true) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(kMargin, kTitleHeight, kMargin, 0); + setLayout(layout); + + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + setMinimumHeight(kTitleHeight); +} + +void CollapsibleInfoPane::SetTitle(const QString& title) { + title_ = title; + update(); +} + +void CollapsibleInfoPane::SetWidget(QWidget* widget) { + if (widget_) + delete widget_; + + widget_ = widget; + layout()->addWidget(widget); +} + +void CollapsibleInfoPane::Collapse() { + expanded_ = false; + update(); +} + +void CollapsibleInfoPane::Expand() { + expanded_ = true; + update(); +} + +void CollapsibleInfoPane::paintEvent(QPaintEvent* e) { + QStylePainter p(this); + + QRect title_rect(0, 0, width(), kTitleHeight); + QRect indicator_rect(0, 0, kTitleHeight, kTitleHeight); + QRect text_rect(title_rect); + text_rect.setLeft(kTitleHeight + 6); + + // Draw the background + const QColor bg_color_1(palette().color(QPalette::Highlight)); + const QColor bg_color_2(palette().color(QPalette::Highlight).lighter(125)); + const QColor bg_border(palette().color(QPalette::Dark)); + QLinearGradient bg_brush(title_rect.topLeft(), title_rect.bottomLeft()); + bg_brush.setColorAt(0.0, bg_color_1); + bg_brush.setColorAt(0.5, bg_color_2); + bg_brush.setColorAt(1.0, bg_color_1); + + p.setPen(bg_border); + p.drawLine(title_rect.topLeft(), title_rect.topRight()); + p.drawLine(title_rect.bottomLeft(), title_rect.bottomRight()); + + p.setPen(Qt::NoPen); + p.fillRect(title_rect, bg_brush); + + // Draw the expand/collapse indicator + QStyleOption opt; + opt.initFrom(this); + opt.rect = indicator_rect; + opt.state |= QStyle::State_Children; + if (expanded_) + opt.state |= QStyle::State_Open; + + p.drawPrimitive(QStyle::PE_IndicatorBranch, opt); + + // Draw the title text + p.setPen(palette().color(QPalette::HighlightedText)); + p.drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, title_); +} diff --git a/src/songinfo/collapsibleinfopane.h b/src/songinfo/collapsibleinfopane.h new file mode 100644 index 000000000..542be4c04 --- /dev/null +++ b/src/songinfo/collapsibleinfopane.h @@ -0,0 +1,47 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef COLLAPSIBLEINFOPANE_H +#define COLLAPSIBLEINFOPANE_H + +#include + +class CollapsibleInfoPane : public QWidget { + Q_OBJECT + +public: + CollapsibleInfoPane(QWidget* parent = 0); + + static const int kTitleHeight; + static const int kMargin; + + void SetTitle(const QString& title); + void SetWidget(QWidget* widget); + +public slots: + void Expand(); + void Collapse(); + +protected: + void paintEvent(QPaintEvent* e); + +private: + QString title_; + QWidget* widget_; + bool expanded_; +}; + +#endif // COLLAPSIBLEINFOPANE_H diff --git a/src/songinfo/echonestartistinfo.cpp b/src/songinfo/echonestartistinfo.cpp new file mode 100644 index 000000000..a83107611 --- /dev/null +++ b/src/songinfo/echonestartistinfo.cpp @@ -0,0 +1,89 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "echonestartistinfo.h" + +#include + +#include + +#include + +struct EchoNestArtistInfo::Request { + Request(int id) : id_(id), artist_(new Echonest::Artist) {} + + int id_; + boost::scoped_ptr artist_; + QList pending_replies_; +}; + +EchoNestArtistInfo::EchoNestArtistInfo(QObject* parent) + : ArtistInfoProvider(parent) +{ +} + +void EchoNestArtistInfo::FetchInfo(int id, const QString& artist_name) { + boost::shared_ptr request(new Request(id)); + request->artist_->setName(artist_name); + + ConnectReply(request, request->artist_->fetchBiographies(), SLOT(BiographiesFinished())); + ConnectReply(request, request->artist_->fetchImages(), SLOT(ImagesFinished())); + + requests_ << request; +} + +void EchoNestArtistInfo::ConnectReply( + boost::shared_ptr request, QNetworkReply* reply, const char* slot) { + request->pending_replies_ << reply; + connect(reply, SIGNAL(finished()), slot); +} + +EchoNestArtistInfo::RequestPtr EchoNestArtistInfo::ReplyFinished(QNetworkReply* reply) { + reply->deleteLater(); + + foreach (RequestPtr request, requests_) { + if (request->pending_replies_.contains(reply)) { + request->artist_->parseProfile(reply); + request->pending_replies_.removeAll(reply); + + if (request->pending_replies_.isEmpty()) { + requests_.removeAll(request); + } + + return request; + } + } + return RequestPtr(); +} + +void EchoNestArtistInfo::ImagesFinished() { + RequestPtr request = ReplyFinished(qobject_cast(sender())); + + foreach (const Echonest::ArtistImage& image, request->artist_->images()) { + emit ImageReady(request->id_, image.url()); + } +} + +void EchoNestArtistInfo::BiographiesFinished() { + RequestPtr request = ReplyFinished(qobject_cast(sender())); + + foreach (const Echonest::Biography& bio, request->artist_->biographies()) { + QTextEdit* editor = new QTextEdit; + editor->setHtml(bio.text()); + + emit InfoReady(request->id_, tr("Biography from %1").arg(bio.site()), editor); + } +} diff --git a/src/songinfo/echonestartistinfo.h b/src/songinfo/echonestartistinfo.h new file mode 100644 index 000000000..cb174deb1 --- /dev/null +++ b/src/songinfo/echonestartistinfo.h @@ -0,0 +1,49 @@ +/* This file is part of Clementine. + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#ifndef ECHONESTARTISTINFO_H +#define ECHONESTARTISTINFO_H + +#include "artistinfoprovider.h" + +#include + +class QNetworkReply; + +class EchoNestArtistInfo : public ArtistInfoProvider { + Q_OBJECT + +public: + EchoNestArtistInfo(QObject* parent = 0); + + void FetchInfo(int id, const QString& artist); + +private slots: + void BiographiesFinished(); + void ImagesFinished(); + +private: + struct Request; + typedef boost::shared_ptr RequestPtr; + + void ConnectReply(RequestPtr request, QNetworkReply* reply, const char* slot); + RequestPtr ReplyFinished(QNetworkReply* reply); + +private: + QList requests_; +}; + +#endif // ECHONESTARTISTINFO_H diff --git a/src/translations/ar.po b/src/translations/ar.po index 642a8adfb..08a1d4776 100644 --- a/src/translations/ar.po +++ b/src/translations/ar.po @@ -312,6 +312,10 @@ msgstr "" msgid "Behavior" msgstr "" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "معدل البت" diff --git a/src/translations/bg.po b/src/translations/bg.po index 1078583df..87825d142 100644 --- a/src/translations/bg.po +++ b/src/translations/bg.po @@ -313,6 +313,10 @@ msgstr "" msgid "Behavior" msgstr "" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "" diff --git a/src/translations/ca.po b/src/translations/ca.po index bd4898fe4..6657dfc8d 100644 --- a/src/translations/ca.po +++ b/src/translations/ca.po @@ -322,6 +322,10 @@ msgstr "Blau Bàsic" msgid "Behavior" msgstr "Comportament" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Taxa de bits" diff --git a/src/translations/cs.po b/src/translations/cs.po index 8f231de58..a6506cf82 100644 --- a/src/translations/cs.po +++ b/src/translations/cs.po @@ -314,6 +314,10 @@ msgstr "Jednoduchá modrá" msgid "Behavior" msgstr "Chování" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Datový tok" diff --git a/src/translations/da.po b/src/translations/da.po index 9af6056d1..458ee1806 100644 --- a/src/translations/da.po +++ b/src/translations/da.po @@ -314,6 +314,10 @@ msgstr "Basal blå" msgid "Behavior" msgstr "Adfærd" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bitrate" diff --git a/src/translations/de.po b/src/translations/de.po index daefcb082..fef4fd16c 100644 --- a/src/translations/de.po +++ b/src/translations/de.po @@ -320,6 +320,10 @@ msgstr "Standardblau" msgid "Behavior" msgstr "Verhalten" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bitrate" diff --git a/src/translations/el.po b/src/translations/el.po index b512ac0c7..183b326ec 100644 --- a/src/translations/el.po +++ b/src/translations/el.po @@ -321,6 +321,10 @@ msgstr "Βασικό μπλε" msgid "Behavior" msgstr "Συμπεριφορά" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Ρυθμός bit" diff --git a/src/translations/en_CA.po b/src/translations/en_CA.po index 02c46c3b7..d53532807 100644 --- a/src/translations/en_CA.po +++ b/src/translations/en_CA.po @@ -312,6 +312,10 @@ msgstr "Basic Blue" msgid "Behavior" msgstr "Behaviour" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bit rate" diff --git a/src/translations/en_GB.po b/src/translations/en_GB.po index a943d4d01..52b2ea0e3 100644 --- a/src/translations/en_GB.po +++ b/src/translations/en_GB.po @@ -312,6 +312,10 @@ msgstr "Basic Blue" msgid "Behavior" msgstr "Behaviour" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bit rate" diff --git a/src/translations/es.po b/src/translations/es.po index 8253e35ea..f4dd76cc3 100644 --- a/src/translations/es.po +++ b/src/translations/es.po @@ -322,6 +322,10 @@ msgstr "Azul básico" msgid "Behavior" msgstr "Comportamiento" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Tasa de bits" diff --git a/src/translations/fi.po b/src/translations/fi.po index aa7b4aa30..1cfa4f9d9 100644 --- a/src/translations/fi.po +++ b/src/translations/fi.po @@ -313,6 +313,10 @@ msgstr "" msgid "Behavior" msgstr "" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bittivirta" diff --git a/src/translations/fr.po b/src/translations/fr.po index 3301ea2da..19ef291e3 100644 --- a/src/translations/fr.po +++ b/src/translations/fr.po @@ -325,6 +325,10 @@ msgstr "Bleu standard" msgid "Behavior" msgstr "Comportement" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Débit" diff --git a/src/translations/gl.po b/src/translations/gl.po index 6937dd7d3..0a0f1c4d6 100644 --- a/src/translations/gl.po +++ b/src/translations/gl.po @@ -313,6 +313,10 @@ msgstr "Azul básico" msgid "Behavior" msgstr "Comportamento" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Taxa de bits" diff --git a/src/translations/hu.po b/src/translations/hu.po index 63963113f..45e39d830 100644 --- a/src/translations/hu.po +++ b/src/translations/hu.po @@ -318,6 +318,10 @@ msgstr "Egyszerű kék" msgid "Behavior" msgstr "Viselkedés" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bitráta" diff --git a/src/translations/it.po b/src/translations/it.po index 95eb4962d..ea487a3f9 100644 --- a/src/translations/it.po +++ b/src/translations/it.po @@ -325,6 +325,10 @@ msgstr "Blu di base" msgid "Behavior" msgstr "Comportamento" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bitrate" diff --git a/src/translations/kk.po b/src/translations/kk.po index 5dc8e0e8a..8a437e808 100644 --- a/src/translations/kk.po +++ b/src/translations/kk.po @@ -312,6 +312,10 @@ msgstr "" msgid "Behavior" msgstr "" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "" diff --git a/src/translations/lt.po b/src/translations/lt.po index 3ce18561d..33fcb6b24 100644 --- a/src/translations/lt.po +++ b/src/translations/lt.po @@ -313,6 +313,10 @@ msgstr "" msgid "Behavior" msgstr "" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "" diff --git a/src/translations/nb.po b/src/translations/nb.po index 525fe4e52..deb968ffe 100644 --- a/src/translations/nb.po +++ b/src/translations/nb.po @@ -313,6 +313,10 @@ msgstr "Blå" msgid "Behavior" msgstr "" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bitrate" diff --git a/src/translations/nl.po b/src/translations/nl.po index c0aa1ce86..109b7ff5f 100644 --- a/src/translations/nl.po +++ b/src/translations/nl.po @@ -321,6 +321,10 @@ msgstr "Basic Blue" msgid "Behavior" msgstr "Gedrag" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bitsnelheid" diff --git a/src/translations/oc.po b/src/translations/oc.po index 2996e1995..d01ceb3e3 100644 --- a/src/translations/oc.po +++ b/src/translations/oc.po @@ -312,6 +312,10 @@ msgstr "Blau estandard" msgid "Behavior" msgstr "Compòrtament" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Debit binari" diff --git a/src/translations/pl.po b/src/translations/pl.po index d18531eb4..596df2111 100644 --- a/src/translations/pl.po +++ b/src/translations/pl.po @@ -321,6 +321,10 @@ msgstr "Prosty niebieski" msgid "Behavior" msgstr "Tryb" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bitrate" diff --git a/src/translations/pt.po b/src/translations/pt.po index 0953bc506..8e3df05fc 100644 --- a/src/translations/pt.po +++ b/src/translations/pt.po @@ -321,6 +321,10 @@ msgstr "Azul básico" msgid "Behavior" msgstr "Comportamento" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Taxa de \"bits\"" diff --git a/src/translations/pt_BR.po b/src/translations/pt_BR.po index 885b56a4c..78f997a46 100644 --- a/src/translations/pt_BR.po +++ b/src/translations/pt_BR.po @@ -318,6 +318,10 @@ msgstr "Azul básico" msgid "Behavior" msgstr "Comportamento" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Taxa de bits" diff --git a/src/translations/ro.po b/src/translations/ro.po index 297c81a1c..bfb1ca710 100644 --- a/src/translations/ro.po +++ b/src/translations/ro.po @@ -312,6 +312,10 @@ msgstr "" msgid "Behavior" msgstr "Comportament" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Rată de biți" diff --git a/src/translations/ru.po b/src/translations/ru.po index 38b23984c..58ef9bb34 100644 --- a/src/translations/ru.po +++ b/src/translations/ru.po @@ -316,6 +316,10 @@ msgstr "Стандартно голубой" msgid "Behavior" msgstr "Поведение" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Битрейт" diff --git a/src/translations/sk.po b/src/translations/sk.po index 9a99590a8..8edf80afd 100644 --- a/src/translations/sk.po +++ b/src/translations/sk.po @@ -318,6 +318,10 @@ msgstr "Základná modrá" msgid "Behavior" msgstr "Správanie" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bit rate" diff --git a/src/translations/sl.po b/src/translations/sl.po index 8fc922d48..97cd52f27 100644 --- a/src/translations/sl.po +++ b/src/translations/sl.po @@ -317,6 +317,10 @@ msgstr "Preprosta modra" msgid "Behavior" msgstr "Obnašanje" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bitna hitrost" diff --git a/src/translations/sr.po b/src/translations/sr.po index c3c292a0c..62fe1af59 100644 --- a/src/translations/sr.po +++ b/src/translations/sr.po @@ -313,6 +313,10 @@ msgstr "Основно плаво" msgid "Behavior" msgstr "Понашање" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Битски проток" diff --git a/src/translations/sv.po b/src/translations/sv.po index fe3b5f0d6..cecd6f81f 100644 --- a/src/translations/sv.po +++ b/src/translations/sv.po @@ -313,6 +313,10 @@ msgstr "Basblå" msgid "Behavior" msgstr "Beteende" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bithastighet" diff --git a/src/translations/tr.po b/src/translations/tr.po index 9c7c945f7..30f263a4d 100644 --- a/src/translations/tr.po +++ b/src/translations/tr.po @@ -317,6 +317,10 @@ msgstr "Temel Mavi" msgid "Behavior" msgstr "Davranış" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Bit oranı" diff --git a/src/translations/translations.pot b/src/translations/translations.pot index e13ccc60c..2df83a131 100644 --- a/src/translations/translations.pot +++ b/src/translations/translations.pot @@ -303,6 +303,10 @@ msgstr "" msgid "Behavior" msgstr "" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "" diff --git a/src/translations/uk.po b/src/translations/uk.po index cd932fff4..ab9bb956e 100644 --- a/src/translations/uk.po +++ b/src/translations/uk.po @@ -317,6 +317,10 @@ msgstr "Стандартний синій" msgid "Behavior" msgstr "Поведінка" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "Бітова швидкість" diff --git a/src/translations/zh_CN.po b/src/translations/zh_CN.po index 095642844..96de089f2 100644 --- a/src/translations/zh_CN.po +++ b/src/translations/zh_CN.po @@ -312,6 +312,10 @@ msgstr "" msgid "Behavior" msgstr "" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "比特率" diff --git a/src/translations/zh_TW.po b/src/translations/zh_TW.po index b697f2b40..70c4b38b9 100644 --- a/src/translations/zh_TW.po +++ b/src/translations/zh_TW.po @@ -317,6 +317,10 @@ msgstr "" msgid "Behavior" msgstr "行為" +#, qt-format +msgid "Biography from %1" +msgstr "" + msgid "Bit rate" msgstr "位元率" diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 2d89be831..d8336a3bd 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -54,6 +54,7 @@ #include "radio/radioview.h" #include "radio/radioviewcontainer.h" #include "radio/savedradio.h" +#include "songinfo/artistinfoview.h" #include "songinfo/lyricfetcher.h" #include "songinfo/lyricview.h" #include "transcoder/transcodedialog.h" @@ -142,6 +143,7 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg radio_view_(new RadioViewContainer(this)), device_view_(new DeviceView(this)), lyric_view_(new LyricView(this)), + artist_info_view_(new ArtistInfoView(this)), settings_dialog_(NULL), cover_manager_(NULL), equalizer_(new Equalizer), @@ -179,14 +181,14 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg ui_->volume->setValue(player_->GetVolume()); // Add tabs to the fancy tab widget - AddFancyTab(library_view_, IconLoader::Load("folder-sound"), tr("Library")); - AddFancyTab(file_view_, IconLoader::Load("document-open"), tr("Files")); - AddFancyTab(radio_view_, QIcon(":last.fm/icon_radio.png"), tr("Internet")); - AddFancyTab(device_view_, IconLoader::Load("multimedia-player-ipod-mini-blue"), tr("Devices")); + ui_->tabs->addTab(library_view_, IconLoader::Load("folder-sound"), tr("Library")); + ui_->tabs->addTab(file_view_, IconLoader::Load("document-open"), tr("Files")); + ui_->tabs->addTab(radio_view_, QIcon(":last.fm/icon_radio.png"), tr("Internet")); + ui_->tabs->addTab(device_view_, IconLoader::Load("multimedia-player-ipod-mini-blue"), tr("Devices")); ui_->tabs->addSpacer(); - AddFancyTab(lyric_view_, IconLoader::Load("view-media-lyrics"), tr("Lyrics")); - AddFancyTab(new QWidget, IconLoader::Load("view-media-lyrics"), tr("Song info")); - AddFancyTab(new QWidget, IconLoader::Load("view-media-lyrics"), tr("Artist info")); + ui_->tabs->addTab(lyric_view_, IconLoader::Load("view-media-lyrics"), tr("Lyrics")); + ui_->tabs->addTab(new QWidget, IconLoader::Load("view-media-lyrics"), tr("Song info")); + ui_->tabs->addTab(artist_info_view_, IconLoader::Load("view-media-lyrics"), tr("Artist info")); ui_->tabs->statusBar()->hide(); ui_->tabs->setBackgroundPixmap(QPixmap(":/sidebar_background.png")); @@ -491,6 +493,7 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg // Lyrics lyric_view_->set_network(network); ConnectInfoView(lyric_view_); + ConnectInfoView(artist_info_view_); // Analyzer ui_->analyzer->SetEngine(player_->GetEngine()); @@ -1579,10 +1582,6 @@ void MainWindow::ShowVisualisations() { #endif // ENABLE_VISUALISATIONS } -void MainWindow::AddFancyTab(QWidget* widget, const QIcon& icon, const QString& label) { - ui_->tabs->addTab(widget, icon, label); -} - void MainWindow::ConnectInfoView(SongInfoBase* view) { connect(playlists_, SIGNAL(CurrentSongChanged(Song)), view, SLOT(SongChanged(Song))); connect(player_, SIGNAL(PlaylistFinished()), view, SLOT(SongFinished())); diff --git a/src/ui/mainwindow.h b/src/ui/mainwindow.h index f847ec298..b44f0dfab 100644 --- a/src/ui/mainwindow.h +++ b/src/ui/mainwindow.h @@ -33,6 +33,7 @@ class About; class AddStreamDialog; class AlbumCoverManager; +class ArtistInfoView; class CommandlineOptions; class Database; class DeviceManager; @@ -196,7 +197,6 @@ class MainWindow : public QMainWindow, public PlatformInterface { void AddLibrarySongsToPlaylist(bool clear_first, const SongList& songs); void AddDeviceSongsToPlaylist(bool clear_first, const SongList& songs); void AddUrls(bool play_now, const QList& urls); - void AddFancyTab(QWidget* widget, const QIcon& icon, const QString& label); void ConnectInfoView(SongInfoBase* view); private: @@ -225,6 +225,7 @@ class MainWindow : public QMainWindow, public PlatformInterface { RadioViewContainer* radio_view_; DeviceView* device_view_; LyricView* lyric_view_; + ArtistInfoView* artist_info_view_; boost::scoped_ptr settings_dialog_; boost::scoped_ptr add_stream_dialog_;