diff --git a/NEWS b/NEWS index 286c89d0..b9c83002 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ ncmpcpp-0.8 (????-??-??) * 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. +* Lyrics can now be fetched for songs with no tags. ncmpcpp-0.7.7 (2016-10-31) * Fixed compilation on 32bit platforms. diff --git a/configure.ac b/configure.ac index 4b888c7a..4e63d515 100644 --- a/configure.ac +++ b/configure.ac @@ -145,17 +145,6 @@ PKG_CHECK_MODULES([ICU], [icu-uc], [ ) ], [[]]) -dnl ============================= -dnl = checking for boost.thread = -dnl ============================= -AC_DEFINE([BOOST_THREAD_VERSION], [3], [require boost.thread v3]) -AC_CHECK_HEADERS([boost/thread.hpp], , - AC_MSG_ERROR([boost/thread.hpp is missing or your boost version is too old (boost.thread v3 is required)]) -) -AC_CHECK_LIB(boost_thread$BOOST_LIB_SUFFIX, main, LIBS="$LIBS -lboost_thread$BOOST_LIB_SUFFIX", - AC_MSG_ERROR([no boost.thread library found]) -) - dnl ================================ dnl = checking for various headers = dnl ================================ @@ -189,16 +178,6 @@ if test "$ax_cv_lib_readline_history" = "no"; then AC_MSG_WARN([readline library has no history functionality]) fi -dnl ======================== -dnl = checking for pthread = -dnl ======================== -AC_CHECK_HEADERS([pthread.h], - AC_CHECK_LIB(pthread, pthread_create, LIBS="$LIBS -lpthread", - AC_MSG_ERROR([pthread.h found but there is no pthread library to make use of]) - ), - AC_MSG_ERROR([no pthread.h header header file found]) -) - dnl ======================== dnl = checking for ncurses = dnl ======================== diff --git a/src/actions.cpp b/src/actions.cpp index 9b306597..636f1da0 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1148,7 +1148,7 @@ bool ToggleLyricsFetcher::canBeRun() void ToggleLyricsFetcher::run() { # ifdef HAVE_CURL_CURL_H - myLyrics->ToggleFetcher(); + myLyrics->toggleFetcher(); # endif // HAVE_CURL_CURL_H } @@ -1601,7 +1601,7 @@ bool EditLyrics::canBeRun() void EditLyrics::run() { - myLyrics->Edit(); + myLyrics->edit(); } bool JumpToBrowser::canBeRun() @@ -2309,7 +2309,7 @@ bool RefetchLyrics::canBeRun() void RefetchLyrics::run() { # ifdef HAVE_CURL_CURL_H - myLyrics->Refetch(); + myLyrics->refetchCurrent(); # endif // HAVE_CURL_CURL_H } @@ -2444,8 +2444,24 @@ void ShowArtistInfo::run() # endif // HAVE_CURL_CURL_H } +bool ShowLyrics::canBeRun() +{ + if (myScreen == myLyrics) + { + m_song = nullptr; + return true; + } + else + { + m_song = currentSong(myScreen); + return m_song != nullptr; + } +} + void ShowLyrics::run() { + if (m_song != nullptr) + myLyrics->fetch(*m_song); myLyrics->switchTo(); } diff --git a/src/actions.h b/src/actions.h index 79561de9..b8bf1eb6 100644 --- a/src/actions.h +++ b/src/actions.h @@ -1240,7 +1240,10 @@ struct ShowLyrics: BaseAction ShowLyrics(): BaseAction(Type::ShowLyrics, "show_lyrics") { } private: + virtual bool canBeRun() override; virtual void run() override; + + const MPD::Song *m_song; }; struct Quit: BaseAction diff --git a/src/curl_handle.cpp b/src/curl_handle.cpp index 9f3ea98e..de1295db 100644 --- a/src/curl_handle.cpp +++ b/src/curl_handle.cpp @@ -23,7 +23,6 @@ #ifdef HAVE_CURL_CURL_H #include -#include namespace { @@ -65,4 +64,3 @@ std::string Curl::escape(const std::string &s) } #endif // HAVE_CURL_CURL_H - diff --git a/src/lyrics.cpp b/src/lyrics.cpp index 5a818ce3..02d440d7 100644 --- a/src/lyrics.cpp +++ b/src/lyrics.cpp @@ -18,6 +18,9 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include +#include +#include #include #include #include @@ -42,22 +45,152 @@ using Global::MainHeight; using Global::MainStartY; -#ifdef HAVE_CURL_CURL_H -LyricsFetcher *Lyrics::itsFetcher = nullptr; -std::queue Lyrics::itsToDownload; -pthread_mutex_t Lyrics::itsDIBLock = PTHREAD_MUTEX_INITIALIZER; -size_t Lyrics::itsWorkersNumber = 0; -#endif // HAVE_CURL_CURL_H - Lyrics *myLyrics; +namespace { + +std::string removeExtension(std::string filename) +{ + size_t dot = filename.rfind('.'); + if (dot != std::string::npos) + filename.resize(dot); + return filename; +} + +std::string lyricsFilename(const MPD::Song &s) +{ + std::string filename; + if (Config.store_lyrics_in_song_dir && !s.isStream()) + { + if (s.isFromDatabase()) + filename = Config.mpd_music_dir + "/"; + filename += removeExtension(s.getURI()); + removeExtension(filename); + } + else + { + std::string artist = s.getArtist(); + std::string title = s.getTitle(); + if (artist.empty() || title.empty()) + filename = removeExtension(s.getName()); + else + filename = artist + " - " + title; + removeInvalidCharsFromFilename(filename, Config.generate_win32_compatible_filenames); + filename = Config.lyrics_directory + "/" + filename; + } + filename += ".txt"; + return filename; +} + +bool loadLyrics(NC::Scrollpad &w, const std::string &filename) +{ + std::ifstream input(filename); + if (input.is_open()) + { + std::string line; + bool first_line = true; + while (std::getline(input, line)) + { + // Remove carriage returns as they mess up the display. + boost::remove_erase(line, '\r'); + if (!first_line) + w << '\n'; + w << Charset::utf8ToLocale(line); + first_line = false; + } + return true; + } + else + return false; +} + +bool saveLyrics(const std::string &filename, const std::string &lyrics) +{ + std::ofstream output(filename); + if (output.is_open()) + { + output << lyrics; + output.close(); + return true; + } + else + return false; +} + +boost::optional downloadLyrics( + const MPD::Song &s, + std::shared_ptr> shared_buffer, + LyricsFetcher *current_fetcher) +{ + std::string s_artist = Curl::escape(s.getArtist()); + std::string s_title = Curl::escape(s.getTitle()); + // If artist or title is empty, use filename. This should give reasonable + // results for google search based lyrics fetchers. + if (s_artist.empty() || s_title.empty()) + { + s_artist.clear(); + s_title = s.getName(); + // Get rid of underscores to improve search results. + std::replace_if(s_title.begin(), s_title.end(), boost::is_any_of("-_"), ' '); + size_t dot = s_title.rfind('.'); + if (dot != std::string::npos) + s_title.resize(dot); + s_title = Curl::escape(s_title); + } + + auto fetch_lyrics = [&](auto &fetcher_) { + { + if (shared_buffer) + { + auto buf = shared_buffer->acquire(); + *buf << "Fetching lyrics from " + << NC::Format::Bold + << fetcher_->name() + << NC::Format::NoBold << "... "; + } + } + auto result_ = fetcher_->fetch(s_artist, s_title); + if (result_.first == false) + { + if (shared_buffer) + { + auto buf = shared_buffer->acquire(); + *buf << NC::Color::Red + << result_.second + << NC::Color::End + << '\n'; + } + } + return result_; + }; + + LyricsFetcher::Result fetcher_result; + if (current_fetcher == nullptr) + { + for (auto &fetcher : Config.lyrics_fetchers) + { + fetcher_result = fetch_lyrics(fetcher); + if (fetcher_result.first) + break; + } + } + else + fetcher_result = fetch_lyrics(current_fetcher); + + boost::optional result; + if (fetcher_result.first) + result = std::move(fetcher_result.second); + return result; +} + +} + Lyrics::Lyrics() -: Screen(NC::Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, NC::Border())) -, Reload(0), -#ifdef HAVE_CURL_CURL_H -isReadyToTake(0), isDownloadInProgress(0), -#endif // HAVE_CURL_CURL_H - itsScrollBegin(0) + : Screen(NC::Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, NC::Border())) + , m_refresh_window(false) + , m_scroll_begin(0) + , m_fetcher(nullptr) + , m_shared_queue(std::make_pair(false, std::queue{})) { } void Lyrics::resize() @@ -71,23 +204,42 @@ void Lyrics::resize() void Lyrics::update() { -# ifdef HAVE_CURL_CURL_H - if (isReadyToTake) - Take(); - - if (isDownloadInProgress) + if (m_worker.valid()) { + auto buffer = m_shared_buffer->acquire(); + if (!buffer->empty()) + { + w << *buffer; + buffer->clear(); + m_refresh_window = true; + } + + if (m_worker.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + auto lyrics = m_worker.get(); + if (lyrics) + { + w.clear(); + w << Charset::utf8ToLocale(*lyrics); + std::string filename = lyricsFilename(m_song); + if (!saveLyrics(filename, *lyrics)) + Statusbar::printf("Couldn't save lyrics as \"%1%\": %2%", + filename, strerror(errno)); + } + else + w << "\nLyrics were not found.\n"; + m_refresh_window = true; + // Reset worker so it's no longer valid. + m_worker = std::future>(); + } + } + + if (m_refresh_window) + { + m_refresh_window = false; w.flush(); w.refresh(); } -# endif // HAVE_CURL_CURL_H - if (Reload) - { - drawHeader(); - itsScrollBegin = 0; - Load(); - Reload = 0; - } } void Lyrics::switchTo() @@ -95,31 +247,9 @@ void Lyrics::switchTo() using Global::myScreen; if (myScreen != this) { -# ifdef HAVE_CURL_CURL_H - // take lyrics if they were downloaded - if (isReadyToTake) - Take(); - - if (isDownloadInProgress || itsWorkersNumber > 0) - { - Statusbar::print("Lyrics are being downloaded..."); - return; - } -# endif // HAVE_CURL_CURL_H - - auto s = currentSong(myScreen); - if (!s) - return; - - if (SetSong(*s)) - { - SwitchTo::execute(this); - itsScrollBegin = 0; - Load(); - drawHeader(); - } - else - Statusbar::print("Song must have both artist and title tag set"); + SwitchTo::execute(this); + m_scroll_begin = 0; + drawHeader(); } else switchToPreviousScreen(); @@ -128,328 +258,143 @@ void Lyrics::switchTo() std::wstring Lyrics::title() { std::wstring result = L"Lyrics: "; - result += Scroller( - Format::stringify(Format::parse(L"%a - %t"), &itsSong), - itsScrollBegin, - COLS-result.length()-(Config.design == Design::Alternative ? 2 : Global::VolumeState.length()) - ); + Format::stringify(Format::parse(L"{%a - %t}|{%f}"), &m_song), + m_scroll_begin, + COLS - result.length() - (Config.design == Design::Alternative + ? 2 + : Global::VolumeState.length())); return result; } -#ifdef HAVE_CURL_CURL_H -void Lyrics::DownloadInBackground(const MPD::Song &s) +void Lyrics::fetch(const MPD::Song &s) { - if (s.empty() || s.getArtist().empty() || s.getTitle().empty()) - return; - - std::string filename = GenerateFilename(s); - std::ifstream f(filename.c_str()); - if (f.is_open()) + if (!m_worker.valid() || s != m_song) { - f.close(); - return; - } - Statusbar::printf("Fetching lyrics for \"%1%\"...", - Format::stringify(Config.song_status_format, &s) - ); - - MPD::Song *s_copy = new MPD::Song(s); - pthread_mutex_lock(&itsDIBLock); - if (itsWorkersNumber == itsMaxWorkersNumber) - itsToDownload.push(s_copy); - else - { - ++itsWorkersNumber; - pthread_t t; - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&t, &attr, DownloadInBackgroundImpl, s_copy); - } - pthread_mutex_unlock(&itsDIBLock); -} - -void *Lyrics::DownloadInBackgroundImpl(void *void_ptr) -{ - MPD::Song *s = static_cast(void_ptr); - DownloadInBackgroundImplHelper(*s); - delete s; - - while (true) - { - pthread_mutex_lock(&itsDIBLock); - if (itsToDownload.empty()) - { - pthread_mutex_unlock(&itsDIBLock); - break; - } - else - { - s = itsToDownload.front(); - itsToDownload.pop(); - pthread_mutex_unlock(&itsDIBLock); - } - DownloadInBackgroundImplHelper(*s); - delete s; - } - - pthread_mutex_lock(&itsDIBLock); - --itsWorkersNumber; - pthread_mutex_unlock(&itsDIBLock); - - pthread_exit(0); -} - -void Lyrics::DownloadInBackgroundImplHelper(const MPD::Song &s) -{ - std::string artist = Curl::escape(s.getArtist()); - std::string title = Curl::escape(s.getTitle()); - - LyricsFetcher::Result result; - - if (itsFetcher == nullptr) - { - for (auto &fetcher : Config.lyrics_fetchers) - { - result = fetcher->fetch(artist, title); - if (result.first) - break; - } - } - else - itsFetcher->fetch(artist, title); - - if (result.first) - Save(GenerateFilename(s), result.second); -} - -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 (itsFetcher == nullptr) - { - for (auto &fetcher : Config.lyrics_fetchers) - { - result = fetch_lyrics(fetcher); - if (result.first) - break; - } - } - else - result = fetch_lyrics(itsFetcher); - - if (result.first) - { - Save(itsFilename, result.second); w.clear(); - w << Charset::utf8ToLocale(result.second); - } - else - w << '\n' << "Lyrics weren't found."; - - isReadyToTake = 1; - pthread_exit(0); -} - -void *Lyrics::DownloadWrapper(void *this_ptr) -{ - return static_cast(this_ptr)->Download(); -} -#endif // HAVE_CURL_CURL_H - -std::string Lyrics::GenerateFilename(const MPD::Song &s) -{ - std::string filename; - if (Config.store_lyrics_in_song_dir) - { - if (s.isFromDatabase()) - { - filename = Config.mpd_music_dir; - filename += "/"; - filename += s.getURI(); - } + m_song = s; + if (loadLyrics(w, lyricsFilename(m_song))) + m_refresh_window = true; else - filename = s.getURI(); - // replace song's extension with .txt - size_t dot = filename.rfind('.'); - assert(dot != std::string::npos); - filename.resize(dot); - filename += ".txt"; - } - else - { - std::string file = s.getArtist(); - file += " - "; - file += s.getTitle(); - file += ".txt"; - removeInvalidCharsFromFilename(file, Config.generate_win32_compatible_filenames); - filename = Config.lyrics_directory; - filename += "/"; - filename += file; - } - return filename; -} - -void Lyrics::Load() -{ -# ifdef HAVE_CURL_CURL_H - if (isDownloadInProgress) - return; -# endif // HAVE_CURL_CURL_H - - assert(!itsSong.getArtist().empty()); - assert(!itsSong.getTitle().empty()); - - itsFilename = GenerateFilename(itsSong); - - w.clear(); - w.reset(); - - std::ifstream input(itsFilename.c_str()); - if (input.is_open()) - { - bool first = 1; - std::string line; - while (std::getline(input, line)) { - // Remove carriage returns as they mess up the display. - line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); - if (!first) - w << '\n'; - w << Charset::utf8ToLocale(line); - first = 0; + m_shared_buffer = std::make_shared>(); + m_worker = std::async( + std::launch::async, + std::bind(downloadLyrics, m_song, m_shared_buffer, m_fetcher)); } - w.flush(); - if (Reload) - w.refresh(); - } - else - { -# ifdef HAVE_CURL_CURL_H - pthread_create(&itsDownloader, 0, DownloadWrapper, this); - isDownloadInProgress = 1; -# else - w << "Local lyrics not found. As ncmpcpp has been compiled without curl support, you can put appropriate lyrics into " << Config.lyrics_directory << " directory (file syntax is \"$ARTIST - $TITLE.txt\") or recompile ncmpcpp with curl support."; - w.flush(); -# endif } } -void Lyrics::Edit() +void Lyrics::refetchCurrent() +{ + std::string filename = lyricsFilename(m_song); + if (std::remove(filename.c_str()) == -1 && errno != ENOENT) + { + const char msg[] = "Couldn't remove \"%1%\": %2%"; + Statusbar::printf(msg, wideShorten(filename, COLS - const_strlen(msg) - 25), + strerror(errno)); + } + else + { + // Get rid of current worker so fetch can restart the process. + m_worker = std::future>(); + fetch(m_song); + } +} + +void Lyrics::edit() { - assert(Global::myScreen == this); - if (Config.external_editor.empty()) { - Statusbar::print("Proper external_editor variable has to be set in configuration file"); + Statusbar::print("external_editor variable has to be set in configuration file"); return; } - + Statusbar::print("Opening lyrics in external editor..."); - + GNUC_UNUSED int res; + std::string command; + std::string filename = lyricsFilename(m_song); if (Config.use_console_editor) { - res = system(("/bin/sh -c \"" + Config.external_editor + " \\\"" + itsFilename + "\\\"\"").c_str()); - Load(); - // below is needed as screen gets cleared, but apparently - // ncurses doesn't know about it, so we need to reload main screen + command = "/bin/sh -c \"" + Config.external_editor + " \\\"" + filename + "\\\"\""; + res = system(command.c_str()); + fetch(m_song); + // Reset ncurses state to refresh the screen. endwin(); initscr(); curs_set(0); } else - res = system(("nohup " + Config.external_editor + " \"" + itsFilename + "\" > /dev/null 2>&1 &").c_str()); -} - -bool Lyrics::SetSong(const MPD::Song &s) -{ - if (!s.getArtist().empty() && !s.getTitle().empty()) { - itsSong = s; - return true; - } - else - return false; -} - -#ifdef HAVE_CURL_CURL_H -void Lyrics::Save(const std::string &filename, const std::string &lyrics) -{ - std::ofstream output(filename.c_str()); - if (output.is_open()) - { - output << lyrics; - output.close(); + command = "nohup " + Config.external_editor + + " \"" + filename + "\" > /dev/null 2>&1 &"; + res = system(command.c_str()); } } -void Lyrics::Refetch() +void Lyrics::toggleFetcher() { - if (remove(itsFilename.c_str()) && errno != ENOENT) - { - const char msg[] = "Couldn't remove \"%1%\": %2%"; - Statusbar::printf(msg, wideShorten(itsFilename, COLS-const_strlen(msg)-25), strerror(errno)); - return; - } - Load(); -} - -void Lyrics::ToggleFetcher() -{ - if (itsFetcher != nullptr) + if (m_fetcher != nullptr) { auto fetcher = std::find_if(Config.lyrics_fetchers.begin(), Config.lyrics_fetchers.end(), - [](auto &f) { return f.get() == itsFetcher; }); + [this](auto &f) { return f.get() == m_fetcher; }); assert(fetcher != Config.lyrics_fetchers.end()); ++fetcher; if (fetcher != Config.lyrics_fetchers.end()) - itsFetcher = fetcher->get(); + m_fetcher = fetcher->get(); else - itsFetcher = nullptr; + m_fetcher = nullptr; } else { assert(!Config.lyrics_fetchers.empty()); - itsFetcher = Config.lyrics_fetchers[0].get(); + m_fetcher = Config.lyrics_fetchers[0].get(); } - if (itsFetcher != nullptr) - Statusbar::printf("Using lyrics fetcher: %s", itsFetcher->name()); + if (m_fetcher != nullptr) + Statusbar::printf("Using lyrics fetcher: %s", m_fetcher->name()); else Statusbar::print("Using all lyrics fetchers"); } -void Lyrics::Take() +void Lyrics::fetchInBackground(const MPD::Song &s) { - assert(isReadyToTake); - pthread_join(itsDownloader, 0); - w.flush(); - w.refresh(); - isDownloadInProgress = 0; - isReadyToTake = 0; -} -#endif // HAVE_CURL_CURL_H + auto consumer = [this] { + std::string lyrics_file; + while (true) + { + MPD::Song qs; + { + auto queue = m_shared_queue.acquire(); + assert(queue->first); + if (queue->second.empty()) + { + queue->first = false; + break; + } + lyrics_file = lyricsFilename(queue->second.front()); + if (!boost::filesystem::exists(lyrics_file)) + qs = queue->second.front(); + queue->second.pop(); + } + if (!qs.empty()) + { + auto lyrics = downloadLyrics(qs, nullptr, m_fetcher); + if (lyrics) + saveLyrics(lyrics_file, *lyrics); + } + } + }; + auto queue = m_shared_queue.acquire(); + queue->second.push(s); + // Start the consumer if it's not running. + if (!queue->first) + { + std::thread t(consumer); + t.detach(); + queue->first = true; + } +} diff --git a/src/lyrics.h b/src/lyrics.h index 8eae4632..48a82097 100644 --- a/src/lyrics.h +++ b/src/lyrics.h @@ -21,7 +21,9 @@ #ifndef NCMPCPP_LYRICS_H #define NCMPCPP_LYRICS_H -#include +#include +#include +#include #include #include "interfaces.h" @@ -29,6 +31,40 @@ #include "screen.h" #include "song.h" +template +struct Shared +{ + struct Resource + { + Resource(std::mutex &mutex, ResourceT &resource) + : m_lock(std::unique_lock(mutex)), m_resource(resource) + { } + + ResourceT &operator*() { return m_resource; } + const ResourceT &operator*() const { return m_resource; } + + ResourceT *operator->() { return &m_resource; } + const ResourceT *operator->() const { return &m_resource; } + + private: + std::unique_lock m_lock; + ResourceT &m_resource; + }; + + Shared(){ } + + template + Shared(ValueT &&value) + : m_resource(std::forward(value)) + { } + + Resource acquire() { return Resource(m_mutex, m_resource); } + +private: + std::mutex m_mutex; + ResourceT m_resource; +}; + struct Lyrics: Screen, Tabbable { Lyrics(); @@ -44,53 +80,26 @@ struct Lyrics: Screen, Tabbable virtual bool isLockable() override { return false; } virtual bool isMergable() override { return true; } - - // private members - bool SetSong(const MPD::Song &s); - void Edit(); - -# ifdef HAVE_CURL_CURL_H - void Refetch(); - static void ToggleFetcher(); - static void DownloadInBackground(const MPD::Song &s); -# endif // HAVE_CURL_CURL_H - - bool Reload; - + // other members + void fetch(const MPD::Song &s); + void refetchCurrent(); + void edit(); + void toggleFetcher(); + + void fetchInBackground(const MPD::Song &s); + private: - void Load(); - -# ifdef HAVE_CURL_CURL_H - static void *DownloadInBackgroundImpl(void *song_ptr); - static void DownloadInBackgroundImplHelper(const MPD::Song &s); - // lock for allowing exclusive access to itsToDownload and itsWorkersNumber - static pthread_mutex_t itsDIBLock; - // storage for songs for which lyrics are scheduled to be downloaded - static std::queue itsToDownload; - // current worker threads (ie. downloading lyrics) - static size_t itsWorkersNumber; - // maximum number of worker threads. if it's reached, next lyrics requests - // are put into itsToDownload queue. - static const size_t itsMaxWorkersNumber = 1; - - void *Download(); - static void *DownloadWrapper(void *); - static void Save(const std::string &filename, const std::string &lyrics); - - void Take(); - bool isReadyToTake; - bool isDownloadInProgress; - pthread_t itsDownloader; - - static LyricsFetcher *itsFetcher; -# endif // HAVE_CURL_CURL_H - - size_t itsScrollBegin; - MPD::Song itsSong; - std::string itsFilename; - - static std::string GenerateFilename(const MPD::Song &s); + bool m_refresh_window; + size_t m_scroll_begin; + + std::shared_ptr> m_shared_buffer; + + MPD::Song m_song; + LyricsFetcher *m_fetcher; + std::future> m_worker; + + Shared>> m_shared_queue; }; extern Lyrics *myLyrics; diff --git a/src/scrollpad.h b/src/scrollpad.h index 7aca920d..1e9b8356 100644 --- a/src/scrollpad.h +++ b/src/scrollpad.h @@ -46,18 +46,30 @@ struct Scrollpad: public Window void flush(); void reset(); - bool setProperties(Color begin, const std::string &s, Color end, size_t flags, size_t id = -2); - bool setProperties(Format begin, const std::string &s, Format end, size_t flags, size_t id = -2); + bool setProperties(Color begin, const std::string &s, Color end, + size_t flags, size_t id = -2); + bool setProperties(Format begin, const std::string &s, Format end, + size_t flags, size_t id = -2); void removeProperties(size_t id = -2); + Scrollpad &operator<<(int n) { return write(n); } + Scrollpad &operator<<(long int n) { return write(n); } + Scrollpad &operator<<(unsigned int n) { return write(n); } + Scrollpad &operator<<(unsigned long int n) { return write(n); } + Scrollpad &operator<<(char c) { return write(c); } + Scrollpad &operator<<(const char *s) { return write(s); } + Scrollpad &operator<<(const std::string &s) { return write(s); } + Scrollpad &operator<<(Color color) { return write(color); } + Scrollpad &operator<<(Format format) { return write(format); } + +private: template - Scrollpad &operator<<(const ItemT &item) + Scrollpad &write(ItemT &&item) { - m_buffer << item; + m_buffer << std::forward(item); return *this; } - -private: + Buffer m_buffer; size_t m_beginning; diff --git a/src/status.cpp b/src/status.cpp index 83d001b0..70284c0e 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -574,7 +574,7 @@ void Status::Changes::songID(int song_id) # ifdef HAVE_CURL_CURL_H if (Config.fetch_lyrics_in_background) - Lyrics::DownloadInBackground(s); + myLyrics->fetchInBackground(s); # endif // HAVE_CURL_CURL_H drawTitle(s); @@ -582,11 +582,10 @@ void Status::Changes::songID(int song_id) if (Config.autocenter_mode) myPlaylist->locateSong(s); - if (Config.now_playing_lyrics && isVisible(myLyrics) && myLyrics->previousScreen() == myPlaylist) - { - if (myLyrics->SetSong(s)) - myLyrics->Reload = 1; - } + if (Config.now_playing_lyrics + && isVisible(myLyrics) + && myLyrics->previousScreen() == myPlaylist) + myLyrics->fetch(s); } } elapsedTime(false); diff --git a/src/strbuffer.h b/src/strbuffer.h index 474fb072..9c8ffb18 100644 --- a/src/strbuffer.h +++ b/src/strbuffer.h @@ -89,7 +89,12 @@ public: ++it; } } - + + bool empty() const + { + return m_string.empty() && m_properties.empty(); + } + void clear() { m_string.clear();