new feature: support for fetching lyrics for currently playing song in background

This commit is contained in:
Andrzej Rybczak
2011-10-18 23:15:32 +02:00
parent 1ccca56da3
commit d9bc1c0950
11 changed files with 130 additions and 33 deletions

View File

@@ -297,6 +297,8 @@
# #
#follow_now_playing_lyrics = "no" #follow_now_playing_lyrics = "no"
# #
#fetch_lyrics_for_current_song_in_background = "no"
#
#store_lyrics_in_song_dir = "no" #store_lyrics_in_song_dir = "no"
# #
## ##

View File

@@ -172,6 +172,8 @@
# #
#key_toggle_lyrics_db = 'L' #key_toggle_lyrics_db = 'L'
# #
#key_toggle_fetching_lyrics_for_current_song_in_background = 'F'
#
#key_go_to_containing_directory = 'G' #key_go_to_containing_directory = 'G'
# #
#key_go_to_media_library = '~' #key_go_to_media_library = '~'

View File

@@ -225,6 +225,9 @@ If enabled, seek time will increment by one each second of seeking.
.B follow_now_playing_lyrics = yes/no .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). 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 .TP
.B fetch_lyrics_for_current_song_in_background = yes/no
If enabled, each time song changes lyrics fetcher will be automatically run in background in attempt to download lyrics for currently playing song.
.TP
.B store_lyrics_in_song_dir = yes/no .B store_lyrics_in_song_dir = yes/no
If enabled, lyrics will be saved in song's directory, otherwise in ~/.lyrics. Note that it needs properly set mpd_music_dir. If enabled, lyrics will be saved in song's directory, otherwise in ~/.lyrics. Note that it needs properly set mpd_music_dir.
.TP .TP

View File

@@ -37,8 +37,6 @@ namespace
CURLcode Curl::perform(std::string &data, const std::string &URL, const std::string &referer, unsigned timeout) CURLcode Curl::perform(std::string &data, const std::string &URL, const std::string &referer, unsigned timeout)
{ {
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&lock);
CURLcode result; CURLcode result;
CURL *c = curl_easy_init(); CURL *c = curl_easy_init();
curl_easy_setopt(c, CURLOPT_URL, URL.c_str()); curl_easy_setopt(c, CURLOPT_URL, URL.c_str());
@@ -51,7 +49,6 @@ CURLcode Curl::perform(std::string &data, const std::string &URL, const std::str
curl_easy_setopt(c, CURLOPT_REFERER, referer.c_str()); curl_easy_setopt(c, CURLOPT_REFERER, referer.c_str());
result = curl_easy_perform(c); result = curl_easy_perform(c);
curl_easy_cleanup(c); curl_easy_cleanup(c);
pthread_mutex_unlock(&lock);
return result; return result;
} }

View File

@@ -222,6 +222,7 @@ void Help::GetKeybindings()
# ifdef HAVE_CURL_CURL_H # ifdef HAVE_CURL_CURL_H
*w << DisplayKeys(Key.ArtistInfo) << "Show artist info\n"; *w << DisplayKeys(Key.ArtistInfo) << "Show artist info\n";
*w << DisplayKeys(Key.ToggleLyricsDB) << "Toggle lyrics database\n"; *w << DisplayKeys(Key.ToggleLyricsDB) << "Toggle lyrics database\n";
*w << DisplayKeys(Key.ToggleFetchingLyricsInBackground) << "Toggle fetching lyrics for current song in background\n";
# endif // HAVE_CURL_CURL_H # endif // HAVE_CURL_CURL_H
*w << DisplayKeys(Key.Lyrics) << "Show/hide song's lyrics\n"; *w << DisplayKeys(Key.Lyrics) << "Show/hide song's lyrics\n";
*w << "\n"; *w << "\n";

View File

