Add support for fetching lyrics from tags

Adaptation of https://github.com/ncmpcpp/ncmpcpp/pull/482.
This commit is contained in:
Andrzej Rybczak
2024-09-03 03:15:09 +02:00
parent 510e28c65a
commit 9c27eec846
7 changed files with 83 additions and 13 deletions

View File

@@ -19,6 +19,7 @@
* Add `visualizer_spectrum_smooth_look_legacy_chars` option (enabled by default) * Add `visualizer_spectrum_smooth_look_legacy_chars` option (enabled by default)
for potentially improved bottom part of the spectrum visualizer in terminals for potentially improved bottom part of the spectrum visualizer in terminals
with transparent background. with transparent background.
* Add support for fetching lyrics from tags.
# ncmpcpp-0.9.2 (2021-01-24) # ncmpcpp-0.9.2 (2021-01-24)
* Revert suppression of output of all external commands as that makes e.g album * Revert suppression of output of all external commands as that makes e.g album

View File

@@ -422,7 +422,7 @@
# #
#cyclic_scrolling = no #cyclic_scrolling = no
# #
#lyrics_fetchers = genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet #lyrics_fetchers = tags, genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet
# #
#follow_now_playing_lyrics = no #follow_now_playing_lyrics = no
# #

View File

@@ -171,7 +171,7 @@ bool configure(int argc, char **argv)
<< fetcher->name() << fetcher->name()
<< " : " << " : "
<< std::flush; << std::flush;
auto result = fetcher->fetch(std::get<1>(data), std::get<2>(data)); auto result = fetcher->fetch(std::get<1>(data), std::get<2>(data), {});
std::cout << (result.first ? "ok" : "failed") std::cout << (result.first ? "ok" : "failed")
<< "\n"; << "\n";
} }

View File

