From a2862b9fdf19297fbae494d797e68dc9f09be28f Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 16 Nov 2016 09:12:06 +0100 Subject: [PATCH] Make the list of lyrics fetchers customizable --- NEWS | 1 + doc/config | 2 + doc/ncmpcpp.1 | 3 ++ src/lyrics.cpp | 93 ++++++++++++++++++++++++++++-------------- src/lyrics.h | 2 +- src/lyrics_fetcher.cpp | 33 +++++++++------ src/lyrics_fetcher.h | 13 ++++-- src/settings.cpp | 17 ++++++++ src/settings.h | 3 ++ 9 files changed, 119 insertions(+), 48 deletions(-) diff --git a/NEWS b/NEWS index 11772a4e..286c89d0 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ ncmpcpp-0.8 (????-??-??) * Lyrics from files containing DOS line endings now load properly on Linux. * Added support for fetching lyrics from genius.com. * Added support for fetching lyrics from tekstowo.pl. +* The list of lyrics fetchers can now be set via configuration file. ncmpcpp-0.7.7 (2016-10-31) * Fixed compilation on 32bit platforms. diff --git a/doc/config b/doc/config index d325df5d..c41d4729 100644 --- a/doc/config +++ b/doc/config @@ -368,6 +368,8 @@ # #lines_scrolled = 2 # +#lyrics_fetchers = lyricwiki, azlyrics, genius, sing365, lyricsmania, metrolyrics, justsomelyrics, tekstowo, internet +# #follow_now_playing_lyrics = no # #fetch_lyrics_for_current_song_in_background = no diff --git a/doc/ncmpcpp.1 b/doc/ncmpcpp.1 index cef49a64..89ae11fa 100644 --- a/doc/ncmpcpp.1 +++ b/doc/ncmpcpp.1 @@ -244,6 +244,9 @@ If enabled, cyclic scrolling is used (e.g. if you press down arrow being at the .B lines_scrolled = NUMBER Number of lines that are scrolled with mouse wheel. .TP +.B lyrics_fetchers = FETCHERS +Comma separated list of lyrics fetchers. +.TP .B follow_now_playing_lyrics = yes/no If enabled, lyrics will be switched at song's change to currently playing one's (Note: this works only if you are viewing lyrics of item from Playlist). .TP diff --git a/src/lyrics.cpp b/src/lyrics.cpp index c0ffc417..46d4ed71 100644 --- a/src/lyrics.cpp +++ b/src/lyrics.cpp @@ -43,7 +43,7 @@ using Global::MainHeight; using Global::MainStartY; #ifdef HAVE_CURL_CURL_H -LyricsFetcher **Lyrics::itsFetcher = 0; +LyricsFetcher *Lyrics::itsFetcher = nullptr; std::queue Lyrics::itsToDownload; pthread_mutex_t Lyrics::itsDIBLock = PTHREAD_MUTEX_INITIALIZER; size_t Lyrics::itsWorkersNumber = 0; @@ -207,16 +207,20 @@ void Lyrics::DownloadInBackgroundImplHelper(const MPD::Song &s) std::string title = Curl::escape(s.getTitle()); LyricsFetcher::Result result; - bool fetcher_defined = itsFetcher && *itsFetcher; - for (LyricsFetcher **plugin = fetcher_defined ? itsFetcher : lyricsPlugins; *plugin != 0; ++plugin) + + if (itsFetcher == nullptr) { - result = (*plugin)->fetch(artist, title); - if (result.first) - break; - if (fetcher_defined) - break; + for (auto &fetcher : Config.lyrics_fetchers) + { + result = fetcher->fetch(artist, title); + if (result.first) + break; + } } - if (result.first == true) + else + itsFetcher->fetch(artist, title); + + if (result.first) Save(GenerateFilename(s), result.second); } @@ -224,25 +228,38 @@ void *Lyrics::Download() { std::string artist = Curl::escape(itsSong.getArtist()); std::string title_ = Curl::escape(itsSong.getTitle()); - + + auto fetch_lyrics = [&](auto &fetcher) { + w << "Fetching lyrics from " + << NC::Format::Bold + << fetcher->name() + << NC::Format::NoBold << "... "; + auto result = fetcher->fetch(artist, title_); + if (result.first == false) + { + w << NC::Color::Red + << result.second + << NC::Color::End + << '\n'; + } + return result; + }; + LyricsFetcher::Result result; - - // if one of plugins is selected, try only this one, - // otherwise try all of them until one of them succeeds - bool fetcher_defined = itsFetcher && *itsFetcher; - for (LyricsFetcher **plugin = fetcher_defined ? itsFetcher : lyricsPlugins; *plugin != 0; ++plugin) + + if (itsFetcher == nullptr) { - w << "Fetching lyrics from " << NC::Format::Bold << (*plugin)->name() << NC::Format::NoBold << "... "; - result = (*plugin)->fetch(artist, title_); - if (result.first == false) - w << NC::Color::Red << result.second << NC::Color::End << '\n'; - else - break; - if (fetcher_defined) - break; + for (auto &fetcher : Config.lyrics_fetchers) + { + result = fetch_lyrics(fetcher); + if (result.first) + break; + } } - - if (result.first == true) + else + result = fetch_lyrics(itsFetcher); + + if (result.first) { Save(itsFilename, result.second); w.clear(); @@ -401,14 +418,28 @@ void Lyrics::Refetch() void Lyrics::ToggleFetcher() { - if (itsFetcher && *itsFetcher) - ++itsFetcher; + if (itsFetcher != nullptr) + { + auto fetcher = std::find_if(Config.lyrics_fetchers.begin(), + Config.lyrics_fetchers.end(), + [](auto &f) { return f.get() == itsFetcher; }); + assert(fetcher != Config.lyrics_fetchers.end()); + ++fetcher; + if (fetcher != Config.lyrics_fetchers.end()) + itsFetcher = fetcher->get(); + else + itsFetcher = nullptr; + } else - itsFetcher = &lyricsPlugins[0]; - if (*itsFetcher) - Statusbar::printf("Using lyrics database: %s", (*itsFetcher)->name()); + { + assert(!Config.lyrics_fetchers.empty()); + itsFetcher = Config.lyrics_fetchers[0].get(); + } + + if (itsFetcher != nullptr) + Statusbar::printf("Using lyrics fetcher: %s", itsFetcher->name()); else - Statusbar::print("Using all lyrics databases"); + Statusbar::print("Using all lyrics fetchers"); } void Lyrics::Take() diff --git a/src/lyrics.h b/src/lyrics.h index 608b3755..d7061bfd 100644 --- a/src/lyrics.h +++ b/src/lyrics.h @@ -83,7 +83,7 @@ private: bool isDownloadInProgress; pthread_t itsDownloader; - static LyricsFetcher **itsFetcher; + static LyricsFetcher *itsFetcher; # endif // HAVE_CURL_CURL_H size_t itsScrollBegin; diff --git a/src/lyrics_fetcher.cpp b/src/lyrics_fetcher.cpp index 89f79851..d9676510 100644 --- a/src/lyrics_fetcher.cpp +++ b/src/lyrics_fetcher.cpp @@ -36,19 +36,28 @@ #include "utility/html.h" #include "utility/string.h" -LyricsFetcher *lyricsPlugins[] = +std::unique_ptr toLyricsFetcher(const std::string &s) { - new LyricwikiFetcher(), - new GeniusLyricsFetcher(), - new AzLyricsFetcher(), - new Sing365Fetcher(), - new LyricsmaniaFetcher(), - new MetrolyricsFetcher(), - new JustSomeLyricsFetcher(), - new TekstowoLyricsFetcher(), - new InternetLyricsFetcher(), - 0 -}; + if (s == "lyricwiki") + return std::make_unique(); + else if (s == "azlyrics") + return std::make_unique(); + else if (s == "genius") + return std::make_unique(); + else if (s == "sing365") + return std::make_unique(); + else if (s == "lyricsmania") + return std::make_unique(); + else if (s == "metrolyrics") + return std::make_unique(); + else if (s == "justsomelyrics") + return std::make_unique(); + else if (s == "tekstowo") + return std::make_unique(); + else if (s == "internet") + return std::make_unique(); + throw std::runtime_error("no lyrics fetcher named '" + s + "'"); +} const char LyricsFetcher::msgNotFound[] = "Not found"; diff --git a/src/lyrics_fetcher.h b/src/lyrics_fetcher.h index c50ce360..225a2032 100644 --- a/src/lyrics_fetcher.h +++ b/src/lyrics_fetcher.h @@ -25,6 +25,7 @@ #ifdef HAVE_CURL_CURL_H +#include #include struct LyricsFetcher @@ -46,6 +47,12 @@ protected: static const char msgNotFound[]; }; +typedef std::vector> LyricsFetchers; + +std::unique_ptr toLyricsFetcher(const std::string &s); + +/**********************************************************************/ + struct LyricwikiFetcher : public LyricsFetcher { virtual const char *name() const override { return "lyricwiki.com"; } @@ -116,7 +123,7 @@ protected: virtual const char *regex() const override { return "
.*?.*
(.*?)
"; } }; -struct GeniusLyricsFetcher : public GoogleLyricsFetcher +struct GeniusFetcher : public GoogleLyricsFetcher { virtual const char *name() const override { return "genius.com"; } @@ -124,7 +131,7 @@ protected: virtual const char *regex() const override { return "(.*?)"; } }; -struct TekstowoLyricsFetcher : public GoogleLyricsFetcher +struct TekstowoFetcher : public GoogleLyricsFetcher { virtual const char *name() const override { return "tekstowo.pl"; } @@ -147,8 +154,6 @@ private: std::string URL; }; -extern LyricsFetcher *lyricsPlugins[]; - #endif // HAVE_CURL_CURL_H #endif // NCMPCPP_LYRICS_FETCHER_H diff --git a/src/settings.cpp b/src/settings.cpp index aaf03cf6..cb78816b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -503,6 +503,23 @@ bool Configuration::read(const std::vector &config_paths, bool igno p.add("lines_scrolled", assign_default( lines_scrolled, 2 )); + p.add("lyrics_fetchers", option_parser::worker([this](std::string v) { + boost::sregex_token_iterator fetcher(v.begin(), v.end(), boost::regex("\\w+")), end; + for (; fetcher != end; ++fetcher) + lyrics_fetchers.push_back(toLyricsFetcher(*fetcher)); + if (lyrics_fetchers.empty()) + throw std::runtime_error("empty list"); + }, [this] { + lyrics_fetchers.push_back(std::make_unique()); + lyrics_fetchers.push_back(std::make_unique()); + lyrics_fetchers.push_back(std::make_unique()); + lyrics_fetchers.push_back(std::make_unique()); + lyrics_fetchers.push_back(std::make_unique()); + lyrics_fetchers.push_back(std::make_unique()); + lyrics_fetchers.push_back(std::make_unique()); + lyrics_fetchers.push_back(std::make_unique()); + lyrics_fetchers.push_back(std::make_unique()); + })); p.add("follow_now_playing_lyrics", yes_no( now_playing_lyrics, false )); diff --git a/src/settings.h b/src/settings.h index 09384e46..529b5f4d 100644 --- a/src/settings.h +++ b/src/settings.h @@ -30,6 +30,7 @@ #include "enums.h" #include "format.h" +#include "lyrics_fetcher.h" #include "screen_type.h" #include "strbuffer.h" @@ -195,6 +196,8 @@ struct Configuration std::list screen_sequence; SortMode browser_sort_mode; + + LyricsFetchers lyrics_fetchers; }; extern Configuration Config;