/**************************************************************************************** * 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 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 ) { 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 ); // 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 ParseError( code ); } 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() == "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.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() == "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.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( 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.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" ) { 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.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( parseTermList( xml ) ); } 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::parseTermList( QXmlStreamReader& xml ) { 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; 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 ); 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.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( 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; }