lastfm: redesigned plugins system

This commit is contained in:
Andrzej Rybczak
2010-08-13 05:20:12 +02:00
parent d4a5574146
commit a1cd5ae1aa
8 changed files with 308 additions and 328 deletions

View File

@@ -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

View File

@@ -29,6 +29,8 @@
#include <string>
#define Error(msg) std::cerr << "ncmpcpp: " << msg;
void FatalError(const std::string &msg) GNUC_NORETURN;
#endif

View File

@@ -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";

View File

@@ -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 <fstream>
# ifdef WIN32
# include <io.h>
# else
# include <sys/stat.h>
# 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<my_char_t> 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<Window &>(*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<Info *>(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<std::string> similar;
for (size_t i = result.find("<name>"); i != std::string::npos; i = result.find("<name>"))
{
result[i] = '.';
size_t j = result.find("</name>");
result[j] = '.';
i += static_strlen("<name>");
similar.push_back(result.substr(i, j-i));
StripHtmlTags(similar.back());
}
std::vector<std::string> urls;
for (size_t i = result.find("<url>"); i != std::string::npos; i = result.find("<url>"))
{
result[i] = '.';
size_t j = result.find("</url>");
result[j] = '.';
i += static_strlen("<url>");
urls.push_back(result.substr(i, j-i));
}
bool parse_failed = 0;
if ((a = result.find("<content>")) != std::string::npos)
{
a += static_strlen("<content>");
if ((b = result.find("</content>")) == 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("<![CDATA[");
b -= 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

225
src/lastfm.cpp Normal file
View File

@@ -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 <io.h>
#else
# include <sys/stat.h>
#endif // WIN32
#include <cassert>
#include <cerrno>
#include <fstream>
#include <iostream>
#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<my_char_t> 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<ArtistInfo *>(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<Lastfm *>(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

View File

@@ -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 <memory>
#include "lastfm_service.h"
#include "screen.h"
class Info : public Screen<Scrollpad>
class Lastfm : public Screen<Scrollpad>
{
public:
virtual void SwitchTo() { }
Lastfm() : isReadyToTake(0), isDownloadInProgress(0) { }
virtual void SwitchTo();
virtual void Resize();
virtual std::basic_string<my_char_t> 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<Scrollpad>
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<my_char_t> 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<LastfmService> 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

View File

@@ -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";

View File

@@ -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))