/*************************************************************************** * Copyright (C) 2008-2009 by Andrzej Rybczak * * electricityispower@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #ifdef WIN32 # include #else # include #endif // WIN32 #include #include "lyrics.h" #include "browser.h" #include "charset.h" #include "global.h" #include "helpers.h" #include "media_library.h" #include "playlist.h" #include "playlist_editor.h" #include "search_engine.h" #include "settings.h" #include "song.h" #include "status.h" #include "tag_editor.h" #ifdef WIN32 # define LYRICS_FOLDER HOME_FOLDER"\\lyrics\\" #else # define LYRICS_FOLDER "/.lyrics" #endif // WIN32 using namespace Global; const std::string Lyrics::Folder = home_path + LYRICS_FOLDER; bool Lyrics::Reload = 0; #ifdef HAVE_CURL_CURL_H bool Lyrics::Ready = 0; #ifdef HAVE_PTHREAD_H pthread_t *Lyrics::Downloader = 0; pthread_mutex_t Global::CurlLock = PTHREAD_MUTEX_INITIALIZER; #endif // HAVE_PTHREAD_H #endif // HAVE_CURL_CURL_H Lyrics *myLyrics = new Lyrics; void Lyrics::Init() { w = new Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, brNone); w->SetTimeout(ncmpcpp_window_timeout); isInitialized = 1; } void Lyrics::Resize() { w->Resize(COLS, MainHeight); w->MoveTo(0, MainStartY); hasToBeResized = 0; } void Lyrics::Update() { # if defined(HAVE_CURL_CURL_H) && defined(HAVE_PTHREAD_H) if (myLyrics->Ready) myLyrics->Take(); # endif // HAVE_CURL_CURL_H && HAVE_PTHREAD_H if (!Reload) return; const MPD::Song *s = myPlaylist->NowPlayingSong(); if (s && !s->GetArtist().empty() && !s->GetTitle().empty()) SwitchTo(); else Reload = 0; } void Lyrics::SwitchTo() { if (myScreen == this && !Reload) { myOldScreen->SwitchTo(); } else { if (!isInitialized) Init(); # if defined(HAVE_CURL_CURL_H) && defined(HAVE_PTHREAD_H) if (Downloader && !Ready) { ShowMessage("Lyrics are being downloaded..."); return; } else if (Ready) Take(); # endif // HAVE_CURL_CURL_H && HAVE_PTHREAD_H const MPD::Song *s = Reload ? myPlaylist->NowPlayingSong() : myScreen->CurrentSong(); if (!s) return; if (!s->GetArtist().empty() && !s->GetTitle().empty()) { if (hasToBeResized) Resize(); itsScrollBegin = 0; itsSong = *s; itsSong.Localize(); if (!Reload) { myOldScreen = myScreen; myScreen = this; } RedrawHeader = 1; w->Clear(); # ifdef HAVE_CURL_CURL_H static_cast(*w) << "Fetching lyrics..."; w->Window::Refresh(); # ifdef HAVE_PTHREAD_H if (!Downloader) # endif // HAVE_PTHREAD_H # endif // HAVE_CURL_CURL_H { std::string file = locale_to_utf_cpy(itsSong.GetArtist()) + " - " + locale_to_utf_cpy(itsSong.GetTitle()) + ".txt"; EscapeUnallowedChars(file); itsFilenamePath = Folder + "/" + file; mkdir(Folder.c_str() # ifndef WIN32 , 0755 # endif // !WIN32 ); std::ifstream input(itsFilenamePath.c_str()); if (input.is_open()) { bool first = 1; std::string line; while (getline(input, line)) { if (!first) *w << "\n"; utf_to_locale(line); *w << line; first = 0; } w->Flush(); } else { # if defined(HAVE_CURL_CURL_H) && defined(HAVE_PTHREAD_H) Downloader = new pthread_t; pthread_create(Downloader, 0, Get, this); # elif defined(HAVE_CURL_CURL_H) Get(this); w->Flush(); # else *w << "Local lyrics not found. As ncmpcpp has been compiled without curl support, you can put appropriate lyrics into " << Folder << " directory (file syntax is \"$ARTIST - $TITLE.txt\") or recompile ncmpcpp with curl support."; w->Flush(); # endif } } } Reload = 0; } } std::basic_string Lyrics::Title() { std::basic_string result = U("Lyrics: "); result += Scroller(itsSong.toString("{%a - %t}"), COLS-result.length()-(Config.new_design ? 2 : VolumeState.length()), itsScrollBegin); return result; } void Lyrics::SpacePressed() { Config.now_playing_lyrics = !Config.now_playing_lyrics; ShowMessage("Reload lyrics if song changes: %s", Config.now_playing_lyrics ? "On" : "Off"); } #ifdef HAVE_CURL_CURL_H void *Lyrics::Get(void *screen_void_ptr) { Lyrics *screen = static_cast(screen_void_ptr); const Plugin *my_lyrics = ChoosePlugin(Config.lyrics_db); std::string result; std::string artist = locale_to_utf_cpy(screen->itsSong.GetArtist()); std::string title = locale_to_utf_cpy(screen->itsSong.GetTitle()); char *c_artist = curl_easy_escape(0, artist.c_str(), artist.length()); char *c_title = curl_easy_escape(0, title.c_str(), title.length()); std::string url = my_lyrics->url; url.replace(url.find("%artist%"), 8, c_artist); url.replace(url.find("%title%"), 7, c_title); CURLcode code; pthread_mutex_lock(&CurlLock); CURL *lyrics = curl_easy_init(); curl_easy_setopt(lyrics, CURLOPT_URL, url.c_str()); curl_easy_setopt(lyrics, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(lyrics, CURLOPT_WRITEDATA, &result); curl_easy_setopt(lyrics, CURLOPT_CONNECTTIMEOUT, 10); curl_easy_setopt(lyrics, CURLOPT_NOSIGNAL, 1); code = curl_easy_perform(lyrics); curl_easy_cleanup(lyrics); pthread_mutex_unlock(&CurlLock); curl_free(c_artist); curl_free(c_title); if (code != CURLE_OK) { *screen->w << "Error while fetching lyrics: " << curl_easy_strerror(code); Ready = 1; pthread_exit(0); } size_t a, b; a = result.find(my_lyrics->tag_open)+strlen(my_lyrics->tag_open); b = result.find(my_lyrics->tag_close, a); result = result.substr(a, b-a); if (my_lyrics->not_found(result)) { *screen->w << "Not found"; Ready = 1; pthread_exit(0); } for (size_t i = result.find("<"); i != std::string::npos; i = result.find("<")) result.replace(i, 4, "<"); for (size_t i = result.find(">"); i != std::string::npos; i = result.find(">")) result.replace(i, 4, ">"); EscapeHtml(result); Trim(result); *screen->w << utf_to_locale_cpy(result); std::ofstream output(screen->itsFilenamePath.c_str()); if (output.is_open()) { output << result; output.close(); } Ready = 1; pthread_exit(0); } #endif // HAVE_CURL_CURL_H void Lyrics::Edit() { if (myScreen != this) return; if (Config.external_editor.empty()) { ShowMessage("External editor is not set!"); return; } ShowMessage("Opening lyrics in external editor..."); if (Config.use_console_editor) { system(("/bin/sh -c \"" + Config.external_editor + " \\\"" + itsFilenamePath + "\\\"\"").c_str()); // below is needed as screen gets cleared, but apparently // ncurses doesn't know about it, so we need to reload main screen endwin(); initscr(); curs_set(0); } else system(("nohup " + Config.external_editor + " \"" + itsFilenamePath + "\" > /dev/null 2>&1 &").c_str()); } #ifdef HAVE_CURL_CURL_H #ifdef HAVE_PTHREAD_H void Lyrics::Take() { if (!Ready) return; pthread_join(*Downloader, NULL); w->Flush(); delete Downloader; Downloader = 0; Ready = 0; } #endif // HAVE_PTHREAD_H const unsigned Lyrics::DBs = 1; // number of currently supported lyrics databases const char *Lyrics::PluginsList[] = { "lyricsplugin.com", 0 }; const char *Lyrics::GetPluginName(int offset) { return PluginsList[offset]; } bool Lyrics::LyricsPlugin_NotFound(const std::string &s) { if (s.empty()) return true; for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) if (isprint(*it)) return false; return true; } const Lyrics::Plugin Lyrics::LyricsPlugin = { "http://www.lyricsplugin.com/winamp03/plugin/?artist=%artist%&title=%title%", "
", "
", LyricsPlugin_NotFound }; const Lyrics::Plugin *Lyrics::ChoosePlugin(int i) { switch (i) { case 0: return &LyricsPlugin; default: return &LyricsPlugin; } } #endif // HAVE_CURL_CURL_H