lastfm: rework service architecture a bit and implement getting full artist info
This commit is contained in:
@@ -505,9 +505,6 @@
|
||||
# refetch_lyrics
|
||||
#
|
||||
#def_key "`"
|
||||
# refetch_artist_info
|
||||
#
|
||||
#def_key "`"
|
||||
# add_random_items
|
||||
#
|
||||
#def_key "ctrl_p"
|
||||
|
||||
@@ -2072,22 +2072,6 @@ void RefetchLyrics::run()
|
||||
# endif // HAVE_CURL_CURL_H
|
||||
}
|
||||
|
||||
bool RefetchArtistInfo::canBeRun() const
|
||||
{
|
||||
# ifdef HAVE_CURL_CURL_H
|
||||
return myScreen == myLastfm;
|
||||
# else
|
||||
return false;
|
||||
# endif // HAVE_CURL_CURL_H
|
||||
}
|
||||
|
||||
void RefetchArtistInfo::run()
|
||||
{
|
||||
# ifdef HAVE_CURL_CURL_H
|
||||
myLastfm->Refetch();
|
||||
# endif // HAVE_CURL_CURL_H
|
||||
}
|
||||
|
||||
bool SetSelectedItemsPriority::canBeRun() const
|
||||
{
|
||||
if (Mpd.Version() < 17)
|
||||
@@ -2161,7 +2145,7 @@ bool ShowArtistInfo::canBeRun() const
|
||||
void ShowArtistInfo::run()
|
||||
{
|
||||
# ifdef HAVE_CURL_CURL_H
|
||||
if (myScreen == myLastfm || myLastfm->isDownloading())
|
||||
if (myScreen == myLastfm)
|
||||
{
|
||||
myLastfm->switchTo();
|
||||
return;
|
||||
@@ -2181,8 +2165,11 @@ void ShowArtistInfo::run()
|
||||
artist = s->getArtist();
|
||||
}
|
||||
|
||||
if (!artist.empty() && myLastfm->SetArtistInfoArgs(artist, Config.lastfm_preferred_language))
|
||||
if (!artist.empty())
|
||||
{
|
||||
myLastfm->queueJob(LastFm::ArtistInfo(artist, Config.lastfm_preferred_language));
|
||||
myLastfm->switchTo();
|
||||
}
|
||||
# endif // HAVE_CURL_CURL_H
|
||||
}
|
||||
|
||||
@@ -2540,7 +2527,6 @@ void populateActions()
|
||||
insert_action(new Actions::ToggleLibraryTagType());
|
||||
insert_action(new Actions::ToggleMediaLibrarySortMode());
|
||||
insert_action(new Actions::RefetchLyrics());
|
||||
insert_action(new Actions::RefetchArtistInfo());
|
||||
insert_action(new Actions::SetSelectedItemsPriority());
|
||||
insert_action(new Actions::FilterPlaylistOnPriorities());
|
||||
insert_action(new Actions::ShowSongInfo());
|
||||
|
||||
@@ -942,15 +942,6 @@ protected:
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
struct RefetchArtistInfo : public BaseAction
|
||||
{
|
||||
RefetchArtistInfo() : BaseAction(Type::RefetchArtistInfo, "refetch_artist_info") { }
|
||||
|
||||
protected:
|
||||
virtual bool canBeRun() const;
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
struct SetSelectedItemsPriority : public BaseAction
|
||||
{
|
||||
SetSelectedItemsPriority()
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
#ifndef NCMPCPP_CURL_HANDLE_H
|
||||
#define NCMPCPP_CURL_HANDLE_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
|
||||
|
||||
155
src/lastfm.cpp
155
src/lastfm.cpp
@@ -22,27 +22,12 @@
|
||||
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
|
||||
#ifdef WIN32
|
||||
# include <io.h>
|
||||
#else
|
||||
# include <sys/stat.h>
|
||||
#endif // WIN32
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <boost/locale/conversion.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "error.h"
|
||||
#include "helpers.h"
|
||||
#include "charset.h"
|
||||
#include "global.h"
|
||||
#include "statusbar.h"
|
||||
#include "title.h"
|
||||
#include "screen_switcher.h"
|
||||
#include "utility/string.h"
|
||||
|
||||
using Global::MainHeight;
|
||||
using Global::MainStartY;
|
||||
@@ -51,7 +36,6 @@ Lastfm *myLastfm;
|
||||
|
||||
Lastfm::Lastfm()
|
||||
: Screen(NC::Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, NC::Border::None))
|
||||
, isReadyToTake(0), isDownloadInProgress(0)
|
||||
{ }
|
||||
|
||||
void Lastfm::resize()
|
||||
@@ -65,23 +49,13 @@ void Lastfm::resize()
|
||||
|
||||
std::wstring Lastfm::title()
|
||||
{
|
||||
return itsTitle;
|
||||
return m_title;
|
||||
}
|
||||
|
||||
void Lastfm::update()
|
||||
{
|
||||
if (isReadyToTake)
|
||||
Take();
|
||||
}
|
||||
|
||||
void Lastfm::Take()
|
||||
{
|
||||
assert(isReadyToTake);
|
||||
pthread_join(itsDownloader, 0);
|
||||
w.flush();
|
||||
w.refresh();
|
||||
isDownloadInProgress = 0;
|
||||
isReadyToTake = 0;
|
||||
if (m_worker.valid() && m_worker.is_ready())
|
||||
getResult();
|
||||
}
|
||||
|
||||
void Lastfm::switchTo()
|
||||
@@ -90,134 +64,27 @@ void Lastfm::switchTo()
|
||||
if (myScreen != this)
|
||||
{
|
||||
SwitchTo::execute(this);
|
||||
// get an old info if it waits
|
||||
if (isReadyToTake)
|
||||
Take();
|
||||
Load();
|
||||
drawHeader();
|
||||
}
|
||||
else
|
||||
switchToPreviousScreen();
|
||||
}
|
||||
|
||||
void Lastfm::Load()
|
||||
void Lastfm::getResult()
|
||||
{
|
||||
if (isDownloadInProgress)
|
||||
return;
|
||||
|
||||
assert(itsService.get());
|
||||
assert(itsService->checkArgs(itsArgs));
|
||||
|
||||
SetTitleAndFolder();
|
||||
|
||||
w.clear();
|
||||
w.reset();
|
||||
|
||||
std::string artist = itsArgs.find("artist")->second;
|
||||
std::string file = boost::locale::to_lower(artist + ".txt");
|
||||
removeInvalidCharsFromFilename(file, Config.generate_win32_compatible_filenames);
|
||||
|
||||
itsFilename = itsFolder + "/" + file;
|
||||
|
||||
mkdir(itsFolder.c_str()
|
||||
# ifndef WIN32
|
||||
, 0755
|
||||
# endif // !WIN32
|
||||
);
|
||||
|
||||
std::ifstream input(itsFilename.c_str());
|
||||
if (input.is_open())
|
||||
{
|
||||
bool first = 1;
|
||||
std::string line;
|
||||
while (std::getline(input, line))
|
||||
{
|
||||
if (!first)
|
||||
w << '\n';
|
||||
w << Charset::utf8ToLocale(line);
|
||||
first = 0;
|
||||
}
|
||||
input.close();
|
||||
itsService->colorizeOutput(w);
|
||||
}
|
||||
else
|
||||
{
|
||||
w << "Fetching informations... ";
|
||||
pthread_create(&itsDownloader, 0, DownloadWrapper, this);
|
||||
isDownloadInProgress = 1;
|
||||
}
|
||||
w.flush();
|
||||
}
|
||||
|
||||
void Lastfm::SetTitleAndFolder()
|
||||
{
|
||||
if (dynamic_cast<ArtistInfo *>(itsService.get()))
|
||||
{
|
||||
itsTitle = L"Artist info - ";
|
||||
itsTitle += ToWString(itsArgs.find("artist")->second);
|
||||
itsFolder = Config.ncmpcpp_directory + "artists";
|
||||
}
|
||||
}
|
||||
|
||||
void *Lastfm::DownloadWrapper(void *this_ptr)
|
||||
{
|
||||
static_cast<Lastfm *>(this_ptr)->Download();
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
void Lastfm::Download()
|
||||
{
|
||||
LastfmService::Result result = itsService->fetch(itsArgs);
|
||||
|
||||
auto result = m_worker.get();
|
||||
if (result.first)
|
||||
{
|
||||
Save(result.second);
|
||||
w.clear();
|
||||
w << Charset::utf8ToLocale(result.second);
|
||||
itsService->colorizeOutput(w);
|
||||
m_service->beautifyOutput(w);
|
||||
}
|
||||
else
|
||||
w << NC::Color::Red << result.second << NC::Color::End;
|
||||
|
||||
isReadyToTake = 1;
|
||||
}
|
||||
|
||||
void Lastfm::Save(const std::string &data)
|
||||
{
|
||||
std::ofstream output(itsFilename.c_str());
|
||||
if (output.is_open())
|
||||
{
|
||||
output << data;
|
||||
output.close();
|
||||
}
|
||||
else
|
||||
std::cerr << "ncmpcpp: couldn't save file \"" << itsFilename << "\"\n";
|
||||
}
|
||||
|
||||
void Lastfm::Refetch()
|
||||
{
|
||||
if (remove(itsFilename.c_str()) && errno != ENOENT)
|
||||
{
|
||||
const char msg[] = "Couldn't remove \"%ls\": %s";
|
||||
Statusbar::msg(msg, wideShorten(ToWString(itsFilename), COLS-const_strlen(msg)-25).c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
Load();
|
||||
}
|
||||
|
||||
bool Lastfm::SetArtistInfoArgs(const std::string &artist, const std::string &lang)
|
||||
{
|
||||
if (isDownloading())
|
||||
return false;
|
||||
|
||||
itsService.reset(new ArtistInfo);
|
||||
itsArgs.clear();
|
||||
itsArgs["artist"] = artist;
|
||||
if (!lang.empty())
|
||||
itsArgs["lang"] = lang;
|
||||
|
||||
return true;
|
||||
w << " " << NC::Color::Red << result.second << NC::Color::End;
|
||||
w.flush();
|
||||
w.refresh();
|
||||
// reset m_worker so it's no longer valid
|
||||
m_worker = boost::unique_future<LastFm::Service::Result>();
|
||||
}
|
||||
|
||||
#endif // HVAE_CURL_CURL_H
|
||||
|
||||
|
||||
53
src/lastfm.h
53
src/lastfm.h
@@ -26,11 +26,12 @@
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
|
||||
#include <memory>
|
||||
#include <pthread.h>
|
||||
#include <boost/thread/future.hpp>
|
||||
|
||||
#include "interfaces.h"
|
||||
#include "lastfm_service.h"
|
||||
#include "screen.h"
|
||||
#include "utility/wide_string.h"
|
||||
|
||||
struct Lastfm: Screen<NC::Scrollpad>, Tabbable
|
||||
{
|
||||
@@ -49,37 +50,39 @@ struct Lastfm: Screen<NC::Scrollpad>, Tabbable
|
||||
|
||||
virtual bool isMergable() OVERRIDE { return true; }
|
||||
|
||||
// private members
|
||||
void Refetch();
|
||||
|
||||
bool isDownloading() { return isDownloadInProgress && !isReadyToTake; }
|
||||
bool SetArtistInfoArgs(const std::string &artist, const std::string &lang = "");
|
||||
template <typename ServiceT>
|
||||
bool queueJob(ServiceT service)
|
||||
{
|
||||
auto old_service = dynamic_cast<ServiceT *>(m_service.get());
|
||||
// if the same service and arguments were used, leave old info
|
||||
if (old_service != nullptr && *old_service == service)
|
||||
return true;
|
||||
|
||||
// download in progress
|
||||
if (m_worker.valid() && !m_worker.is_ready())
|
||||
return false;
|
||||
|
||||
m_service = std::make_shared<ServiceT>(std::forward<ServiceT>(service));
|
||||
m_worker = boost::async(boost::launch::async, boost::bind(&LastFm::Service::fetch, m_service.get()));
|
||||
|
||||
w.clear();
|
||||
w << "Fetching information...";
|
||||
w.flush();
|
||||
m_title = ToWString(m_service->name());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool isLockable() OVERRIDE { return false; }
|
||||
|
||||
private:
|
||||
std::wstring itsTitle;
|
||||
void getResult();
|
||||
|
||||
std::string itsArtist;
|
||||
std::string itsFilename;
|
||||
std::wstring m_title;
|
||||
|
||||
std::string itsFolder;
|
||||
|
||||
std::auto_ptr<LastfmService> itsService;
|
||||
LastfmService::Args itsArgs;
|
||||
|
||||
void Load();
|
||||
void Save(const std::string &data);
|
||||
void SetTitleAndFolder();
|
||||
|
||||
void Download();
|
||||
static void *DownloadWrapper(void *);
|
||||
|
||||
void Take();
|
||||
bool isReadyToTake;
|
||||
bool isDownloadInProgress;
|
||||
pthread_t itsDownloader;
|
||||
std::shared_ptr<LastFm::Service> m_service;
|
||||
boost::unique_future<LastFm::Service::Result> m_worker;
|
||||
};
|
||||
|
||||
extern Lastfm *myLastfm;
|
||||
|
||||
@@ -23,152 +23,190 @@
|
||||
#ifdef HAVE_CURL_CURL_H
|
||||
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/locale/conversion.hpp>
|
||||
#include <fstream>
|
||||
#include "charset.h"
|
||||
#include "curl_handle.h"
|
||||
#include "settings.h"
|
||||
#include "utility/html.h"
|
||||
#include "utility/string.h"
|
||||
|
||||
const char *LastfmService::baseURL = "http://ws.audioscrobbler.com/2.0/?api_key=d94e5b6e26469a2d1ffae8ef20131b79&method=";
|
||||
namespace {
|
||||
|
||||
const char *LastfmService::msgParseFailed = "Fetched data could not be parsed";
|
||||
const char *apiUrl = "http://ws.audioscrobbler.com/2.0/?api_key=d94e5b6e26469a2d1ffae8ef20131b79&method=";
|
||||
const char *msgInvalidResponse = "Invalid response";
|
||||
|
||||
LastfmService::Result LastfmService::fetch(Args &args)
|
||||
}
|
||||
|
||||
namespace LastFm {
|
||||
|
||||
Service::Result Service::fetch()
|
||||
{
|
||||
Result result;
|
||||
result.first = false;
|
||||
|
||||
std::string url = baseURL;
|
||||
std::string url = apiUrl;
|
||||
url += methodName();
|
||||
for (Args::const_iterator it = args.begin(); it != args.end(); ++it)
|
||||
for (auto &arg : m_arguments)
|
||||
{
|
||||
url += "&";
|
||||
url += it->first;
|
||||
url += arg.first;
|
||||
url += "=";
|
||||
url += Curl::escape(it->second);
|
||||
url += Curl::escape(arg.second);
|
||||
}
|
||||
|
||||
std::string data;
|
||||
CURLcode code = Curl::perform(data, url);
|
||||
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
result.second = curl_easy_strerror(code);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (actionFailed(data))
|
||||
{
|
||||
stripHtmlTags(data);
|
||||
result.second = data;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!parse(data))
|
||||
else if (actionFailed(data))
|
||||
result.second = msgInvalidResponse;
|
||||
else
|
||||
{
|
||||
result = processData(data);
|
||||
|
||||
// if relevant part of data was not found and one of arguments
|
||||
// was language, try to fetch it again without that parameter.
|
||||
// otherwise just report failure.
|
||||
Args::iterator lang = args.find("lang");
|
||||
if (lang != args.end())
|
||||
if (!result.first && !m_arguments["lang"].empty())
|
||||
{
|
||||
args.erase(lang);
|
||||
return fetch(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
// parse should change data to error msg, if it fails
|
||||
result.second = data;
|
||||
return result;
|
||||
m_arguments.erase("lang");
|
||||
result = fetch();
|
||||
}
|
||||
}
|
||||
|
||||
result.first = true;
|
||||
result.second = data;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LastfmService::actionFailed(const std::string &data)
|
||||
bool Service::actionFailed(const std::string &data)
|
||||
{
|
||||
return data.find("status=\"failed\"") != std::string::npos;
|
||||
}
|
||||
|
||||
void LastfmService::postProcess(std::string &data)
|
||||
{
|
||||
stripHtmlTags(data);
|
||||
boost::trim(data);
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
bool ArtistInfo::checkArgs(const Args &args)
|
||||
bool ArtistInfo::argumentsOk()
|
||||
{
|
||||
return args.find("artist") != args.end();
|
||||
return !m_arguments["artist"].empty();
|
||||
}
|
||||
|
||||
void ArtistInfo::colorizeOutput(NC::Scrollpad &w)
|
||||
void ArtistInfo::beautifyOutput(NC::Scrollpad &w)
|
||||
{
|
||||
w.setProperties(NC::Format::Bold, "\n\nSimilar artists:\n", NC::Format::NoBold, 0, boost::regex::literal);
|
||||
w.setProperties(NC::Format::Bold, "\n\nSimilar artists:\n", NC::Format::NoBold, 0);
|
||||
w.setProperties(NC::Format::Bold, "\n\nSimilar tags:\n", NC::Format::NoBold, 0);
|
||||
w.setProperties(Config.color2, "\n * ", NC::Color::End, 0, boost::regex::literal);
|
||||
}
|
||||
|
||||
bool ArtistInfo::parse(std::string &data)
|
||||
Service::Result ArtistInfo::processData(const std::string &data)
|
||||
{
|
||||
size_t a, b;
|
||||
bool parse_failed = false;
|
||||
Service::Result result;
|
||||
result.first = false;
|
||||
|
||||
if ((a = data.find("<content>")) != std::string::npos)
|
||||
boost::regex rx("<content>(.*?)</content>");
|
||||
boost::smatch what;
|
||||
if (boost::regex_search(data, what, rx))
|
||||
{
|
||||
a += const_strlen("<content>");
|
||||
if ((b = data.find("</content>")) == std::string::npos)
|
||||
parse_failed = true;
|
||||
std::string desc = what[1];
|
||||
// if there is a description...
|
||||
if (desc.length() > 0)
|
||||
{
|
||||
// ...locate the link to wiki on last.fm...
|
||||
rx.assign("<link rel=\"original\" href=\"(.*?)\"");
|
||||
if (boost::regex_search(data, what, rx))
|
||||
{
|
||||
// ...try to get the content of it...
|
||||
std::string wiki;
|
||||
CURLcode code = Curl::perform(wiki, what[1]);
|
||||
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
result.second = curl_easy_strerror(code);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...and filter it to get the whole description.
|
||||
rx.assign("<div id=\"wiki\">(.*?)</div>");
|
||||
if (boost::regex_search(wiki, what, rx))
|
||||
desc = unescapeHtmlUtf8(what[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, get rid of CDATA wrapper.
|
||||
rx.assign("<!\\[CDATA\\[(.*)\\]\\]>");
|
||||
desc = boost::regex_replace(desc, rx, "\\1");
|
||||
}
|
||||
stripHtmlTags(desc);
|
||||
boost::trim(desc);
|
||||
result.second += desc;
|
||||
}
|
||||
else
|
||||
result.second += "No description available for this artist.";
|
||||
}
|
||||
else
|
||||
parse_failed = true;
|
||||
|
||||
if (parse_failed)
|
||||
{
|
||||
data = msgParseFailed;
|
||||
return false;
|
||||
result.second = msgInvalidResponse;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (a == b)
|
||||
auto add_similars = [&result](boost::sregex_iterator &it, const boost::sregex_iterator &last) {
|
||||
for (; it != last; ++it)
|
||||
{
|
||||
std::string name = it->str(1);
|
||||
std::string url = it->str(2);
|
||||
stripHtmlTags(name);
|
||||
stripHtmlTags(url);
|
||||
result.second += "\n * ";
|
||||
result.second += name;
|
||||
result.second += " (";
|
||||
result.second += url;
|
||||
result.second += ")";
|
||||
}
|
||||
};
|
||||
|
||||
a = data.find("<similar>");
|
||||
b = data.find("</similar>");
|
||||
if (a != std::string::npos && b != std::string::npos)
|
||||
{
|
||||
data = "No description available for this artist.";
|
||||
return false;
|
||||
rx.assign("<artist>.*?<name>(.*?)</name>.*?<url>(.*?)</url>.*?</artist>");
|
||||
auto it = boost::sregex_iterator(data.begin()+a, data.begin()+b, rx);
|
||||
auto last = boost::sregex_iterator();
|
||||
if (it != last)
|
||||
result.second += "\n\nSimilar artists:\n";
|
||||
add_similars(it, last);
|
||||
}
|
||||
|
||||
std::vector< std::pair<std::string, std::string> > similars;
|
||||
for (size_t i = data.find("<name>"), j, k = data.find("<url>"), l;
|
||||
i != std::string::npos; i = data.find("<name>", i), k = data.find("<url>", k))
|
||||
a = data.find("<tags>");
|
||||
b = data.find("</tags>");
|
||||
if (a != std::string::npos && b != std::string::npos)
|
||||
{
|
||||
j = data.find("</name>", i);
|
||||
i += const_strlen("<name>");
|
||||
|
||||
l = data.find("</url>", k);
|
||||
k += const_strlen("<url>");
|
||||
|
||||
similars.push_back(std::make_pair(data.substr(i, j-i), data.substr(k, l-k)));
|
||||
stripHtmlTags(similars.back().first);
|
||||
rx.assign("<tag>.*?<name>(.*?)</name>.*?<url>(.*?)</url>.*?</tag>");
|
||||
auto it = boost::sregex_iterator(data.begin()+a, data.begin()+b, rx);
|
||||
auto last = boost::sregex_iterator();
|
||||
if (it != last)
|
||||
result.second += "\n\nSimilar tags:\n";
|
||||
add_similars(it, last);
|
||||
}
|
||||
|
||||
a += const_strlen("<![CDATA[");
|
||||
b -= const_strlen("]]>");
|
||||
data = data.substr(a, b-a);
|
||||
// get artist we look for, it's the one before similar artists
|
||||
rx.assign("<name>.*?</name>.*?<url>(.*?)</url>.*?<similar>");
|
||||
|
||||
postProcess(data);
|
||||
|
||||
data += "\n\nSimilar artists:\n";
|
||||
for (size_t i = 1; i < similars.size(); ++i)
|
||||
if (boost::regex_search(data, what, rx))
|
||||
{
|
||||
data += "\n * ";
|
||||
data += similars[i].first;
|
||||
data += " (";
|
||||
data += similars[i].second;
|
||||
data += ")";
|
||||
std::string url = what[1];
|
||||
stripHtmlTags(url);
|
||||
result.second += "\n\n";
|
||||
// add only url
|
||||
result.second += url;
|
||||
}
|
||||
data += "\n\n";
|
||||
data += similars.front().second;
|
||||
|
||||
return true;
|
||||
result.first = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,42 +30,51 @@
|
||||
|
||||
#include "scrollpad.h"
|
||||
|
||||
struct LastfmService
|
||||
namespace LastFm {
|
||||
|
||||
struct Service
|
||||
{
|
||||
typedef std::map<std::string, std::string> Args;
|
||||
typedef std::map<std::string, std::string> Arguments;
|
||||
typedef std::pair<bool, std::string> Result;
|
||||
|
||||
virtual const char *name() = 0;
|
||||
virtual Result fetch(Args &args);
|
||||
Service(Arguments args) : m_arguments(args) { }
|
||||
|
||||
virtual bool checkArgs(const Args &args) = 0;
|
||||
virtual void colorizeOutput(NC::Scrollpad &w) = 0;
|
||||
virtual const char *name() = 0;
|
||||
virtual Result fetch();
|
||||
|
||||
virtual void beautifyOutput(NC::Scrollpad &w) = 0;
|
||||
|
||||
protected:
|
||||
virtual bool argumentsOk() = 0;
|
||||
virtual bool actionFailed(const std::string &data);
|
||||
|
||||
virtual bool parse(std::string &data) = 0;
|
||||
virtual void postProcess(std::string &data);
|
||||
virtual Result processData(const std::string &data) = 0;
|
||||
|
||||
virtual const char *methodName() = 0;
|
||||
|
||||
static const char *baseURL;
|
||||
static const char *msgParseFailed;
|
||||
Arguments m_arguments;
|
||||
};
|
||||
|
||||
struct ArtistInfo : public LastfmService
|
||||
struct ArtistInfo : public Service
|
||||
{
|
||||
ArtistInfo(std::string artist, std::string lang)
|
||||
: Service({{"artist", artist}, {"lang", lang}}) { }
|
||||
|
||||
virtual const char *name() { return "Artist info"; }
|
||||
|
||||
virtual bool checkArgs(const Args &args);
|
||||
virtual void colorizeOutput(NC::Scrollpad &w);
|
||||
virtual void beautifyOutput(NC::Scrollpad &w);
|
||||
|
||||
bool operator==(const ArtistInfo &ai) const { return m_arguments == ai.m_arguments; }
|
||||
|
||||
protected:
|
||||
virtual bool parse(std::string &data);
|
||||
virtual bool argumentsOk();
|
||||
virtual Result processData(const std::string &data);
|
||||
|
||||
virtual const char *methodName() { return "artist.getinfo"; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HAVE_CURL_CURL_H
|
||||
|
||||
#endif // NCMPCPP_LASTFM_SERVICE_H
|
||||
|
||||
Reference in New Issue
Block a user