1215 lines
54 KiB
C++
1215 lines
54 KiB
C++
/****************************************************************************************
|
|
* Copyright (c) 2010 Leo Franchi <lfranchi@kde.org> *
|
|
* *
|
|
* 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 <http://www.gnu.org/licenses/>. *
|
|
****************************************************************************************/
|
|
|
|
#include "Parsing_p.h"
|
|
|
|
#include "Artist.h"
|
|
#include "CatalogItem_p.h"
|
|
#include "Util.h"
|
|
|
|
// QJSon
|
|
#include <qjson/parser.h>
|
|
|
|
#include <QtNetwork/QNetworkReply>
|
|
#include <QDateTime>
|
|
#include <QStringBuilder>
|
|
|
|
void Echonest::Parser::checkForErrors( QNetworkReply* reply ) throw( Echonest::ParseError )
|
|
{
|
|
if( !reply )
|
|
throw ParseError( Echonest::UnknownError );
|
|
|
|
// TODO sometimes this returns false when it shouldn't be? what's going on..
|
|
// if( !reply->isFinished() )
|
|
// throw ParseError( Echonest::UnfinishedQuery );
|
|
//
|
|
if( reply->error() != QNetworkReply::NoError && reply->error() != QNetworkReply::UnknownContentError ) { // let UnknownContentError through so we parse it in readStatus with the proper error message
|
|
ParseError err( Echonest::NetworkError );
|
|
err.setNetworkError( reply->error() );
|
|
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
void Echonest::Parser::readStatus( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.readNextStartElement() ) {
|
|
// sanity checks
|
|
if( xml.atEnd() || xml.name() != QLatin1String( "response" ) )
|
|
throw ParseError( UnknownParseError );
|
|
|
|
if( xml.readNextStartElement() ) {
|
|
if( xml.atEnd() || xml.name() != "status" )
|
|
throw ParseError( UnknownParseError );
|
|
|
|
xml.readNextStartElement();
|
|
double version = xml.readElementText().toDouble();
|
|
// TODO use version for something?
|
|
Q_UNUSED(version);
|
|
xml.readNextStartElement();
|
|
Echonest::ErrorType code = static_cast< Echonest::ErrorType >( xml.readElementText().toInt() );
|
|
xml.readNextStartElement();
|
|
QString msg = xml.readElementText();
|
|
xml.readNextStartElement();
|
|
|
|
if( code != Echonest::NoError ) {
|
|
qDebug() << "Parse Error:" << code << msg;
|
|
throw ParseError( code, msg );
|
|
}
|
|
|
|
xml.readNext();
|
|
}
|
|
|
|
} else {
|
|
throw ParseError( UnknownParseError );
|
|
}
|
|
}
|
|
|
|
QVector< Echonest::Song > Echonest::Parser::parseSongList( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
QVector< Echonest::Song > songs;
|
|
|
|
xml.readNext();
|
|
while( !( xml.name() == "songs" && xml.tokenType() == QXmlStreamReader::EndElement ) ) {
|
|
// parse a song
|
|
songs.append( parseSong( xml ) );
|
|
}
|
|
return songs;
|
|
}
|
|
|
|
Echonest::Song Echonest::Parser::parseSong( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "song" )
|
|
throw 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() == "release" && xml.isStartElement() )
|
|
song.setRelease( 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() == "tracks" && xml.isStartElement() ) {
|
|
song.setTracks( parseSongTrackBucket( xml ) );
|
|
} else if( xml.name() == "artist_location" && xml.isStartElement() ) {
|
|
song.setArtistLocation( parseSongArtistLocation( xml ) );
|
|
} else if( xml.name() == "audio_summary" && xml.isStartElement() ) {
|
|
song.setAudioSummary( parseAudioSummary( xml ) );
|
|
}
|
|
xml.readNext();
|
|
}
|
|
xml.readNext(); // skip past the last </song>
|
|
|
|
return song;
|
|
}
|
|
|
|
|
|
Echonest::ArtistLocation Echonest::Parser::parseSongArtistLocation( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "artist_location" ) {
|
|
throw ParseError( Echonest::UnknownParseError );
|
|
}
|
|
/**
|
|
* while( !( xml.name() == "location" && xml.tokenType() == QXmlStreamReader::EndElement ) ) {
|
|
x ml.readNex*tStartElement();
|
|
if( xml.name() == "location" )
|
|
song.setArtistLocation( xml.readElementText() );
|
|
}
|
|
xml.readNext();
|
|
**/
|
|
Echonest::ArtistLocation location;
|
|
while( !( xml.name() == "artist_location" && xml.tokenType() == QXmlStreamReader::EndElement ) ) {
|
|
if( xml.name() == "latitude" && xml.isStartElement() ) {
|
|
location.latitude = xml.readElementText().toDouble();
|
|
} else if( xml.name() == "longitude" && xml.isStartElement() ) {
|
|
location.longitude = xml.readElementText().toDouble();
|
|
} else if( xml.name() == "location" && xml.isStartElement() ) {
|
|
location.location = xml.readElementText();
|
|
}
|
|
xml.readNext();
|
|
}
|
|
|
|
return location;
|
|
}
|
|
|
|
Echonest::Tracks Echonest::Parser::parseSongTrackBucket( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "tracks" ) {
|
|
throw ParseError( Echonest::UnknownParseError );
|
|
}
|
|
|
|
Echonest::Tracks tracks;
|
|
while( !( xml.name() == "tracks" && xml.tokenType() == QXmlStreamReader::EndElement ) && ( xml.name() != "track" || !xml.isEndElement() ) ) {
|
|
if( xml.name() == "track" && xml.isStartElement() ) {
|
|
Echonest::Track track = parseTrack( xml );
|
|
tracks.append( track );
|
|
} else
|
|
xml.readNext();
|
|
}
|
|
|
|
return tracks;
|
|
}
|
|
|
|
|
|
Echonest::Tracks Echonest::Parser::parseCatalogSongTracks( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "tracks" ) {
|
|
throw ParseError( Echonest::UnknownParseError );
|
|
}
|
|
|
|
Echonest::Tracks tracks;
|
|
while( !( xml.name() == "tracks" && xml.tokenType() == QXmlStreamReader::EndElement ) ) {
|
|
if( xml.name() == "track" && xml.isStartElement() ) {
|
|
tracks.append( Echonest::Track( xml.readElementText().toLatin1() ) );
|
|
}
|
|
xml.readNext();
|
|
}
|
|
|
|
return tracks;
|
|
}
|
|
|
|
Echonest::Track Echonest::Parser::parseTrack( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "track" ) {
|
|
throw 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() == "song_id" && xml.isStartElement() )
|
|
track.setSong( Echonest::Song( xml.readElementText().toLatin1() ) );
|
|
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() == "catalog" && xml.isStartElement() )
|
|
track.setCatalog( xml.readElementText() );
|
|
else if( xml.name() == "foreign_id" && xml.isStartElement() )
|
|
track.setForeignId( xml.readElementText().toLatin1() );
|
|
else if( xml.name() == "release_image" && xml.isStartElement() )
|
|
track.setReleaseImage( QUrl( xml.readElementText(), QUrl::TolerantMode ) );
|
|
else if( xml.name() == "preview_url" && xml.isStartElement() )
|
|
track.setPreviewUrl( QUrl( xml.readElementText(), QUrl::TolerantMode ) );
|
|
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.atEnd() || xml.name() != "audio_summary" ) {
|
|
throw 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( QUrl::fromEncoded( xml.readElementText().toUtf8(), QUrl::TolerantMode ) );
|
|
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() );
|
|
else if( xml.name() == "danceability" && xml.isStartElement() )
|
|
summary.setDanceability( xml.readElementText().toDouble() );
|
|
else if( xml.name() == "energy" && xml.isStartElement() )
|
|
summary.setEnergy( xml.readElementText().toDouble() );
|
|
|
|
xml.readNext();
|
|
}
|
|
|
|
return summary;
|
|
}
|
|
|
|
// extract confidence, duration, start out of a list of them. same code for bars, beats, sections, tatums
|
|
template< class T >
|
|
inline QVector< T > extractTripleTuple( const QVariantList& list ) {
|
|
QVector< T > tList;
|
|
tList.reserve( list.size() );
|
|
for( QVariantList::const_iterator iter = list.constBegin(); iter != list.constEnd(); ++iter ) {
|
|
T t;
|
|
QVariantMap tMap = iter->toMap();
|
|
t.confidence = tMap.value( QLatin1String( "confidence" ), -1 ).toReal();
|
|
t.duration = tMap.value( QLatin1String( "duration" ), -1 ).toReal();
|
|
t.start = tMap.value( QLatin1String( "start" ), -1 ).toReal();
|
|
|
|
tList.append( t );
|
|
}
|
|
// qDebug() << "Parsed simple list:" << tList.size();
|
|
return tList;
|
|
}
|
|
|
|
void Echonest::Parser::parseDetailedAudioSummary( QNetworkReply* reply, Echonest::AudioSummary& summary ) throw( ParseError )
|
|
{
|
|
QJson::Parser parser;
|
|
bool ok;
|
|
QVariant data = parser.parse( reply, &ok );
|
|
if( !ok ) {
|
|
qWarning() << "Failed to parse JSON data!" << parser.errorString();
|
|
throw ParseError( Echonest::UnknownParseError );
|
|
}
|
|
QVariantMap mainMap = data.toMap();
|
|
if( mainMap.contains( QLatin1String( "meta" ) ) ) {
|
|
QVariantMap metaMap = mainMap.value( QLatin1String( "meta" ) ).toMap();
|
|
summary.setAnalysisTime( metaMap.value( QLatin1String( "analysis_time" ), -1 ).toReal() );
|
|
summary.setAnalysisStatus( metaMap.value( QLatin1String( "status_code" ) ).toInt() );
|
|
summary.setDetailedStatus( metaMap.value( QLatin1String( "detailed_status" ) ).toString() );
|
|
summary.setAnalyzerVersion( metaMap.value( QLatin1String( "analyzer_version" ) ).toString() );
|
|
summary.setTimestamp( metaMap.value( QLatin1String( "analysis_time" ), -1 ).toReal() );
|
|
}
|
|
if( mainMap.contains( QLatin1String( "bars" ) ) ) {
|
|
QVariantList barList = mainMap.value( QLatin1String( "bars" ) ).toList();
|
|
summary.setBars( extractTripleTuple<Echonest::Bar>( barList ) );
|
|
}
|
|
if( mainMap.contains( QLatin1String( "beats" ) ) ) {
|
|
QVariantList beatList = mainMap.value( QLatin1String( "beats" ) ).toList();
|
|
summary.setBeats( extractTripleTuple<Echonest::Beat>( beatList ) );
|
|
}
|
|
if( mainMap.contains( QLatin1String( "sections" ) ) ) {
|
|
QVariantList sectionList = mainMap.value( QLatin1String( "sections" ) ).toList();
|
|
summary.setSections( extractTripleTuple<Echonest::Section>( sectionList ) );
|
|
}
|
|
if( mainMap.contains( QLatin1String( "segments" ) ) ) {
|
|
QVariantList segmentList = mainMap.value( QLatin1String( "segments" ) ).toList();
|
|
Echonest::SegmentList segments;
|
|
segments.reserve( segmentList.size() );
|
|
for( QVariantList::const_iterator iter = segmentList.constBegin(); iter != segmentList.constEnd(); ++iter ) {
|
|
Echonest::Segment segment;
|
|
QVariantMap segmentMap = iter->toMap();
|
|
segment.confidence = segmentMap.value( QLatin1String( "confidence" ), -1 ).toReal();
|
|
segment.duration = segmentMap.value( QLatin1String( "duration" ), -1 ).toReal();
|
|
segment.loudness_max = segmentMap.value( QLatin1String( "loudness_max" ), -1 ).toReal();
|
|
segment.loudness_max_time = segmentMap.value( QLatin1String( "loudness_max_time" ), -1 ).toReal();
|
|
segment.loudness_start = segmentMap.value( QLatin1String( "loudness_start" ), -1 ).toReal();
|
|
// pitches
|
|
QVariantList pitchesList = segmentMap.value( QLatin1String( "pitches" ) ).toList();
|
|
QVector< qreal > pitches;
|
|
pitches.reserve( pitchesList.size() );
|
|
for( QVariantList::const_iterator piter = pitchesList.constBegin(); piter != pitchesList.constEnd(); ++piter )
|
|
pitches.append( piter->toReal() );
|
|
segment.pitches = pitches;
|
|
segment.start = segmentMap.value( QLatin1String( "start" ), -1 ).toReal();
|
|
// timbre
|
|
QVariantList timbreList = segmentMap.value( QLatin1String( "timbre" ) ).toList();
|
|
QVector< qreal > timbres;
|
|
timbres.reserve( timbreList.size() );
|
|
for( QVariantList::const_iterator titer = timbreList.constBegin(); titer != timbreList.constEnd(); ++titer )
|
|
timbres.append( titer->toReal() );
|
|
segment.timbre = timbres;
|
|
segments.append( segment );
|
|
}
|
|
summary.setSegments( segments );
|
|
}
|
|
if( mainMap.contains( QLatin1String( "tatums" ) ) ) {
|
|
QVariantList tatumList = mainMap.value( QLatin1String( "tatums" ) ).toList();
|
|
summary.setTatums( extractTripleTuple<Echonest::Tatum>( tatumList ) );
|
|
}
|
|
if( mainMap.contains( QLatin1String( "track" ) ) ) {
|
|
QVariantMap trackMap = mainMap.value( QLatin1String( "track" ) ).toMap();
|
|
summary.setSampleRate( trackMap.value( QLatin1String( "analysis_sample_rate" ), -1 ).toReal() );
|
|
summary.setEndOfFadeIn( trackMap.value( QLatin1String( "end_of_fade_in" ), -1 ).toReal() );
|
|
summary.setKeyConfidence( trackMap.value( QLatin1String( "key_confidence" ), -1 ).toReal() );
|
|
summary.setModeConfidence( trackMap.value( QLatin1String( "mode_confidence" ), -1 ).toReal() );
|
|
summary.setNumSamples( trackMap.value( QLatin1String( "num_samples" ), -1 ).toLongLong() );
|
|
summary.setSampleMD5( trackMap.value( QLatin1String( "sample_md5" ) ).toString() );
|
|
summary.setStartOfFadeOut( trackMap.value( QLatin1String( "start_of_fade_out" ), -1 ).toReal() );
|
|
summary.setTempoConfidence( trackMap.value( QLatin1String( "tempo_confidence" ), -1 ).toReal() );
|
|
summary.setTimeSignatureConfidence( trackMap.value( QLatin1String( "time_signature_confidence" ), -1 ).toReal() );
|
|
}
|
|
}
|
|
|
|
|
|
Echonest::Artists Echonest::Parser::parseArtists( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
// we expect to be in an <artists> start element
|
|
if( xml.atEnd() || xml.name() != "artists" || !xml.isStartElement() )
|
|
throw ParseError( Echonest::UnknownParseError );
|
|
|
|
xml.readNextStartElement();
|
|
|
|
Echonest::Artists artists;
|
|
while( !xml.atEnd() && ( xml.name() != "artists" || !xml.isEndElement() ) ) {
|
|
if( xml.atEnd() || xml.name() != "artist" || !xml.isStartElement() )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
Echonest::Artist artist;
|
|
while( !xml.atEnd() && ( 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" && xml.isStartElement() ) {
|
|
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" ) {
|
|
parseForeignArtistIds( 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.atEnd() || xml.name() != "audio" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
xml.readNextStartElement();
|
|
Echonest::AudioList audioList;
|
|
while( !xml.atEnd() && ( 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.atEnd() && ( 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.atEnd() || xml.name() != "biographies" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
xml.readNextStartElement();
|
|
Echonest::BiographyList bios;
|
|
while( !xml.atEnd() && ( 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.atEnd() && ( 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.atEnd() || xml.name() != "images" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
xml.readNextStartElement();
|
|
Echonest::ArtistImageList imgs;
|
|
while( !xml.atEnd() && ( 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.atEnd() && ( 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.atEnd() || xml.name() != "news" || xml.tokenType() != QXmlStreamReader::StartElement ) )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
else if( !news && ( xml.atEnd() || xml.name() != "blogs" || xml.tokenType() != QXmlStreamReader::StartElement ) )
|
|
throw 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.atEnd() || xml.name() != "reviews" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
xml.readNextStartElement();
|
|
Echonest::ReviewList reviews;
|
|
while( !xml.atEnd() && ( 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.atEnd() && ( 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.atEnd() || xml.name() != "songs" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
xml.readNextStartElement();
|
|
Echonest::SongList songs;
|
|
while( !xml.atEnd() && ( xml.name() != "songs" || xml.tokenType() != QXmlStreamReader::EndElement ) ) {
|
|
if( xml.name() == "song" && xml.isStartElement() )
|
|
{
|
|
Echonest::Song song;
|
|
while( !xml.atEnd() && ( 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.atEnd() || xml.name() != "terms" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
artist.setTerms( parseTopTermList( xml ) );
|
|
}
|
|
|
|
Echonest::Artists Echonest::Parser::parseArtistSuggestList( QXmlStreamReader& xml ) throw( ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "artists" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
Echonest::Artists artists;
|
|
|
|
xml.readNextStartElement();
|
|
while( xml.name() != "artists" || !xml.isEndElement() ) {
|
|
QString name;
|
|
QByteArray id;
|
|
while( xml.name() != "artist" || !xml.isEndElement() ) {
|
|
if( xml.name() == "name" && xml.isStartElement() )
|
|
name = xml.readElementText();
|
|
else if( xml.name() == "id" && xml.isStartElement() )
|
|
id = xml.readElementText().toLatin1();
|
|
xml.readNext();
|
|
}
|
|
artists << Echonest::Artist( id, name );
|
|
xml.readNext();
|
|
}
|
|
return artists;
|
|
}
|
|
|
|
|
|
void Echonest::Parser::parseUrls( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "urls" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
xml.readNextStartElement();
|
|
// xml.readNextStartElement();
|
|
|
|
while( !xml.atEnd() && ( 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.atEnd() || xml.name() != "video" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
Echonest::VideoList videos;
|
|
while( xml.name() == "video" && xml.isStartElement() ) {
|
|
|
|
Echonest::Video video;
|
|
|
|
while( !xml.atEnd() && ( 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::parseTopTermList( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "terms" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
Echonest::TermList terms;
|
|
while( xml.name() == "terms" && xml.isStartElement() ) {
|
|
Echonest::Term term;
|
|
// qDebug() << "Parsing term outer item:" << xml.name() << xml.isStartElement();
|
|
while( !xml.atEnd() && ( 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 );
|
|
// qDebug() << "Parsing exernal term item:" << xml.name() << xml.isStartElement();
|
|
|
|
xml.readNext();
|
|
}
|
|
// qDebug() << " done Parsing terms:" << xml.name() << xml.isStartElement();
|
|
|
|
return terms;
|
|
}
|
|
|
|
|
|
QVector< QString > Echonest::Parser::parseTermList( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "terms" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
QVector< QString > terms;
|
|
while( xml.name() != "response" || !xml.isEndElement() ) {
|
|
if( xml.name() == "name" && xml.isStartElement() )
|
|
terms.append( xml.readElementText() );
|
|
xml.readNextStartElement();
|
|
}
|
|
|
|
return terms;
|
|
}
|
|
|
|
void Echonest::Parser::parseForeignArtistIds( QXmlStreamReader& xml, Echonest::Artist& artist ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "foreign_ids" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
Echonest::ForeignIds ids;
|
|
while( xml.name() != "foreign_ids" || !xml.isEndElement() ) {
|
|
xml.readNext();
|
|
xml.readNext(); // get past the enclosing <foreign_id>, or else we'll think it's the internal one.
|
|
Echonest::ForeignId id;
|
|
while( xml.name() != "foreign_id" || !xml.isEndElement() ) {
|
|
if( xml.name() == "catalog" && xml.isStartElement() )
|
|
id.catalog = xml.readElementText();
|
|
else if( xml.name() == "foreign_id" && xml.isStartElement() )
|
|
id.foreign_id = xml.readElementText();
|
|
|
|
xml.readNext();
|
|
}
|
|
ids.append( id );
|
|
xml.readNext();
|
|
}
|
|
artist.setForeignIds( ids );
|
|
}
|
|
|
|
Echonest::License Echonest::Parser::parseLicense( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "license" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
Echonest::License license;
|
|
while( !xml.atEnd() && ( 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( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "session_id" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
QByteArray sessionId = xml.readElementText().toLatin1();
|
|
xml.readNext(); //read to next start element
|
|
return sessionId;
|
|
}
|
|
|
|
// Catalogs parseCatalogList( QXmlStreamReader& xml ) throw( ParseError );
|
|
Echonest::Catalogs Echonest::Parser::parseCatalogList( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
int total = -1;
|
|
while( xml.name() != "response" && ( xml.name() != QLatin1String( "catalogs" ) || !xml.isStartElement() ) ) {
|
|
if( xml.name() == "total" && xml.isStartElement() )
|
|
total = xml.readElementText().toInt();
|
|
xml.readNextStartElement();
|
|
}
|
|
|
|
|
|
Echonest::Catalogs catalogs;
|
|
|
|
if( xml.name() != "catalogs" ) { // none
|
|
return catalogs;
|
|
}
|
|
|
|
catalogs.reserve( total );
|
|
// now we're pointing at the first catalog
|
|
while( xml.name() != "response" || !xml.isEndElement() )
|
|
catalogs.append( Echonest::Parser::parseCatalog( xml ) );
|
|
|
|
return catalogs;
|
|
}
|
|
|
|
Echonest::Catalog Echonest::Parser::parseCatalog( QXmlStreamReader& xml, bool justOne ) throw( Echonest::ParseError )
|
|
{
|
|
QString cName = justOne ? QLatin1String( "catalog" ) : QLatin1String( "catalogs" );
|
|
if( xml.atEnd() || xml.name() != cName || !xml.isStartElement() )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
xml.readNextStartElement();
|
|
|
|
Echonest::Catalog catalog;
|
|
while( xml.name() != cName || !xml.isEndElement() ) {
|
|
if( xml.name() == "total" && xml.isStartElement() )
|
|
catalog.setTotal( xml.readElementText().toInt() );
|
|
else if( xml.name() == "type" && xml.isStartElement() )
|
|
catalog.setType( Echonest::literalToCatalogType( xml.readElementText().toLatin1() ) );
|
|
else if( xml.name() == "id" && xml.isStartElement() )
|
|
catalog.setId( xml.readElementText().toLatin1() );
|
|
else if( xml.name() == "name" && xml.isStartElement() )
|
|
catalog.setName( xml.readElementText() );
|
|
else if( xml.name() == "items" && xml.isStartElement() ) {
|
|
QList<Echonest::CatalogItem*> items = parseCatalogItems( xml );
|
|
if( items.isEmpty() ) {
|
|
xml.readNextStartElement();
|
|
continue;
|
|
}
|
|
if( items[ 0 ]->type() == Echonest::CatalogTypes::Artist ) {
|
|
saveArtistList( catalog, items );
|
|
} else if( items[ 0 ]->type() == Echonest::CatalogTypes::Song ) {
|
|
saveSongList( catalog, items );
|
|
}
|
|
}
|
|
xml.readNextStartElement();
|
|
}
|
|
xml.readNext();
|
|
|
|
return catalog;
|
|
}
|
|
|
|
QList<Echonest::CatalogItem*> Echonest::Parser::parseCatalogItems( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "items" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
QList<Echonest::CatalogItem*> items;
|
|
while( xml.name() == "items" && xml.isStartElement() ) {
|
|
// OK, the mixture of the crappy Catalog API and strongly typed c++ makes this ugly. We don't know if this is an artist or song until we reach the artist_id or song_id.
|
|
// so, we'll keep two copies till the end, where we throw away one. :(
|
|
Echonest::CatalogArtist* artist = new Echonest::CatalogArtist;
|
|
Echonest::CatalogSong* song = new CatalogSong;
|
|
while( xml.name() != "items" || !xml.isEndElement() ) {
|
|
// OK, we have to check for all possible song+artist types :'(
|
|
if( xml.name() == "rating" && xml.isStartElement() ) { /// mixed and artist items
|
|
artist->setRating( xml.readElementText().toInt() );
|
|
song->setRating( artist->rating() );
|
|
} else if( xml.name() == "request" && xml.isStartElement() ) {
|
|
parseCatalogRequestItem( xml, *artist, *song );
|
|
} else if( xml.name() == "artist_name" && xml.isStartElement() ) {
|
|
artist->setName( xml.readElementText() );
|
|
song->setArtistName( artist->name() );
|
|
} else if( xml.name() == "reviews" && xml.isStartElement() ) {
|
|
parseReviews( xml, *artist );
|
|
} else if( xml.name() == "terms" && xml.isStartElement() ) {
|
|
parseTerms( xml, *artist );
|
|
continue;
|
|
} else if( xml.name() == "biographies" && xml.isStartElement() ) {
|
|
parseBiographies( xml, *artist );
|
|
} else if( xml.name() == "familiarity" && xml.isStartElement() ) {
|
|
artist->setFamiliarity( xml.readElementText().toDouble() );
|
|
song->setArtistFamiliarity( artist->familiarity() );
|
|
} else if( xml.name() == "blogs" && xml.isStartElement() ) {
|
|
parseNewsOrBlogs( xml, *artist, false );
|
|
} else if( xml.name() == "hotttnesss" && xml.isStartElement() ) {
|
|
artist->setHotttnesss( xml.readElementText().toDouble() );
|
|
song->setArtistHotttnesss( artist->hotttnesss() );
|
|
} else if( xml.name() == "video" && xml.isStartElement() ) {
|
|
parseVideos( xml, *artist );
|
|
} else if( xml.name() == "urls" && xml.isStartElement() ) {
|
|
parseUrls( xml, *artist );
|
|
} else if( xml.name() == "news" && xml.isStartElement() ) {
|
|
parseNewsOrBlogs( xml, *artist );
|
|
} else if( xml.name() == "images" && xml.isStartElement() ) {
|
|
parseImages( xml, *artist );
|
|
} else if( xml.name() == "date_added" && xml.isStartElement() ) {
|
|
artist->setDateAdded( QDateTime::fromString( xml.readElementText(), Qt::ISODate ) );
|
|
song->setDateAdded( artist->dateAdded() );
|
|
} else if( xml.name() == "artist_id" && xml.isStartElement() ) {
|
|
artist->setId( xml.readElementText().toLatin1() );
|
|
song->setArtistId( artist->id() );
|
|
} else if( xml.name() == "audio" && xml.isStartElement() ) {
|
|
parseAudio( xml, *artist );
|
|
} else if( xml.name() == "foreign_id" && xml.isStartElement() ) {
|
|
artist->setForeignId( xml.readElementText().toLatin1() );
|
|
song->setForeignId( artist->foreignId() );
|
|
} else if( xml.name() == "song_id" && xml.isStartElement() ) { /// song-specific entries
|
|
song->setId( xml.readElementText().toLatin1() );
|
|
} else if( xml.name() == "song_name" && xml.isStartElement() ) {
|
|
song->setTitle( xml.readElementText() );
|
|
} else if( xml.name() == "tracks" && xml.isStartElement() ) {
|
|
song->setTracks( parseCatalogSongTracks( xml ) );
|
|
} else if( xml.name() == "play_count" && xml.isStartElement() ) {
|
|
static_cast<Echonest::CatalogSong*>(song)->setPlayCount( xml.readElementText().toInt() );
|
|
} else if( xml.name() == "artist_hotttnesss" && xml.isStartElement() ) {
|
|
song->setArtistHotttnesss( xml.readElementText().toDouble() );
|
|
} else if( xml.name() == "artist_location" && xml.isStartElement() ) {
|
|
// TODO
|
|
} else if( xml.name() == "song_hotttnesss" && xml.isStartElement() ) {
|
|
song->setHotttnesss( xml.readElementText().toDouble() );
|
|
} else if( xml.name() == "artist_familiarity" && xml.isStartElement() ) {
|
|
song->setArtistFamiliarity( xml.readElementText().toDouble() );
|
|
} else if( xml.name() == "audio_summary" && xml.isStartElement() ) {
|
|
song->setAudioSummary( parseAudioSummary( xml ) );
|
|
}
|
|
xml.readNextStartElement();
|
|
}
|
|
if( !song->id().isEmpty() ) { // No song id, so it's an artist.
|
|
// qDebug() << "Adding a song";
|
|
items << song;
|
|
delete artist;
|
|
} else if( !artist->id().isEmpty() ) {
|
|
// qDebug() << "Adding an artist";
|
|
items << artist;
|
|
delete song;
|
|
} else { // dunno what this is really. lets use the song one for now
|
|
// qDebug() << "Adding an EMPTY";
|
|
items << song;
|
|
delete artist;
|
|
}
|
|
xml.readNext();
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
void Echonest::Parser::parseCatalogRequestItem( QXmlStreamReader& xml, Echonest::CatalogArtist& artist, Echonest::CatalogSong& song) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "request" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
Echonest::CatalogUpdateEntry request;
|
|
while( xml.name() != "request" || !xml.isEndElement() ) {
|
|
if( xml.name() == "item_id" ) {
|
|
request.setItemId( xml.readElementText().toLatin1() );
|
|
} else if( xml.name() == "artist_name" ) {
|
|
request.setArtistName( xml.readElementText() );
|
|
} else if( xml.name() == "song_name" ) {
|
|
request.setSongName( xml.readElementText() );
|
|
} else if( xml.name() == "fp_code" ) {
|
|
request.setFingerprint( xml.readElementText().toLatin1() );
|
|
} else if( xml.name() == "song_id" ) {
|
|
request.setSongId( xml.readElementText().toLatin1() );
|
|
} else if( xml.name() == "artist_id" ) {
|
|
request.setArtistId( xml.readElementText().toLatin1() );
|
|
} else if( xml.name() == "release" ) {
|
|
request.setRelease( xml.readElementText() );
|
|
} else if( xml.name() == "genre" ) {
|
|
request.setGenre( xml.readElementText() );
|
|
}
|
|
xml.readNext();
|
|
}
|
|
artist.setRequest( request );
|
|
song.setRequest( request );
|
|
}
|
|
|
|
void Echonest::Parser::saveArtistList( Echonest::Catalog& catalog, QList<Echonest::CatalogItem*>& artists)
|
|
{
|
|
// will copy artists into the catalog, and delete the origin
|
|
Echonest::CatalogArtists ca;
|
|
foreach( const Echonest::CatalogItem* item, artists ) {
|
|
ca.append( CatalogArtist( *static_cast<const CatalogArtist*>( item ) ) );
|
|
}
|
|
qDeleteAll( artists );
|
|
catalog.setArtists( ca );
|
|
}
|
|
|
|
void Echonest::Parser::saveSongList( Echonest::Catalog& catalog, QList<Echonest::CatalogItem*>& songs)
|
|
{
|
|
// will copy songs into the catalog, and delete the origin
|
|
Echonest::CatalogSongs ca;
|
|
foreach( const Echonest::CatalogItem* item, songs ) {
|
|
ca.append( CatalogSong( *static_cast<const CatalogSong*>( item ) ) );
|
|
}
|
|
qDeleteAll( songs );
|
|
catalog.setSongs( ca );
|
|
}
|
|
|
|
|
|
Echonest::CatalogStatus Echonest::Parser::parseCatalogStatus( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
Echonest::CatalogStatus status;
|
|
|
|
while( xml.name() != "response" || !xml.isEndElement() ) {
|
|
if( xml.name() == "ticket_status" && xml.isStartElement() )
|
|
status.status = Echonest::literalToCatalogStatus( xml.readElementText().toLatin1() );
|
|
else if( xml.name() == "details" && xml.isStartElement() )
|
|
status.details = xml.readElementText();
|
|
else if( xml.name() == "items_updated" && xml.isStartElement() )
|
|
status.items_updated = xml.readElementText().toInt();
|
|
else if( xml.name() == "update_info" && xml.isStartElement() )
|
|
status.items = parseTicketUpdateInfo( xml );
|
|
// else if( xml.name() == "percent_complete" && xml.isStartElement() )
|
|
// status.percent_complete = xml.readElementText().toInt();
|
|
|
|
xml.readNext();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
Echonest::CatalogStatusItem Echonest::Parser::parseTicketUpdateInfo( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
// if( xml.atEnd() || xml.name() != "ticket_status" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
// throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
// TODO
|
|
return Echonest::CatalogStatusItem();
|
|
}
|
|
|
|
QByteArray Echonest::Parser::parseCatalogTicket( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "ticket" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
QByteArray ticket= xml.readElementText().toLatin1();
|
|
return ticket;
|
|
}
|
|
|
|
Echonest::Catalog Echonest::Parser::parseNewCatalog( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
QString name;
|
|
QByteArray id;
|
|
Echonest::CatalogTypes::Type type = Echonest::CatalogTypes::Artist;
|
|
|
|
qDebug() << "Parsing new catalog...";
|
|
while( xml.name() != "response" || !xml.isEndElement() ) {
|
|
qDebug() << "Parsing at:" << xml.name().toString();
|
|
if( xml.name() == "name" && xml.isStartElement() )
|
|
name = xml.readElementText();
|
|
else if( xml.name() == "id" && xml.isStartElement() )
|
|
id = xml.readElementText().toLatin1();
|
|
else if( xml.name() == "type" && xml.isStartElement() )
|
|
type = Echonest::literalToCatalogType( xml.readElementText().toLatin1() );
|
|
|
|
xml.readNextStartElement();
|
|
qDebug() << "Parsing next at:" << xml.name().toString();
|
|
}
|
|
Echonest::Catalog c = Echonest::Catalog( id );
|
|
c.setName( name );
|
|
c.setType( type );
|
|
|
|
return c;
|
|
}
|
|
|
|
Echonest::SessionInfo Echonest::Parser::parseSessionInfo( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
Echonest::SessionInfo info;
|
|
|
|
while( xml.name() != "response" || !xml.isEndElement() ) {
|
|
// qDebug() << "Parsing part of session info:" << xml.name() << xml.isStartElement();
|
|
if( xml.name() == "terms" && xml.isStartElement() ) {
|
|
info.terms = parseTopTermList( xml );
|
|
continue;
|
|
} else if( xml.name() == "rules" && xml.isStartElement() ) {
|
|
info.rules = parseRulesList( xml );
|
|
continue;
|
|
} else if( xml.name() == "session_id" && xml.isStartElement() )
|
|
info.session_id = xml.readElementText().toLatin1();
|
|
else if( xml.name() == "seeds" && xml.isStartElement() )
|
|
info.seeds.append( Artist( xml.readElementText().toLatin1() ) );
|
|
else if( xml.name() == "banned_artists" && xml.isStartElement() )
|
|
info.banned_artists.append( Artist( xml.readElementText().toLatin1() ) );
|
|
else if( xml.name() == "seed_songs" && xml.isStartElement() )
|
|
info.seed_songs.append( Song( xml.readElementText().toLatin1() ) );
|
|
else if( xml.name() == "seed_catalog" && xml.isStartElement() )
|
|
info.seed_catalogs.append( Catalog( xml.readElementText().toLatin1() ) );
|
|
else if( xml.name() == "playlist_type" && xml.isStartElement() )
|
|
info.playlist_type = xml.readElementText();
|
|
else if( xml.name() == "skipped_songs" && xml.isStartElement() ) {
|
|
info.skipped_songs = parseSessionSongItem( xml, QLatin1String( "skipped_songs" ) );
|
|
continue;
|
|
} else if( xml.name() == "banned_songs" && xml.isStartElement() ) {
|
|
info.banned_songs = parseSessionSongItem( xml, QLatin1String( "banned_songs" ) );
|
|
continue;
|
|
} else if( xml.name() == "rated_songs" && xml.isStartElement() ) {
|
|
info.rated_songs = parseSessionSongItem( xml, QLatin1String( "rated_songs" ) );
|
|
continue;
|
|
} else if( xml.name() == "history" && xml.isStartElement() ) {
|
|
info.history = parseSessionSongItem( xml, QLatin1String( "history" ) );
|
|
continue;
|
|
}
|
|
xml.readNext();
|
|
}
|
|
return info;
|
|
}
|
|
|
|
|
|
QVector< QString > Echonest::Parser::parseRulesList( QXmlStreamReader& xml ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != "rules" || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
QVector< QString > rules;
|
|
while( xml.name() == "rules" && xml.isStartElement() ) {
|
|
// qDebug() << "Parsing start of rules:" << xml.name() << xml.isStartElement();
|
|
xml.readNextStartElement();
|
|
rules.append( xml.readElementText() );
|
|
xml.readNext();
|
|
xml.readNext();
|
|
// qDebug() << "Parsing end of rules:" << xml.name() << xml.isStartElement();
|
|
}
|
|
return rules;
|
|
}
|
|
|
|
QVector< Echonest::SessionItem > Echonest::Parser::parseSessionSongItem( QXmlStreamReader& xml, const QString& type ) throw( Echonest::ParseError )
|
|
{
|
|
if( xml.atEnd() || xml.name() != type || xml.tokenType() != QXmlStreamReader::StartElement )
|
|
throw Echonest::ParseError( Echonest::UnknownParseError );
|
|
|
|
QVector< Echonest::SessionItem > items;
|
|
while( xml.name() == type && xml.isStartElement() ) {
|
|
// qDebug() << "Parsing exernal item:" << xml.name() << xml.isStartElement();
|
|
|
|
Echonest::SessionItem item;
|
|
|
|
while( !xml.atEnd() && ( xml.name() != type || !xml.isEndElement() ) ) {
|
|
// qDebug() << "Parsing internal item:" << xml.name() << xml.isStartElement();
|
|
if( xml.name() == "served_time" )
|
|
item.served_time = xml.readElementText().toDouble();
|
|
else if( xml.name() == "artist_id" )
|
|
item.artist_id = xml.readElementText().toLatin1();
|
|
else if( xml.name() == "id" )
|
|
item.id = xml.readElementText().toLatin1();
|
|
else if( xml.name() == "artist_name" )
|
|
item.artist_name= xml.readElementText();
|
|
else if( xml.name() == "title" )
|
|
item.title = xml.readElementText();
|
|
else if( xml.name() == "rating" )
|
|
item.rating = xml.readElementText().toInt();
|
|
|
|
xml.readNextStartElement();
|
|
}
|
|
items.append( item );
|
|
|
|
xml.readNext();
|
|
}
|
|
return items;
|
|
}
|
|
|