scrollpad: explicitely include iostream
[ncmpcpp.git] / src / lastfm_service.cpp
blob9eec5e508fecc1d4d25e3276f1729d25d704526c
1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include "lastfm_service.h"
23 #ifdef HAVE_CURL_CURL_H
25 #include <boost/algorithm/string/trim.hpp>
26 #include <boost/locale/conversion.hpp>
27 #include <fstream>
28 #include "charset.h"
29 #include "curl_handle.h"
30 #include "settings.h"
31 #include "utility/html.h"
32 #include "utility/string.h"
34 namespace {
36 const char *apiUrl = "http://ws.audioscrobbler.com/2.0/?api_key=d94e5b6e26469a2d1ffae8ef20131b79&method=";
37 const char *msgInvalidResponse = "Invalid response";
41 namespace LastFm {
43 Service::Result Service::fetch()
45 Result result;
46 result.first = false;
48 std::string url = apiUrl;
49 url += methodName();
50 for (auto &arg : m_arguments)
52 url += "&";
53 url += arg.first;
54 url += "=";
55 url += Curl::escape(arg.second);
58 std::string data;
59 CURLcode code = Curl::perform(data, url);
61 if (code != CURLE_OK)
62 result.second = curl_easy_strerror(code);
63 else if (actionFailed(data))
64 result.second = msgInvalidResponse;
65 else
67 result = processData(data);
69 // if relevant part of data was not found and one of arguments
70 // was language, try to fetch it again without that parameter.
71 // otherwise just report failure.
72 if (!result.first && !m_arguments["lang"].empty())
74 m_arguments.erase("lang");
75 result = fetch();
79 return result;
82 bool Service::actionFailed(const std::string &data)
84 return data.find("status=\"failed\"") != std::string::npos;
87 /***********************************************************************/
89 bool ArtistInfo::argumentsOk()
91 return !m_arguments["artist"].empty();
94 void ArtistInfo::beautifyOutput(NC::Scrollpad &w)
96 w.setProperties(NC::Format::Bold, "\n\nSimilar artists:\n", NC::Format::NoBold, 0);
97 w.setProperties(NC::Format::Bold, "\n\nSimilar tags:\n", NC::Format::NoBold, 0);
98 w.setProperties(Config.color2, "\n * ", NC::Color::End, 0, boost::regex::literal);
101 Service::Result ArtistInfo::processData(const std::string &data)
103 size_t a, b;
104 Service::Result result;
105 result.first = false;
107 boost::regex rx("<content>(.*?)</content>");
108 boost::smatch what;
109 if (boost::regex_search(data, what, rx))
111 std::string desc = what[1];
112 // if there is a description...
113 if (desc.length() > 0)
115 // ...locate the link to wiki on last.fm...
116 rx.assign("<link rel=\"original\" href=\"(.*?)\"");
117 if (boost::regex_search(data, what, rx))
119 // ...try to get the content of it...
120 std::string wiki;
121 CURLcode code = Curl::perform(wiki, what[1]);
123 if (code != CURLE_OK)
125 result.second = curl_easy_strerror(code);
126 return result;
128 else
130 // ...and filter it to get the whole description.
131 rx.assign("<div id=\"wiki\">(.*?)</div>");
132 if (boost::regex_search(wiki, what, rx))
133 desc = unescapeHtmlUtf8(what[1]);
136 else
138 // otherwise, get rid of CDATA wrapper.
139 rx.assign("<!\\[CDATA\\[(.*)\\]\\]>");
140 desc = boost::regex_replace(desc, rx, "\\1");
142 stripHtmlTags(desc);
143 boost::trim(desc);
144 result.second += desc;
146 else
147 result.second += "No description available for this artist.";
149 else
151 result.second = msgInvalidResponse;
152 return result;
155 auto add_similars = [&result](boost::sregex_iterator &it, const boost::sregex_iterator &last) {
156 for (; it != last; ++it)
158 std::string name = it->str(1);
159 std::string url = it->str(2);
160 stripHtmlTags(name);
161 stripHtmlTags(url);
162 result.second += "\n * ";
163 result.second += name;
164 result.second += " (";
165 result.second += url;
166 result.second += ")";
170 a = data.find("<similar>");
171 b = data.find("</similar>");
172 if (a != std::string::npos && b != std::string::npos)
174 rx.assign("<artist>.*?<name>(.*?)</name>.*?<url>(.*?)</url>.*?</artist>");
175 auto it = boost::sregex_iterator(data.begin()+a, data.begin()+b, rx);
176 auto last = boost::sregex_iterator();
177 if (it != last)
178 result.second += "\n\nSimilar artists:\n";
179 add_similars(it, last);
182 a = data.find("<tags>");
183 b = data.find("</tags>");
184 if (a != std::string::npos && b != std::string::npos)
186 rx.assign("<tag>.*?<name>(.*?)</name>.*?<url>(.*?)</url>.*?</tag>");
187 auto it = boost::sregex_iterator(data.begin()+a, data.begin()+b, rx);
188 auto last = boost::sregex_iterator();
189 if (it != last)
190 result.second += "\n\nSimilar tags:\n";
191 add_similars(it, last);
194 // get artist we look for, it's the one before similar artists
195 rx.assign("<name>.*?</name>.*?<url>(.*?)</url>.*?<similar>");
197 if (boost::regex_search(data, what, rx))
199 std::string url = what[1];
200 stripHtmlTags(url);
201 result.second += "\n\n";
202 // add only url
203 result.second += url;
206 result.first = true;
207 return result;
212 #endif