From ea3778da7cb8fb031a9cf4f55cd97d766f326aae Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 15 Aug 2008 20:31:36 +0200 Subject: [PATCH] add lyrics screen --- configure.in | 43 +++++++++++------ src/Makefile.am | 6 +-- src/helpers.cpp | 2 +- src/lyrics.cpp | 107 +++++++++++++++++++++++++++++++++++++++++ src/lyrics.h | 34 +++++++++++++ src/ncmpcpp.cpp | 96 ++++++++++++++++++++++++++++-------- src/ncmpcpp.h | 3 +- src/scrollpad.cpp | 9 +++- src/scrollpad.h | 2 +- src/settings.h | 2 - src/status_checker.cpp | 2 +- 11 files changed, 260 insertions(+), 46 deletions(-) create mode 100644 src/lyrics.cpp create mode 100644 src/lyrics.h diff --git a/configure.in b/configure.in index b5ed7c61..6778f613 100644 --- a/configure.in +++ b/configure.in @@ -11,28 +11,25 @@ AM_PROG_LIBTOOL AC_ARG_ENABLE(unicode, AS_HELP_STRING([--enable-unicode], [Enable utf8 support]), [unicode=$enableval], [unicode=yes]) AC_ARG_WITH(taglib, AS_HELP_STRING([--with-taglib], [Enable tag editor]), [taglib=$withval], [taglib=no]) +AC_ARG_WITH(curl, AS_HELP_STRING([--with-curl], [Enable fetching lyrics from the Internet]), [curl=$withval], [curl=no]) dnl ======================== dnl = checking for ncurses = dnl ======================== if test "$unicode" = "yes" ; then - AC_PATH_PROG(NCURSESW5_CONFIG, ncursesw5-config) - if test "$NCURSESW5_CONFIG" != "" ; then - CPPFLAGS="$CPPFLAGS -DUTF8_ENABLED `$NCURSESW5_CONFIG --cflags`" - LDFLAGS="$LDFLAGS `$NCURSESW5_CONFIG --libs`" - AC_CHECK_LIB(ncursesw, initscr, , AC_MSG_ERROR([ncursesw library is required])) - else - AC_MSG_ERROR([ncursesw5-config executable is missing]) - fi + ncurses_config_bin=ncursesw5-config + ncurses_lib=ncursesw else - AC_PATH_PROG(NCURSES5_CONFIG, ncurses5-config) - if test "$NCURSES5_CONFIG" != "" ; then - CPPFLAGS="$CPPFLAGS `$NCURSES5_CONFIG --cflags`" - LDFLAGS="$LDFLAGS `$NCURSES5_CONFIG --libs`" - AC_CHECK_LIB(ncurses, initscr, , AC_MSG_ERROR([ncurses library is required])) - else - AC_MSG_ERROR([ncurses5-config executable is missing]) - fi + ncurses_config_bin=ncurses5-config + ncurses_lib=ncurses +fi +AC_PATH_PROG(NCURSES_CONFIG, $ncurses_config_bin) +if test "$NCURSES_CONFIG" != "" ; then + CPPFLAGS="$CPPFLAGS -DUTF8_ENABLED `$NCURSES_CONFIG --cflags`" + LDFLAGS="$LDFLAGS `$NCURSES_CONFIG --libs`" + AC_CHECK_LIB($ncurses_lib, initscr, , AC_MSG_ERROR([$ncurses_lib library is required])) +else + AC_MSG_ERROR([$ncurses_config_bin executable is missing]) fi AC_CHECK_HEADERS([ncurses.h], , AC_MSG_ERROR([missing ncurses.h header])) @@ -46,6 +43,20 @@ CPPFLAGS="$CPPFLAGS $libmpd_CFLAGS" LDFLAGS="$LDFLAGS $libmpd_LIBS" AC_CHECK_HEADERS([libmpd/libmpd.h], , AC_MSG_ERROR([missing libmpd.h header])) +dnl ===================== +dnl = checking for curl = +dnl ===================== +if test "$curl" = "yes" ; then + AC_PATH_PROG(CURL_CONFIG, curl-config) + if test "$CURL_CONFIG" != "" ; then + CPPFLAGS="$CPPFLAGS `$CURL_CONFIG --cflags`" + AC_CHECK_LIB(curl, curl_easy_init, LDFLAGS="$LDFLAGS `$CURL_CONFIG --libs`", AC_MSG_ERROR([curl library is required])) + AC_CHECK_HEADERS([curl/curl.h], , AC_MSG_ERROR([missing curl.h header])) + else + AC_MSG_ERROR([curl-config executable is missing]) + fi +fi + dnl ======================= dnl = checking for taglib = dnl ======================= diff --git a/src/Makefile.am b/src/Makefile.am index 338f4f0f..e57b44bd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,11 +1,11 @@ bin_PROGRAMS = ncmpcpp -ncmpcpp_SOURCES = color_parser.cpp helpers.cpp menu.cpp misc.cpp ncmpcpp.cpp \ - scrollpad.cpp settings.cpp song.cpp status_checker.cpp window.cpp +ncmpcpp_SOURCES = color_parser.cpp helpers.cpp lyrics.cpp menu.cpp misc.cpp \ + ncmpcpp.cpp scrollpad.cpp settings.cpp song.cpp status_checker.cpp window.cpp # set the include path found by configure INCLUDES= $(all_includes) # the library search path. ncmpcpp_LDFLAGS = $(all_libraries) -noinst_HEADERS = helpers.h menu.h scrollpad.h settings.h song.h \ +noinst_HEADERS = helpers.h lyrics.h menu.h scrollpad.h settings.h song.h \ status_checker.h window.h diff --git a/src/helpers.cpp b/src/helpers.cpp index 875b503a..18879de9 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -37,7 +37,7 @@ extern Window *wFooter; extern vector vPlaylist; extern vector vBrowser; -extern CurrScreen current_screen; +extern NcmpcppScreen current_screen; extern Song edited_song; extern Song searched_song; diff --git a/src/lyrics.cpp b/src/lyrics.cpp new file mode 100644 index 00000000..2f9dfaea --- /dev/null +++ b/src/lyrics.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2008 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "lyrics.h" + +const string lyrics_folder = home_folder + "/" + ".lyrics"; +const string mkdir_command = "mkdir " + lyrics_folder + " &>/dev/null"; + +size_t write_data(char *buffer, size_t size, size_t nmemb, string data) +{ + int result = 0; + if (buffer) + { + data += buffer; + result = size*nmemb; + } + return result; +} + +string GetLyrics(string artist, string song) +{ + const string filename = artist + " - " + song + ".txt"; + const string fullpath = lyrics_folder + "/" + filename; + system(mkdir_command.c_str()); + + string result; + std::ifstream input(fullpath.c_str()); + + if (input.is_open()) + { + string line; + while (getline(input, line)) + result += line + "\n"; + return result; + } + +# ifdef HAVE_CURL_CURL_H + for (string::iterator it = artist.begin(); it != artist.end(); it++) + if (*it == ' ') + *it = '+'; + + for (string::iterator it = song.begin(); it != song.end(); it++) + if (*it == ' ') + *it = '+'; + + CURLcode code; + + string url = "http://lyricwiki.org/api.php?artist=" + artist + "&song=" + song + "&fmt=xml"; + + 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, 30); + code = curl_easy_perform(lyrics); + curl_easy_cleanup(lyrics); + + if (code != CURLE_OK) + { + result = "Error while fetching lyrics: " + string(curl_easy_strerror(code)); + return result; + } + + int a, b; + a = result.find("")+8; + b = result.find(""); + + result = result.substr(a, b-a); + + if (result == "Not found") + return result; + + for (int i = result.find("'"); i != string::npos; i = result.find("'")) + result.replace(i, 6, "'"); + for (int i = result.find("""); i != string::npos; i = result.find(""")) + result.replace(i, 6, "\""); + + std::ofstream output(fullpath.c_str()); + + if (output.is_open()) + { + output << result; + output.close(); + } +# else + result = "Local lyrics not found. As ncmpcpp has been compiled without curl support, you can put appropriate lyrics into ~/.lyrics directory (file syntax is \"ARTIST - TITLE.txt\") or recompile ncmpcpp with curl support."; +# endif + return result + '\n'; +} + diff --git a/src/lyrics.h b/src/lyrics.h new file mode 100644 index 00000000..92b57961 --- /dev/null +++ b/src/lyrics.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2008 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef HAVE_LYRICS_H +#define HAVE_LYRICS_H + +#include "ncmpcpp.h" + +#include +#ifdef HAVE_CURL_CURL_H +# include +#endif + +string GetLyrics(string, string); + +#endif + diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index a9f30b5f..d6c6022b 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -23,6 +23,7 @@ #include "helpers.h" #include "settings.h" #include "song.h" +#include "lyrics.h" #define FOR_EACH_MPD_DATA(x) for (; (x); (x) = mpd_data_get_next(x)) @@ -43,6 +44,13 @@ block_progressbar_update = 0; \ } +#define REFRESH_MEDIA_LIBRARY_SCREEN \ + mLibArtists->Display(redraw_me); \ + mvvline(main_start_y, lib_albums_start_x-1, 0, main_height); \ + mLibAlbums->Display(redraw_me); \ + mvvline(main_start_y, lib_songs_start_x-1, 0, main_height); \ + mLibSongs->Display(redraw_me) + #ifdef HAVE_TAGLIB_H const string tag_screen = "Tag editor"; const string tag_screen_keydesc = "\tE e : Edit song's tags\n"; @@ -77,6 +85,7 @@ Menu *mLibArtists; Menu *mLibAlbums; Menu *mLibSongs; Scrollpad *sHelp; +Scrollpad *sLyrics; Window *wHeader; Window *wFooter; @@ -107,8 +116,8 @@ string mpd_random; string mpd_crossfade; string mpd_db_updating; -CurrScreen current_screen; -CurrScreen prev_screen; +NcmpcppScreen current_screen; +NcmpcppScreen prev_screen; Song edited_song; Song searched_song; @@ -189,6 +198,7 @@ int main(int argc, char *argv[]) mLibSongs = new Menu(lib_songs_start_x, main_start_y, lib_songs_width, main_height, "Songs", Config.main_color, brNone); sHelp = new Scrollpad(0, main_start_y, COLS, main_height, "", Config.main_color, brNone); + sLyrics = new Scrollpad(sHelp->EmptyClone()); sHelp->Add(" [b]Keys - Movement\n -----------------------------------------[/b]\n"); sHelp->Add("\tUp : Move Cursor up\n"); @@ -224,7 +234,8 @@ int main(int argc, char *argv[]) sHelp->Add("\t? : Backward find\n"); sHelp->Add("\t. : Go to next/previous found position\n"); sHelp->Add(tag_screen_keydesc); - sHelp->Add("\tg : Go to chosen position in current song\n\n"); + sHelp->Add("\tg : Go to chosen position in current song\n"); + sHelp->Add("\tl : Show/hide song's lyrics\n\n"); sHelp->Add("\tQ q : Quit\n\n\n"); @@ -292,6 +303,7 @@ int main(int argc, char *argv[]) mLibArtists->Timeout(ncmpcpp_window_timeout); mLibAlbums->Timeout(ncmpcpp_window_timeout); mLibSongs->Timeout(ncmpcpp_window_timeout); + sLyrics->Timeout(ncmpcpp_window_timeout); wFooter->Timeout(ncmpcpp_window_timeout); while (!main_exit) @@ -326,6 +338,9 @@ int main(int argc, char *argv[]) case csLibrary: title = "Media library"; break; + case csLyrics: + title = "Lyrics"; + break; } if (title_allowed) @@ -523,6 +538,8 @@ int main(int argc, char *argv[]) mBrowser->Resize(COLS, main_height); mTagEditor->Resize(COLS, main_height); mSearcher->Resize(COLS, main_height); + sLyrics->Resize(COLS, main_height); + sLyrics->Timeout(ncmpcpp_window_timeout); lib_artist_width = COLS/3-1; lib_albums_start_x = lib_artist_width+1; @@ -550,13 +567,9 @@ int main(int argc, char *argv[]) if (current_screen == csLibrary) { mLibArtists->Hide(); - mLibArtists->Display(redraw_me); - mvvline(main_start_y, lib_albums_start_x-1, 0, main_height); mLibAlbums->Hide(); - mLibAlbums->Display(redraw_me); - mvvline(main_start_y, lib_songs_start_x-1, 0, main_height); mLibSongs->Hide(); - mLibSongs->Display(redraw_me); + REFRESH_MEDIA_LIBRARY_SCREEN; } header_update_status = 1; @@ -786,11 +799,7 @@ int main(int argc, char *argv[]) # else wCurrent = wPrev; # endif - mLibArtists->Display(redraw_me); - mvvline(main_start_y, lib_albums_start_x-1, 0, main_height); - mLibAlbums->Display(redraw_me); - mvvline(main_start_y, lib_songs_start_x-1, 0, main_height); - mLibSongs->Display(redraw_me); + REFRESH_MEDIA_LIBRARY_SCREEN; } # ifdef HAVE_TAGLIB_H break; @@ -1599,6 +1608,60 @@ int main(int argc, char *argv[]) } break; } + case 'l': // show lyrics + { + if (wCurrent == sLyrics) + { + wCurrent->Hide(); + current_screen = prev_screen; + wCurrent = wPrev; + redraw_me = 1; + if (current_screen == csLibrary) + { + REFRESH_MEDIA_LIBRARY_SCREEN; + } + break; + } + if ((wCurrent == mPlaylist && !vPlaylist.empty()) + || (wCurrent == mBrowser && vBrowser[mBrowser->GetChoice()-1].type == MPD_DATA_TYPE_SONG) + || (wCurrent == mSearcher && !vSearched.empty() && mSearcher->GetChoice() > search_engine_static_option) + || (wCurrent == mLibSongs)) + { + Song s; + switch (current_screen) + { + case csPlaylist: + s = *vPlaylist[mPlaylist->GetChoice()-1]; + break; + case csBrowser: + s = mpd_database_get_fileinfo(conn, vBrowser[mBrowser->GetChoice()-1].name.c_str()); + break; + case csSearcher: + s = vSearched[mSearcher->GetChoice()-search_engine_static_option-1]; + break; + case csLibrary: + s = vSongs[mLibSongs->GetChoice()-1]; + break; + } + + if (s.GetArtist() != UNKNOWN_ARTIST && s.GetTitle() != UNKNOWN_TITLE) + { + wPrev = wCurrent; + prev_screen = current_screen; + wCurrent = sLyrics; + wCurrent->Hide(); + wCurrent->Clear(); + current_screen = csLyrics; + + sLyrics->WriteXY(0, 0, "Fetching lyrics..."); + sLyrics->Refresh(); + sLyrics->Add("[b]" + s.GetArtist() + " - " + s.GetTitle() + "[/b]\n\n"); + sLyrics->Add(GetLyrics(s.GetArtist(), s.GetTitle())); + sLyrics->Timeout(ncmpcpp_window_timeout); + } + } + break; + } case '1': // help screen { if (wCurrent != sHelp) @@ -1606,7 +1669,6 @@ int main(int argc, char *argv[]) wCurrent = sHelp; wCurrent->Hide(); current_screen = csHelp; - redraw_me = 1; } break; } @@ -1722,11 +1784,7 @@ int main(int argc, char *argv[]) wCurrent->Hide(); - mLibArtists->Display(); - mvvline(main_start_y, lib_albums_start_x-1, 0, main_height); - mLibAlbums->Display(); - mvvline(main_start_y, lib_songs_start_x-1, 0, main_height); - mLibSongs->Display(); + REFRESH_MEDIA_LIBRARY_SCREEN; wCurrent = mLibArtists; current_screen = csLibrary; diff --git a/src/ncmpcpp.h b/src/ncmpcpp.h index d92a7033..69eb1031 100644 --- a/src/ncmpcpp.h +++ b/src/ncmpcpp.h @@ -55,11 +55,12 @@ const bool UNICODE = 0; #include "scrollpad.h" #include "misc.h" -enum CurrScreen { csHelp, csPlaylist, csBrowser, csTagEditor, csSearcher, csLibrary }; +enum NcmpcppScreen { csHelp, csPlaylist, csBrowser, csTagEditor, csSearcher, csLibrary, csLyrics }; const int ncmpcpp_window_timeout = 500; const int search_engine_static_option = 17; +const string home_folder = getenv("HOME"); const string TERMINAL_TYPE = getenv("TERM"); const string search_mode_one = "Match if tag contains searched phrase"; diff --git a/src/scrollpad.cpp b/src/scrollpad.cpp index 2337bcce..9da487f9 100644 --- a/src/scrollpad.cpp +++ b/src/scrollpad.cpp @@ -22,7 +22,7 @@ void Scrollpad::Add(string str) { - if (itsXPos > 0 && str[0] != ' ') + if (itsXPos > 0 && (str[0] != ' ' || str[0] != '\n')) str = " " + str; itsRawContent += str; @@ -39,8 +39,13 @@ void Scrollpad::Add(string str) int space_pos = 0; bool collect = 0; - for (size_t i = 0; i <= s.length(); i++, itsXPos++) + for (size_t i = 0; i < s.length(); i++) { + if (s[i] != '\t') + itsXPos++; + else + itsXPos += 8; + if (BBEnabled) { if (s[i] == '[') diff --git a/src/scrollpad.h b/src/scrollpad.h index 751c6bcd..a9532c27 100644 --- a/src/scrollpad.h +++ b/src/scrollpad.h @@ -26,7 +26,7 @@ class Scrollpad: public Window { public: - Scrollpad(int startx, int starty, int width, int height, string title, COLOR color, BORDER border) : Window(startx, starty, width, height, title, color, border), itsBeginning(0), itsRealHeight(1), itsXPos(0) { delwin(itsWindow); itsWindow = newpad(0,0); } + Scrollpad(int startx, int starty, int width, int height, string title, COLOR color, BORDER border) : Window(startx, starty, width, height, title, color, border), itsBeginning(0), itsRealHeight(1), itsXPos(0) { delwin(itsWindow); itsWindow = newpad(itsHeight,itsWidth); } virtual ~Scrollpad() {} virtual void Add(string); virtual void Display(bool = 0); diff --git a/src/settings.h b/src/settings.h index cd06237d..05468a0d 100644 --- a/src/settings.h +++ b/src/settings.h @@ -25,8 +25,6 @@ #include "ncmpcpp.h" -const string home_folder = getenv("HOME"); - struct ncmpcpp_config { string mpd_music_dir; diff --git a/src/status_checker.cpp b/src/status_checker.cpp index d7a56b2f..d370da07 100644 --- a/src/status_checker.cpp +++ b/src/status_checker.cpp @@ -65,7 +65,7 @@ extern string mpd_random; extern string mpd_crossfade; extern string mpd_db_updating; -extern CurrScreen current_screen; +extern NcmpcppScreen current_screen; extern bool header_update_status;