@@ -48,12 +48,18 @@ using Global::MainStartY;
using Global::myScreen; using Global::myScreen;
using Global::myOldScreen; using Global::myOldScreen;
std::string Lyrics::itsFolder = home_path + LYRICS_FOLDER;
#ifdef HAVE_CURL_CURL_H
LyricsFetcher **Lyrics::itsFetcher = 0;
std::set<MPD::Song *> Lyrics::itsDownloaded;
#endif // HAVE_CURL_CURL_H
Lyrics *myLyrics = new Lyrics; Lyrics *myLyrics = new Lyrics;
void Lyrics::Init() void Lyrics::Init()
{ {
w = new Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, brNone); w = new Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, brNone);
itsFolder = home_path + LYRICS_FOLDER;
isInitialized = 1; isInitialized = 1;
} }
@@ -107,6 +113,12 @@ void Lyrics::SwitchTo()
// take lyrics if they were downloaded // take lyrics if they were downloaded
if (isReadyToTake) if (isReadyToTake)
Take(); Take();
if (isDownloadInProgress || !itsDownloaded.empty())
{
ShowMessage("Lyrics are being downloaded...");
return;
}
# endif // HAVE_CURL_CURL_H # endif // HAVE_CURL_CURL_H
if (const MPD::Song *s = myScreen->CurrentSong()) if (const MPD::Song *s = myScreen->CurrentSong())
@@ -140,9 +152,55 @@ void Lyrics::SpacePressed()
} }
#ifdef HAVE_CURL_CURL_H #ifdef HAVE_CURL_CURL_H
void *Lyrics::DownloadWrapper(void *this_ptr) void Lyrics::DownloadInBackground(const MPD::Song *s)
{ {
return static_cast<Lyrics *>(this_ptr)->Download(); if (!s || s->GetArtist().empty() || s->GetTitle().empty())
return;
if (!s->Localized())
const_cast<MPD::Song *>(s)->Localize();
std::string filename = GenerateFilename(*s);
std::ifstream f(filename.c_str());
if (f.is_open())
{
f.close();
return;
}
ShowMessage("Fetching lyrics for %s...", s->toString(Config.song_status_format_no_colors).c_str());
// we need to copy it and store separetely since this song may get deleted in the meantime.
MPD::Song *s_copy = new MPD::Song(*s);
itsDownloaded.insert(s_copy);
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);
}
void *Lyrics::DownloadInBackgroundImpl(void *void_ptr)
{
MPD::Song *s = static_cast<MPD::Song *>(void_ptr);
std::string artist = Curl::escape(locale_to_utf_cpy(s->GetArtist()));
std::string title = Curl::escape(locale_to_utf_cpy(s->GetTitle()));
LyricsFetcher::Result result;
bool fetcher_defined = itsFetcher && *itsFetcher;
for (LyricsFetcher **plugin = fetcher_defined ? itsFetcher : lyricsPlugins; *plugin != 0; ++plugin)
{
result = (*plugin)->fetch(artist, title);
if (result.first)
break;
if (fetcher_defined)
break;
}
if (result.first == true)
Save(GenerateFilename(*s), result.second);
delete s;
itsDownloaded.erase(s);
pthread_exit(0);
} }
void *Lyrics::Download() void *Lyrics::Download()
@@ -169,7 +227,7 @@ void *Lyrics::Download()
if (result.first == true) if (result.first == true)
{ {
Save(result.second); Save(itsFilename, result.second);
utf_to_locale(result.second); utf_to_locale(result.second);
w->Clear(); w->Clear();
@@ -181,37 +239,44 @@ void *Lyrics::Download()
isReadyToTake = 1; isReadyToTake = 1;
pthread_exit(0); pthread_exit(0);
} }
void *Lyrics::DownloadWrapper(void *this_ptr)
{
return static_cast<Lyrics *>(this_ptr)->Download();
}
#endif // HAVE_CURL_CURL_H #endif // HAVE_CURL_CURL_H
void Lyrics::SetFilename() std::string Lyrics::GenerateFilename(const MPD::Song &s)
{ {
std::string filename;
if (Config.store_lyrics_in_song_dir) if (Config.store_lyrics_in_song_dir)
{ {
if (itsSong.isFromDB()) if (s.isFromDB())
{ {
itsFilename = Config.mpd_music_dir; filename = Config.mpd_music_dir;
itsFilename += "/"; filename += "/";
itsFilename += itsSong.GetFile(); filename += s.GetFile();
} }
else else
itsFilename = itsSong.GetFile(); filename = s.GetFile();
// replace song's extension with .txt // replace song's extension with .txt
size_t dot = itsFilename.rfind('.'); size_t dot = filename.rfind('.');
assert(dot != std::string::npos); assert(dot != std::string::npos);
itsFilename.resize(dot); filename.resize(dot);
itsFilename += ".txt"; filename += ".txt";
} }
else else
{ {
std::string file = locale_to_utf_cpy(itsSong.GetArtist()); std::string file = locale_to_utf_cpy(s.GetArtist());
file += " - "; file += " - ";
file += locale_to_utf_cpy(itsSong.GetTitle()); file += locale_to_utf_cpy(s.GetTitle());
file += ".txt"; file += ".txt";
EscapeUnallowedChars(file); EscapeUnallowedChars(file);
itsFilename = itsFolder; filename = itsFolder;
itsFilename += "/"; filename += "/";
itsFilename += file; filename += file;
} }
return filename;
} }
void Lyrics::Load() void Lyrics::Load()
@@ -225,7 +290,7 @@ void Lyrics::Load()
assert(!itsSong.GetTitle().empty()); assert(!itsSong.GetTitle().empty());
itsSong.Localize(); itsSong.Localize();
SetFilename(); itsFilename = GenerateFilename(itsSong);
mkdir(itsFolder.c_str() mkdir(itsFolder.c_str()
# ifndef WIN32 # ifndef WIN32
@@ -292,9 +357,9 @@ void Lyrics::Edit()
} }
#ifdef HAVE_CURL_CURL_H #ifdef HAVE_CURL_CURL_H
void Lyrics::Save(const std::string &lyrics) void Lyrics::Save(const std::string &filename, const std::string &lyrics)
{ {
std::ofstream output(itsFilename.c_str()); std::ofstream output(filename.c_str());
if (output.is_open()) if (output.is_open())
{ {
output << lyrics; output << lyrics;

View File

@@ -33,7 +33,7 @@ class Lyrics : public Screen<Scrollpad>
public: public:
Lyrics() : ReloadNP(0), Lyrics() : ReloadNP(0),
# ifdef HAVE_CURL_CURL_H # ifdef HAVE_CURL_CURL_H
isReadyToTake(0), isDownloadInProgress(0), itsFetcher(0), isReadyToTake(0), isDownloadInProgress(0),
# endif // HAVE_CURL_CURL_H # endif // HAVE_CURL_CURL_H
itsScrollBegin(0) { } itsScrollBegin(0) { }
@@ -53,8 +53,10 @@ class Lyrics : public Screen<Scrollpad>
void Edit(); void Edit();
void Refetch(); void Refetch();
# ifdef HAVE_CURL_CURL_H # ifdef HAVE_CURL_CURL_H
void ToggleFetcher(); static void ToggleFetcher();
static void DownloadInBackground(const MPD::Song *s);
# endif // HAVE_CURL_CURL_H # endif // HAVE_CURL_CURL_H
bool ReloadNP; bool ReloadNP;
@@ -65,25 +67,29 @@ class Lyrics : public Screen<Scrollpad>
private: private:
void Load(); void Load();
void SetFilename();
std::string itsFilename;
std::string itsFolder;
# ifdef HAVE_CURL_CURL_H # ifdef HAVE_CURL_CURL_H
static void *DownloadInBackgroundImpl(void *);
// storage for songs for which lyrics are being downloaded at the moment
static std::set<MPD::Song *> itsDownloaded;
void *Download(); void *Download();
static void *DownloadWrapper(void *); static void *DownloadWrapper(void *);
static void Save(const std::string &filename, const std::string &lyrics);
void Save(const std::string &lyrics);
void Take(); void Take();
bool isReadyToTake; bool isReadyToTake;
bool isDownloadInProgress; bool isDownloadInProgress;
pthread_t itsDownloader; pthread_t itsDownloader;
LyricsFetcher **itsFetcher;
static LyricsFetcher **itsFetcher;
# endif // HAVE_CURL_CURL_H # endif // HAVE_CURL_CURL_H
size_t itsScrollBegin; size_t itsScrollBegin;
MPD::Song itsSong; MPD::Song itsSong;
std::string itsFilename;
static std::string itsFolder;
static std::string GenerateFilename(const MPD::Song &s);
}; };
extern Lyrics *myLyrics; extern Lyrics *myLyrics;

View File

@@ -1432,6 +1432,11 @@ int main(int argc, char *argv[])
{ {
myLyrics->ToggleFetcher(); myLyrics->ToggleFetcher();
} }
else if (Keypressed(input, Key.ToggleFetchingLyricsInBackground))
{
Config.fetch_lyrics_in_background = !Config.fetch_lyrics_in_background;
ShowMessage("Fetching lyrics for currently playing song in background: %s", Config.fetch_lyrics_in_background ? "On" : "Off");
}
# endif // HAVE_CURL_CURL_H # endif // HAVE_CURL_CURL_H
else if (Keypressed(input, Key.ToggleAutoCenter)) else if (Keypressed(input, Key.ToggleAutoCenter))
{ {

View File

@@ -251,6 +251,7 @@ void NcmpcppKeys::SetDefaults()
ToggleInterface[0] = '\\'; ToggleInterface[0] = '\\';
ToggleSeparatorsInPlaylist[0] = '!'; ToggleSeparatorsInPlaylist[0] = '!';
ToggleLyricsDB[0] = 'L'; ToggleLyricsDB[0] = 'L';
ToggleFetchingLyricsInBackground[0] = 'F';
GoToParentDir[0] = KEY_BACKSPACE; GoToParentDir[0] = KEY_BACKSPACE;
SwitchTagTypeList[0] = '`'; SwitchTagTypeList[0] = '`';
Quit[0] = 'q'; Quit[0] = 'q';
@@ -339,6 +340,7 @@ void NcmpcppKeys::SetDefaults()
ToggleInterface[1] = NullKey; ToggleInterface[1] = NullKey;
ToggleSeparatorsInPlaylist[1] = NullKey; ToggleSeparatorsInPlaylist[1] = NullKey;
ToggleLyricsDB[1] = NullKey; ToggleLyricsDB[1] = NullKey;
ToggleFetchingLyricsInBackground[1] = NullKey;
GoToParentDir[1] = 127; GoToParentDir[1] = 127;
SwitchTagTypeList[1] = NullKey; SwitchTagTypeList[1] = NullKey;
Quit[1] = 'Q'; Quit[1] = 'Q';
@@ -404,6 +406,7 @@ void NcmpcppConfig::SetDefaults()
albums_in_tag_editor = false; albums_in_tag_editor = false;
incremental_seeking = true; incremental_seeking = true;
now_playing_lyrics = false; now_playing_lyrics = false;
fetch_lyrics_in_background = false;
local_browser_show_hidden_files = false; local_browser_show_hidden_files = false;
search_in_db = true; search_in_db = true;
display_screens_numbers_on_start = true; display_screens_numbers_on_start = true;
@@ -628,6 +631,8 @@ void NcmpcppKeys::Read()
GetKeys(key, ToggleSeparatorsInPlaylist); GetKeys(key, ToggleSeparatorsInPlaylist);
else if (key.find("key_toggle_lyrics_db ") != std::string::npos) else if (key.find("key_toggle_lyrics_db ") != std::string::npos)
GetKeys(key, ToggleLyricsDB); GetKeys(key, ToggleLyricsDB);
else if (key.find("key_toggle_fetching_lyrics_for_current_song_in_background ") != std::string::npos)
GetKeys(key, ToggleFetchingLyricsInBackground);
else if (key.find("key_go_to_containing_directory ") != std::string::npos) else if (key.find("key_go_to_containing_directory ") != std::string::npos)
GetKeys(key, GoToContainingDir); GetKeys(key, GoToContainingDir);
else if (key.find("key_go_to_media_library ") != std::string::npos) else if (key.find("key_go_to_media_library ") != std::string::npos)
@@ -987,6 +992,10 @@ void NcmpcppConfig::Read()
{ {
now_playing_lyrics = v == "yes"; now_playing_lyrics = v == "yes";
} }
else if (cl.find("fetch_lyrics_for_current_song_in_background") != std::string::npos)
{
fetch_lyrics_in_background = v == "yes";
}
else if (cl.find("ncmpc_like_songs_adding") != std::string::npos) else if (cl.find("ncmpc_like_songs_adding") != std::string::npos)
{ {
ncmpc_like_songs_adding = v == "yes"; ncmpc_like_songs_adding = v == "yes";

View File

@@ -142,6 +142,7 @@ struct NcmpcppKeys
int ToggleInterface[2]; int ToggleInterface[2];
int ToggleSeparatorsInPlaylist[2]; int ToggleSeparatorsInPlaylist[2];
int ToggleLyricsDB[2]; int ToggleLyricsDB[2];
int ToggleFetchingLyricsInBackground[2];
int GoToParentDir[2]; int GoToParentDir[2];
int SwitchTagTypeList[2]; int SwitchTagTypeList[2];
int Quit[2]; int Quit[2];
@@ -227,6 +228,7 @@ struct NcmpcppConfig
bool albums_in_tag_editor; bool albums_in_tag_editor;
bool incremental_seeking; bool incremental_seeking;
bool now_playing_lyrics; bool now_playing_lyrics;
bool fetch_lyrics_in_background;
bool local_browser_show_hidden_files; bool local_browser_show_hidden_files;
bool search_in_db; bool search_in_db;
bool display_screens_numbers_on_start; bool display_screens_numbers_on_start;

View File

@@ -401,6 +401,11 @@ void NcmpcppStatusChanged(MPD::Connection *, MPD::StatusChanges changed, void *)
if (!Config.execute_on_song_change.empty()) if (!Config.execute_on_song_change.empty())
system(Config.execute_on_song_change.c_str()); system(Config.execute_on_song_change.c_str());
# ifdef HAVE_CURL_CURL_H
if (Config.fetch_lyrics_in_background)
Lyrics::DownloadInBackground(myPlaylist->NowPlayingSong());
# endif // HAVE_CURL_CURL_H
if (Mpd.isPlaying() && !(np = Mpd.GetCurrentSong()).Empty()) if (Mpd.isPlaying() && !(np = Mpd.GetCurrentSong()).Empty())
WindowTitle(utf_to_locale_cpy(np.toString(Config.song_window_title_format))); WindowTitle(utf_to_locale_cpy(np.toString(Config.song_window_title_format)));