diff --git a/src/Makefile.am b/src/Makefile.am index 4b3b3f19..8c86a601 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,10 @@ bin_PROGRAMS = ncmpcpp ncmpcpp_SOURCES = browser.cpp charset.cpp clock.cpp conv.cpp curl_handle.cpp \ - display.cpp error.cpp help.cpp helpers.cpp info.cpp lyrics.cpp lyrics_fetcher.cpp \ - media_library.cpp menu.cpp mpdpp.cpp ncmpcpp.cpp outputs.cpp playlist.cpp \ - playlist_editor.cpp scrollpad.cpp search_engine.cpp sel_items_adder.cpp server_info.cpp \ - settings.cpp song.cpp song_info.cpp status.cpp tag_editor.cpp tiny_tag_editor.cpp \ - tolower.cpp visualizer.cpp window.cpp + display.cpp error.cpp help.cpp helpers.cpp lastfm.cpp lastfm_service.cpp lyrics.cpp \ + lyrics_fetcher.cpp media_library.cpp menu.cpp mpdpp.cpp ncmpcpp.cpp outputs.cpp \ + playlist.cpp playlist_editor.cpp scrollpad.cpp search_engine.cpp sel_items_adder.cpp \ + server_info.cpp settings.cpp song.cpp song_info.cpp status.cpp tag_editor.cpp \ + tiny_tag_editor.cpp tolower.cpp visualizer.cpp window.cpp # set the include path found by configure INCLUDES= $(all_includes) @@ -12,7 +12,7 @@ INCLUDES= $(all_includes) # the library search path. ncmpcpp_LDFLAGS = $(all_libraries) noinst_HEADERS = browser.h charset.h clock.h conv.h curl_handle.h display.h \ - error.h global.h help.h helpers.h home.h info.h lyrics.h lyrics_fetcher.h \ - media_library.h menu.h mpdpp.h outputs.h playlist_editor.h screen.h scrollpad.h \ - search_engine.h sel_items_adder.h server_info.h settings.h song.h song_info.h \ - tag_editor.h tiny_tag_editor.h tolower.h visualizer.h window.h + error.h global.h help.h helpers.h home.h lastfm.h lastfm_service.h lyrics.h \ + lyrics_fetcher.h media_library.h menu.h mpdpp.h outputs.h playlist_editor.h screen.h \ + scrollpad.h search_engine.h sel_items_adder.h server_info.h settings.h song.h \ + song_info.h tag_editor.h tiny_tag_editor.h tolower.h visualizer.h window.h diff --git a/src/error.h b/src/error.h index 31ee2b20..01fbb3a0 100644 --- a/src/error.h +++ b/src/error.h @@ -29,6 +29,8 @@ #include +#define Error(msg) std::cerr << "ncmpcpp: " << msg; + void FatalError(const std::string &msg) GNUC_NORETURN; #endif diff --git a/src/help.cpp b/src/help.cpp index 0f4e0c49..a055f771 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -304,6 +304,10 @@ void Help::GetKeybindings() *w << DisplayKeys(Key.SwitchTagTypeList) << "Refetch lyrics\n"; + *w << "\n\n " << fmtBold << "Keys - Artist info\n -----------------------------------------\n" << fmtBoldEnd; + *w << DisplayKeys(Key.SwitchTagTypeList) << "Refetch artist info\n"; + + # ifdef HAVE_TAGLIB_H *w << "\n\n " << fmtBold << "Keys - Tiny tag editor\n -----------------------------------------\n" << fmtBoldEnd; *w << DisplayKeys(Key.Enter) << "Edit tag\n"; diff --git a/src/info.cpp b/src/info.cpp deleted file mode 100644 index ab840f6e..00000000 --- a/src/info.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008-2010 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 "info.h" -#include "curl_handle.h" - -#ifdef HAVE_CURL_CURL_H -# include -# ifdef WIN32 -# include -# else -# include -# endif // WIN32 -#endif - -#include "browser.h" -#include "charset.h" -#include "global.h" -#include "media_library.h" -#include "playlist.h" -#include "playlist_editor.h" -#include "search_engine.h" -#include "status.h" -#include "tag_editor.h" - -using Global::MainHeight; -using Global::MainStartY; -using Global::myScreen; -using Global::myOldScreen; - -#ifdef HAVE_CURL_CURL_H -const std::string Info::Folder = home_path + HOME_FOLDER"artists"; -bool Info::ArtistReady = 0; - -pthread_t *Info::Downloader = 0; - -#endif // HAVE_CURL_CURL_H - -Info *myInfo = new Info; - -void Info::Init() -{ - w = new Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, brNone); - isInitialized = 1; -} - -void Info::Resize() -{ - w->Resize(COLS, MainHeight); - w->MoveTo(0, MainStartY); - hasToBeResized = 0; -} - -std::basic_string Info::Title() -{ - return TO_WSTRING(itsTitle); -} - -#ifdef HAVE_CURL_CURL_H -void Info::Update() -{ - if (!ArtistReady) - return; - pthread_join(*Downloader, 0); - w->Flush(); - w->Refresh(); - delete Downloader; - Downloader = 0; - ArtistReady = 0; -} - -void Info::GetArtist() -{ - if (myScreen == this) - { - myOldScreen->SwitchTo(); - } - else - { - if (!isInitialized) - Init(); - - if (Downloader && !ArtistReady) - { - ShowMessage("Artist info is being downloaded..."); - return; - } - else if (ArtistReady) - Update(); - - MPD::Song *s = myScreen->CurrentSong(); - - if (!s && myScreen->ActiveWindow() != myLibrary->Artists) - return; - - itsArtist = !s ? myLibrary->Artists->Current() : s->GetArtist(); - - if (itsArtist.empty()) - return; - - if (hasToBeResized) - Resize(); - myOldScreen = myScreen; - myScreen = this; - Global::RedrawHeader = 1; - itsTitle = "Artist info - " + itsArtist; - w->Clear(); - w->Reset(); - static_cast(*w) << "Fetching artist info..."; - w->Window::Refresh(); - if (!Downloader) - { - locale_to_utf(itsArtist); - - std::string file = itsArtist + ".txt"; - ToLower(file); - 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; - } - input.close(); - w->SetFormatting(fmtBold, U("\n\nSimilar artists:\n"), fmtBoldEnd, 0); - w->SetFormatting(Config.color2, U("\n * "), clEnd, 1); - // below is used so format won't be removed using RemoveFormatting() by accident. - w->ForgetFormatting(); - w->Flush(); - } - else - { - Downloader = new pthread_t; - pthread_create(Downloader, 0, PrepareArtist, this); - } - } - } -} - -void *Info::PrepareArtist(void *screen_void_ptr) -{ - Info *screen = static_cast(screen_void_ptr); - - std::string url = "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist="; - url += Curl::escape(screen->itsArtist); - url += "&api_key=d94e5b6e26469a2d1ffae8ef20131b79"; - - std::string result; - CURLcode code = Curl::perform(result, url); - - if (code != CURLE_OK) - { - *screen->w << "Error while fetching artist's info: " << curl_easy_strerror(code); - ArtistReady = 1; - pthread_exit(0); - } - - size_t a, b; - bool save = 1; - - a = result.find("status=\"failed\""); - - if (a != std::string::npos) - { - StripHtmlTags(result); - *screen->w << "Last.fm returned an error message: " << result; - ArtistReady = 1; - pthread_exit(0); - } - - std::vector similar; - for (size_t i = result.find(""); i != std::string::npos; i = result.find("")) - { - result[i] = '.'; - size_t j = result.find(""); - result[j] = '.'; - i += static_strlen(""); - similar.push_back(result.substr(i, j-i)); - StripHtmlTags(similar.back()); - } - std::vector urls; - for (size_t i = result.find(""); i != std::string::npos; i = result.find("")) - { - result[i] = '.'; - size_t j = result.find(""); - result[j] = '.'; - i += static_strlen(""); - urls.push_back(result.substr(i, j-i)); - } - - bool parse_failed = 0; - if ((a = result.find("")) != std::string::npos) - { - a += static_strlen(""); - if ((b = result.find("")) == std::string::npos) - parse_failed = 1; - } - else - parse_failed = 1; - if (parse_failed) - { - *screen->w << "Error: Fetched data could not be parsed"; - ArtistReady = 1; - pthread_exit(0); - } - - if (a == b) - { - result = "No description available for this artist."; - save = 0; - } - else - { - a += static_strlen(""); - result = result.substr(a, b-a); - } - - StripHtmlTags(result); - Trim(result); - - std::ostringstream filebuffer; - if (save) - filebuffer << result; - utf_to_locale(result); - *screen->w << result; - - if (save) - filebuffer << "\n\nSimilar artists:\n"; - *screen->w << fmtBold << "\n\nSimilar artists:\n" << fmtBoldEnd; - for (size_t i = 1; i < similar.size(); ++i) - { - if (save) - filebuffer << "\n * " << similar[i] << " (" << urls[i] << ")"; - utf_to_locale(similar[i]); - utf_to_locale(urls[i]); - *screen->w << "\n" << Config.color2 << " * " << clEnd << similar[i] << " (" << urls[i] << ")"; - } - - if (save) - filebuffer << "\n\n" << urls.front(); - utf_to_locale(urls.front()); - *screen->w << "\n\n" << urls.front(); - - if (save) - { - std::ofstream output(screen->itsFilenamePath.c_str()); - if (output.is_open()) - { - output << filebuffer.str(); - output.close(); - } - } - ArtistReady = 1; - pthread_exit(0); -} -#endif // HVAE_CURL_CURL_H - diff --git a/src/lastfm.cpp b/src/lastfm.cpp new file mode 100644 index 00000000..efdc9300 --- /dev/null +++ b/src/lastfm.cpp @@ -0,0 +1,225 @@ +/*************************************************************************** + * Copyright (C) 2008-2010 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 "lastfm.h" + +#ifdef HAVE_CURL_CURL_H + +#ifdef WIN32 +# include +#else +# include +#endif // WIN32 + +#include +#include +#include +#include + +#include "charset.h" +#include "global.h" + +using Global::MainHeight; +using Global::MainStartY; +using Global::myScreen; +using Global::myOldScreen; + +Lastfm *myLastfm = new Lastfm; + +void Lastfm::Init() +{ + w = new Scrollpad(0, MainStartY, COLS, MainHeight, "", Config.main_color, brNone); + isInitialized = 1; +} + +void Lastfm::Resize() +{ + w->Resize(COLS, MainHeight); + w->MoveTo(0, MainStartY); + hasToBeResized = 0; +} + +std::basic_string Lastfm::Title() +{ + return itsTitle; +} + +void Lastfm::Update() +{ + if (isReadyToTake) + { + pthread_join(itsDownloader, 0); + w->Flush(); + w->Refresh(); + isDownloadInProgress = 0; + isReadyToTake = 0; + } +} + +void Lastfm::SwitchTo() +{ + if (myScreen == this) + return myOldScreen->SwitchTo(); + + if (!isInitialized) + Init(); + + if (hasToBeResized) + Resize(); + + // if something is ready to take, take it + Update(); + + Load(); + + myOldScreen = myScreen; + myScreen = this; + + Global::RedrawHeader = 1; +} + +void Lastfm::Load() +{ + if (isDownloadInProgress) + return; + + assert(itsService.get()); + assert(itsService->checkArgs(itsArgs)); + + SetTitleAndFolder(); + + w->Clear(); + w->Reset(); + + std::string artist = itsArgs.find("artist")->second; + locale_to_utf(artist); + + std::string file = artist + ".txt"; + ToLower(file); + EscapeUnallowedChars(file); + + itsFilename = itsFolder + "/" + file; + + mkdir(itsFolder.c_str() +# ifndef WIN32 + , 0755 +# endif // !WIN32 + ); + + std::ifstream input(itsFilename.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; + } + input.close(); + itsService->colorizeOutput(*w); + } + else + { + *w << "Fetching informations... "; + pthread_create(&itsDownloader, 0, DownloadWrapper, this); + isDownloadInProgress = 1; + } + w->Flush(); +} + +void Lastfm::SetTitleAndFolder() +{ + if (dynamic_cast(itsService.get())) + { + itsTitle = U("Artist info - "); + itsTitle += TO_WSTRING(itsArgs.find("artist")->second); + itsFolder = home_path + HOME_FOLDER + "artists"; + } +} + +void *Lastfm::DownloadWrapper(void *this_ptr) +{ + static_cast(this_ptr)->Download(); + pthread_exit(0); +} + +void Lastfm::Download() +{ + LastfmService::Result result = itsService->fetch(itsArgs); + + if (result.first) + { + Save(result.second); + w->Clear(); + utf_to_locale(result.second); + *w << result.second; + itsService->colorizeOutput(*w); + } + else + *w << clRed << result.second << clEnd; + + isReadyToTake = 1; +} + +void Lastfm::Save(const std::string &data) +{ + std::ofstream output(itsFilename.c_str()); + if (output.is_open()) + { + output << data; + output.close(); + } + else + Error("couldn't save file \"" << itsFilename << "\""); +} + +void Lastfm::Refetch() +{ + if (!remove(itsFilename.c_str())) + { + Load(); + } + else + { + static const char msg[] = "Couldn't remove \"%s\": %s"; + ShowMessage(msg, Shorten(TO_WSTRING(itsFilename), COLS-static_strlen(msg)-25).c_str(), strerror(errno)); + } +} + +bool Lastfm::SetArtistInfoArgs(const std::string &artist, const std::string &lang) +{ + if (isDownloadInProgress) + return false; + + itsService.reset(new ArtistInfo); + itsArgs.clear(); + itsArgs["artist"] = artist; + if (!lang.empty()) + itsArgs["lang"] = lang; + + return true; +} + +#endif // HVAE_CURL_CURL_H + diff --git a/src/info.h b/src/lastfm.h similarity index 69% rename from src/info.h rename to src/lastfm.h index 9b68d798..3c09c944 100644 --- a/src/info.h +++ b/src/lastfm.h @@ -18,24 +18,31 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef _H_INFO -#define _H_INFO +#ifndef _H_LASTFM +#define _H_LASTFM -#include "ncmpcpp.h" -#include "mpdpp.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_CURL_CURL_H + +#include + +#include "lastfm_service.h" #include "screen.h" -class Info : public Screen +class Lastfm : public Screen { public: - virtual void SwitchTo() { } + Lastfm() : isReadyToTake(0), isDownloadInProgress(0) { } + + virtual void SwitchTo(); virtual void Resize(); virtual std::basic_string Title(); -# ifdef HAVE_CURL_CURL_H virtual void Update(); -# endif // HAVE_CURL_CURL_H virtual void EnterPressed() { } virtual void SpacePressed() { } @@ -44,30 +51,39 @@ class Info : public Screen virtual List *GetList() { return 0; } -# ifdef HAVE_CURL_CURL_H - void GetArtist(); -# endif // HAVE_CURL_CURL_H + void Refetch(); + + bool SetArtistInfoArgs(const std::string &artist, const std::string &lang = ""); protected: virtual void Init(); private: + std::basic_string itsTitle; + std::string itsArtist; - std::string itsTitle; - std::string itsFilenamePath; + std::string itsFilename; -# ifdef HAVE_CURL_CURL_H - static void *PrepareArtist(void *); + std::string itsFolder; - static const std::string Folder; - static bool ArtistReady; + std::auto_ptr itsService; + LastfmService::Args itsArgs; - static pthread_t *Downloader; + void Load(); + void Save(const std::string &data); + void SetTitleAndFolder(); -# endif // HAVE_CURL_CURL_H + void Download(); + static void *DownloadWrapper(void *); + + bool isReadyToTake; + bool isDownloadInProgress; + pthread_t itsDownloader; }; -extern Info *myInfo; +extern Lastfm *myLastfm; + +#endif // HAVE_CURL_CURL_H #endif diff --git a/src/lyrics.cpp b/src/lyrics.cpp index a19d51ff..caf01349 100644 --- a/src/lyrics.cpp +++ b/src/lyrics.cpp @@ -79,7 +79,8 @@ void Lyrics::Update() # endif // HAVE_CURL_CURL_H if (ReloadNP) { - if (const MPD::Song *s = myPlaylist->NowPlayingSong()) + const MPD::Song *s = myPlaylist->NowPlayingSong(); + if (s && !s->GetArtist().empty() && !s->GetTitle().empty()) { Global::RedrawHeader = 1; itsScrollBegin = 0; @@ -109,11 +110,14 @@ void Lyrics::SwitchTo() // for taking lyrics if they were downloaded Update(); - if (const MPD::Song *s = myOldScreen->CurrentSong()) + const MPD::Song *s = myOldScreen->CurrentSong(); + if (s && !s->GetArtist().empty() && !s->GetTitle().empty()) { itsSong = *s; Load(); } + else + return myOldScreen->SwitchTo(); Global::RedrawHeader = 1; } @@ -181,8 +185,9 @@ void Lyrics::Load() if (DownloadInProgress) return; # endif // HAVE_CURL_CURL_H - if (itsSong.GetArtist().empty() || itsSong.GetTitle().empty()) - return; + + assert(!itsSong.GetArtist().empty()); + assert(!itsSong.GetTitle().empty()); itsSong.Localize(); std::string file = locale_to_utf_cpy(itsSong.GetArtist()) + " - " + locale_to_utf_cpy(itsSong.GetTitle()) + ".txt"; diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index 0d67f57b..e73c808d 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -48,7 +48,7 @@ #include "settings.h" #include "song.h" #include "song_info.h" -#include "info.h" +#include "lastfm.h" #include "outputs.h" #include "status.h" #include "tag_editor.h" @@ -140,7 +140,7 @@ namespace mySearcher->hasToBeResized = 1; myLibrary->hasToBeResized = 1; myPlaylistEditor->hasToBeResized = 1; - myInfo->hasToBeResized = 1; + myLastfm->hasToBeResized = 1; myLyrics->hasToBeResized = 1; mySelectedItemsAdder->hasToBeResized = 1; @@ -1899,7 +1899,7 @@ int main(int argc, char *argv[]) if (myScreen == myPlaylist) myPlaylist->EnableHighlighting(); } - else if (myScreen == myHelp || myScreen == myLyrics || myScreen == myInfo) + else if (myScreen == myHelp || myScreen == myLyrics || myScreen == myLastfm) { LockStatusbar(); Statusbar() << "Find: "; @@ -2042,6 +2042,10 @@ int main(int argc, char *argv[]) { myLyrics->Refetch(); } + else if (myScreen == myLastfm) + { + myLastfm->Refetch(); + } } else if (Keypressed(input, Key.SongInfo)) { @@ -2050,7 +2054,22 @@ int main(int argc, char *argv[]) # ifdef HAVE_CURL_CURL_H else if (Keypressed(input, Key.ArtistInfo)) { - myInfo->GetArtist(); + if (myScreen == myLastfm) + { + myLastfm->SwitchTo(); + continue; + } + + std::string artist; + MPD::Song *s = myScreen->CurrentSong(); + + if (s) + artist = s->GetArtist(); + else if (myScreen == myLibrary && myLibrary->Main() == myLibrary->Artists) + artist = myLibrary->Artists->Current(); + + if (!artist.empty() && myLastfm->SetArtistInfoArgs(artist)) + myLastfm->SwitchTo(); } # endif // HAVE_CURL_CURL_H else if (Keypressed(input, Key.Lyrics))