@@ -19,7 +19,6 @@
***************************************************************************/ ***************************************************************************/
#include "config.h" #include "config.h"
#include "curl_handle.h"
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@@ -29,8 +28,15 @@
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#ifdef HAVE_TAGLIB_H
#include <fileref.h>
#include <tpropertymap.h>
#endif // HAVE_TAGLIB_H
#include "charset.h" #include "charset.h"
#include "curl_handle.h"
#include "lyrics_fetcher.h" #include "lyrics_fetcher.h"
#include "settings.h"
#include "utility/html.h" #include "utility/html.h"
#include "utility/string.h" #include "utility/string.h"
@@ -52,6 +58,10 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
fetcher = std::make_unique<ZeneszovegFetcher>(); fetcher = std::make_unique<ZeneszovegFetcher>();
else if (s == "internet") else if (s == "internet")
fetcher = std::make_unique<InternetLyricsFetcher>(); fetcher = std::make_unique<InternetLyricsFetcher>();
#ifdef HAVE_TAGLIB_H
else if (s == "tags")
fetcher = std::make_unique<TagsLyricsFetcher>();
#endif // HAVE_TAGLIB_H
else else
is.setstate(std::ios::failbit); is.setstate(std::ios::failbit);
return is; return is;
@@ -60,7 +70,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
const char LyricsFetcher::msgNotFound[] = "Not found"; const char LyricsFetcher::msgNotFound[] = "Not found";
LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist, LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist,
const std::string &title) const std::string &title,
[[maybe_unused]] const MPD::Song &song)
{ {
Result result; Result result;
result.first = false; result.first = false;
@@ -131,7 +142,7 @@ void LyricsFetcher::postProcess(std::string &data) const
stripHtmlTags(data); stripHtmlTags(data);
// Remove indentation from each line and collapse multiple newlines into one. // Remove indentation from each line and collapse multiple newlines into one.
std::vector<std::string> lines; std::vector<std::string> lines;
boost::split(lines, data, boost::is_any_of("\n")); boost::split(lines, data, boost::is_any_of("\r\n"));
for (auto &line : lines) for (auto &line : lines)
boost::trim(line); boost::trim(line);
auto last = std::unique( auto last = std::unique(
@@ -146,7 +157,8 @@ void LyricsFetcher::postProcess(std::string &data) const
/**********************************************************************/ /**********************************************************************/
LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist, LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
const std::string &title) const std::string &title,
const MPD::Song &song)
{ {
Result result; Result result;
result.first = false; result.first = false;
@@ -188,7 +200,7 @@ LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
data = unescapeHtmlUtf8(urls[0]); data = unescapeHtmlUtf8(urls[0]);
URL = data.c_str(); URL = data.c_str();
return LyricsFetcher::fetch("", ""); return LyricsFetcher::fetch("", "", song);
} }
bool GoogleLyricsFetcher::isURLOk(const std::string &url) bool GoogleLyricsFetcher::isURLOk(const std::string &url)
@@ -199,9 +211,10 @@ bool GoogleLyricsFetcher::isURLOk(const std::string &url)
/**********************************************************************/ /**********************************************************************/
LyricsFetcher::Result InternetLyricsFetcher::fetch(const std::string &artist, LyricsFetcher::Result InternetLyricsFetcher::fetch(const std::string &artist,
const std::string &title) const std::string &title,
const MPD::Song &song)
{ {
GoogleLyricsFetcher::fetch(artist, title); GoogleLyricsFetcher::fetch(artist, title, song);
LyricsFetcher::Result result; LyricsFetcher::Result result;
result.first = false; result.first = false;
result.second = "The following site may contain lyrics for this song: "; result.second = "The following site may contain lyrics for this song: ";
@@ -214,3 +227,42 @@ bool InternetLyricsFetcher::isURLOk(const std::string &url)
URL = url; URL = url;
return false; return false;
} }
#ifdef HAVE_TAGLIB_H
LyricsFetcher::Result TagsLyricsFetcher::fetch([[maybe_unused]] const std::string &artist,
[[maybe_unused]] const std::string &title,
const MPD::Song &song)
{
LyricsFetcher::Result result;
result.first = false;
std::string path;
if (song.isFromDatabase())
path += Config.mpd_music_dir;
path += song.getURI();
TagLib::FileRef f(path.c_str());
if (f.isNull())
{
result.second = "Could not open file";
return result;
}
TagLib::PropertyMap properties = f.file()->properties();
if (properties.contains("LYRICS"))
{
result.first = true;
result.second = properties["LYRICS"].toString("\n\n").to8Bit(true);
}
else if (properties.contains("UNSYNCEDLYRICS"))
{
result.first = true;
result.second = properties["UNSYNCEDLYRICS"].toString("\n\n").to8Bit(true);
}
else
result.second = "No lyrics in tags";
return result;
}
#endif // HAVE_TAGLIB_H

View File

@@ -26,6 +26,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "song.h"
struct LyricsFetcher struct LyricsFetcher
{ {
typedef std::pair<bool, std::string> Result; typedef std::pair<bool, std::string> Result;
@@ -33,7 +35,7 @@ struct LyricsFetcher
virtual ~LyricsFetcher() { } virtual ~LyricsFetcher() { }
virtual const char *name() const = 0; virtual const char *name() const = 0;
virtual Result fetch(const std::string &artist, const std::string &title); virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song);
protected: protected:
virtual const char *urlTemplate() const = 0; virtual const char *urlTemplate() const = 0;
@@ -57,7 +59,7 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher);
struct GoogleLyricsFetcher : public LyricsFetcher struct GoogleLyricsFetcher : public LyricsFetcher
{ {
virtual Result fetch(const std::string &artist, const std::string &title); virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song);
protected: protected:
virtual const char *urlTemplate() const { return URL; } virtual const char *urlTemplate() const { return URL; }
@@ -120,7 +122,7 @@ protected:
struct InternetLyricsFetcher : public GoogleLyricsFetcher struct InternetLyricsFetcher : public GoogleLyricsFetcher
{ {
virtual const char *name() const override { return "the Internet"; } virtual const char *name() const override { return "the Internet"; }
virtual Result fetch(const std::string &artist, const std::string &title) override; virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song) override;
protected: protected:
virtual const char *siteKeyword() const override { return nullptr; } virtual const char *siteKeyword() const override { return nullptr; }
@@ -132,4 +134,16 @@ private:
std::string URL; std::string URL;
}; };
#ifdef HAVE_TAGLIB_H
struct TagsLyricsFetcher : public LyricsFetcher
{
virtual const char *name() const override { return "tags"; }
virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song) override;
protected:
virtual const char *urlTemplate() const override { return ""; }
virtual const char *regex() const override { return ""; }
};
#endif // HAVE_TAGLIB_H
#endif // NCMPCPP_LYRICS_FETCHER_H #endif // NCMPCPP_LYRICS_FETCHER_H

View File

@@ -151,7 +151,7 @@ boost::optional<std::string> downloadLyrics(
<< NC::Format::NoBold << "... "; << NC::Format::NoBold << "... ";
} }
} }
auto result_ = fetcher_->fetch(s_artist, s_title); auto result_ = fetcher_->fetch(s_artist, s_title, s);
if (result_.first == false) if (result_.first == false)
{ {
if (shared_buffer) if (shared_buffer)

View File

@@ -462,6 +462,9 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
p.add("header_text_scrolling", &header_text_scrolling, "yes", yes_no); p.add("header_text_scrolling", &header_text_scrolling, "yes", yes_no);
p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no); p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
p.add<void>("lyrics_fetchers", nullptr, p.add<void>("lyrics_fetchers", nullptr,
#ifdef HAVE_TAGLIB_H
"tags, "
#endif
"genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet", [this](std::string v) { "genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet", [this](std::string v) {
lyrics_fetchers = list_of<LyricsFetcher_>(v, [](std::string s) { lyrics_fetchers = list_of<LyricsFetcher_>(v, [](std::string s) {
LyricsFetcher_ fetcher; LyricsFetcher_ fetcher;