From 5dfbd3f23a52739a9a7b84b1c471c031d70b2087 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Sun, 24 Jan 2021 17:11:27 +0100 Subject: [PATCH 01/64] Bump version to 0.10_dev --- CHANGELOG.md | 2 ++ configure.ac | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb5a658d8..c4b290449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# ncmpcpp-0.10 (????-??-??) + # ncmpcpp-0.9.2 (2021-01-24) * Revert suppression of output of all external commands as that makes e.g album art addons no longer work. diff --git a/configure.ac b/configure.ac index 24627582f..ef0c52b79 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([ncmpcpp], [0.9.2]) +AC_INIT([ncmpcpp], [0.10_dev]) AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_HEADERS(config.h) AM_INIT_AUTOMAKE([subdir-objects]) From d9c55771bb1059671486b1df245075e10bd6e853 Mon Sep 17 00:00:00 2001 From: V Date: Sun, 24 Jan 2021 19:15:59 +0100 Subject: [PATCH 02/64] configuration: add mpd_password option (#407) Although this can already be set via mpd_host, having a separate option is cleaner, and doesn't require setting the host when ncmpcpp already detects it correctly. --- AUTHORS | 1 + CHANGELOG.md | 1 + doc/config | 2 ++ doc/ncmpcpp.1 | 3 +++ src/settings.cpp | 4 ++++ 5 files changed, 11 insertions(+) diff --git a/AUTHORS b/AUTHORS index 85642200c..9249a29e2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -46,3 +46,4 @@ Trygve Aaberge Wieland Hoffmann Wojciech Siewierski Yannick LM +V diff --git a/CHANGELOG.md b/CHANGELOG.md index c4b290449..303a35312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # ncmpcpp-0.10 (????-??-??) +* Add the configuration option `mpd_password`. # ncmpcpp-0.9.2 (2021-01-24) * Revert suppression of output of all external commands as that makes e.g album diff --git a/doc/config b/doc/config index 88893cff2..57c8f9a9b 100644 --- a/doc/config +++ b/doc/config @@ -27,6 +27,8 @@ # #mpd_port = 6600 # +#mpd_password = "" +# #mpd_connection_timeout = 5 # ## Needed for tag editor and file operations to work. diff --git a/doc/ncmpcpp.1 b/doc/ncmpcpp.1 index 6ef2745ce..ff2669aca 100644 --- a/doc/ncmpcpp.1 +++ b/doc/ncmpcpp.1 @@ -74,6 +74,9 @@ Connect to MPD running on specified host/unix socket. When HOST starts with a '/ .B mpd_port = PORT Connect to MPD on the specified port. Note: MPD_PORT environment variable overrides this setting. .TP +.B mpd_password = PASSWORD +Use PASSWORD to authenticate to MPD. +.TP .B mpd_music_dir = PATH Search for files in specified directory. This is needed for tag editor to work. .TP diff --git a/src/settings.cpp b/src/settings.cpp index 80a2b4e32..f00326830 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -244,6 +244,10 @@ bool Configuration::read(const std::vector &config_paths, bool igno p.add("mpd_port", nullptr, "6600", [](std::string port) { Mpd.SetPort(verbose_lexical_cast(port)); }); + p.add("mpd_password", nullptr, "", [](std::string password) { + if (!password.empty()) + Mpd.SetPassword(password); + }); p.add("mpd_music_dir", &mpd_music_dir, "~/music", adjust_directory); p.add("mpd_connection_timeout", &mpd_connection_timeout, "5"); p.add("mpd_crossfade_time", &crossfade_time, "5"); From 689771c2f7734d3473dd45cbb4ce8b95b9551ed5 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Tue, 26 Jan 2021 19:06:26 +0100 Subject: [PATCH 03/64] Separate chunks of lyrics with a double newline --- CHANGELOG.md | 1 + src/lyrics_fetcher.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 303a35312..c80c515b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # ncmpcpp-0.10 (????-??-??) * Add the configuration option `mpd_password`. +* Separate chunks of lyrics with a double newline. # ncmpcpp-0.9.2 (2021-01-24) * Revert suppression of output of all external commands as that makes e.g album diff --git a/src/lyrics_fetcher.cpp b/src/lyrics_fetcher.cpp index 1cea52aa9..12a6fb2fd 100644 --- a/src/lyrics_fetcher.cpp +++ b/src/lyrics_fetcher.cpp @@ -102,7 +102,11 @@ LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist, { postProcess(*it); if (!it->empty()) + { data += *it; + if (it != lyrics.end() - 1) + data += "\n\n"; + } } result.second = data; From 231c0970c953f5c8213cb293cba54398b45e0647 Mon Sep 17 00:00:00 2001 From: tibequadorian Date: Thu, 8 Apr 2021 22:56:14 +0200 Subject: [PATCH 04/64] Fix vorbis comment and add support for Opus (#463) * fix multiple values for vorbis comment * add vorbis comment support for opus --- src/tags.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/tags.cpp b/src/tags.cpp index a8b34ab79..db0e113c1 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -174,7 +175,7 @@ void writeXiphComments(const MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag) { auto writeXiph = [&](const TagLib::String &type, const TagLib::StringList &list) { for (auto it = list.begin(); it != list.end(); ++it) - tag->addField(type, *it); + tag->addField(type, *it, it == list.begin()); }; // remove field previously used as album artist tag->removeFields("ALBUM ARTIST"); @@ -229,15 +230,21 @@ bool extendedSetSupported(const TagLib::File *f) { return dynamic_cast(f) || dynamic_cast(f) + || dynamic_cast(f) || dynamic_cast(f); } ReplayGainInfo readReplayGain(TagLib::File *f) { ReplayGainInfo result; - if (auto ogg_file = dynamic_cast(f)) + if (auto vorbis_file = dynamic_cast(f)) { - if (auto xiph = ogg_file->tag()) + if (auto xiph = vorbis_file->tag()) + result = getReplayGain(xiph); + } + else if (auto opus_file = dynamic_cast(f)) + { + if (auto xiph = opus_file->tag()) result = getReplayGain(xiph); } else if (auto flac_file = dynamic_cast(f)) @@ -264,9 +271,14 @@ void read(mpd_song *s) else if (auto id3v1 = mpeg_file->ID3v1Tag()) readID3v1Tags(s, id3v1); } - else if (auto ogg_file = dynamic_cast(f.file())) + else if (auto vorbis_file = dynamic_cast(f.file())) + { + if (auto xiph = vorbis_file->tag()) + readXiphComments(s, xiph); + } + else if (auto opus_file = dynamic_cast(f.file())) { - if (auto xiph = ogg_file->tag()) + if (auto xiph = opus_file->tag()) readXiphComments(s, xiph); } else if (auto flac_file = dynamic_cast(f.file())) @@ -299,9 +311,13 @@ bool write(MPD::MutableSong &s) // do not call generic save() as it will duplicate tags saved = true; } - else if (auto ogg_file = dynamic_cast(f.file())) + else if (auto vorbis_file = dynamic_cast(f.file())) + { + writeXiphComments(s, vorbis_file->tag()); + } + else if (auto opus_file = dynamic_cast(f.file())) { - writeXiphComments(s, ogg_file->tag()); + writeXiphComments(s, opus_file->tag()); } else if (auto flac_file = dynamic_cast(f.file())) { From 295663fc1cbfe2be5c501f9f819bdb29208aed0a Mon Sep 17 00:00:00 2001 From: John Ferguson Date: Sun, 25 Apr 2021 13:37:27 -0400 Subject: [PATCH 05/64] update .gitignore to ignore more build artifacts (#475) --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ae5fc5af0..04dde4683 100644 --- a/.gitignore +++ b/.gitignore @@ -6,13 +6,16 @@ Makefile Makefile.in aclocal.m4 autom4te.cache/ +compile config.* configure depcomp +**/.dirstamp install-sh libtool ltmain.sh missing -src/.deps/ +**.m4 +**/.deps/ src/ncmpcpp stamp-h1 From 71970fa73cdcf760499274fc9666611272cd62bf Mon Sep 17 00:00:00 2001 From: John Ferguson Date: Sun, 25 Apr 2021 13:39:52 -0400 Subject: [PATCH 06/64] Fix overflow errors and allow small terminal sizes (#473) * In #259 users request the ability to have ncmpcpp remain open when the terminal has a small size. Previously ncmpcpp would exit when there were less than 30 columns or 5 rows. * After removing this constraint, ncmpcpp would freeze when terminal sizes went below those thresholds. * Upon debugging, it became clear that ncmpcpp wasn't frozen, it was stuck in a very long for loop because it thought the display was unreasonably large (resulting from integer overflow). * Preventing that overflow allows ncmpcpp to handle small sizes gracefully. No other changes to the rendering code were necessary. Fixes #259 --- src/actions.cpp | 14 -------------- src/curses/window.cpp | 9 ++++++--- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 70fb97ff7..9bcb15ecc 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -124,18 +124,6 @@ size_t HeaderHeight; size_t FooterHeight; size_t FooterStartY; -void validateScreenSize() -{ - using Global::MainHeight; - - if (COLS < 30 || MainHeight < 5) - { - NC::destroyScreen(); - std::cout << "Screen is too small to handle ncmpcpp correctly\n"; - exit(1); - } -} - void initializeScreens() { myHelp = new Help; @@ -221,8 +209,6 @@ void resizeScreen(bool reload_main_window) MainHeight = std::max(LINES-(Config.design == Design::Alternative ? 7 : 4), 0); - validateScreenSize(); - if (!Config.header_visibility) MainHeight += 2; if (!Config.statusbar_visibility) diff --git a/src/curses/window.cpp b/src/curses/window.cpp index 231e36a6c..d3f9efd66 100644 --- a/src/curses/window.cpp +++ b/src/curses/window.cpp @@ -682,13 +682,16 @@ void Window::moveTo(size_t new_x, size_t new_y) void Window::adjustDimensions(size_t width, size_t height) { + // NOTE: when dimensions get small, integer overflow will cause calls to + // `Menu::refresh()` to run for a very long time. + if (m_border) { - width -= 2; - height -= 2; + width -= width >= 2 ? 2 : 0; + height -= height >= 2 ? 2 : 0; } if (!m_title.empty()) - height -= 2; + height -= height >= 2 ? 2 : 0; m_height = height; m_width = width; } From 22fd919ce48e2ff8abb739d1bf31ba827d27fe1b Mon Sep 17 00:00:00 2001 From: Jonathan Neidel Date: Sun, 25 Apr 2021 19:41:07 +0200 Subject: [PATCH 07/64] Fix separator between albums with the same name, to check for album artist instead of artist (#472) --- CHANGELOG.md | 2 ++ src/display.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c80c515b3..531599e32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # ncmpcpp-0.10 (????-??-??) * Add the configuration option `mpd_password`. * Separate chunks of lyrics with a double newline. +* Fix separator between albums with the same name, to check for album artist + instead of artist. # ncmpcpp-0.9.2 (2021-01-24) * Revert suppression of output of all external commands as that makes e.g album diff --git a/src/display.cpp b/src/display.cpp index 8a09c55d4..4a52d8ced 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -92,9 +92,9 @@ void setProperties(NC::Menu &menu, const MPD::Song &s, const SongList &list, { // Draw a separator when the next album is different than the current // one. In case there are two albums with the same name, but a different - // artist, compare also artists. + // album artist, compare also album artists. separate_albums = next->song()->getAlbum() != s.getAlbum() - || next->song()->getArtist() != s.getArtist(); + || next->song()->getAlbumArtist() != s.getAlbumArtist(); } } } From f47cf7f37c2bae64fc66da32c197b40ab424f4ca Mon Sep 17 00:00:00 2001 From: Jinwoo Park Date: Mon, 26 Apr 2021 03:42:56 +1000 Subject: [PATCH 08/64] Configurable Column Widths (#360) * add configurable column widths * reorder to match config file * fix possibly misleading documentation * fix crash when integer out of bounds * parse config string during initial processing * use std::bind with length of list * fix division by zero error * use list_of * change escaped_list_separator variable names --- doc/config | 14 ++++++++++++++ doc/ncmpcpp.1 | 9 +++++++++ src/screens/media_library.cpp | 30 +++++++++++++++++++++--------- src/screens/playlist_editor.cpp | 15 +++++++++++---- src/settings.cpp | 7 +++++++ src/settings.h | 4 ++++ src/utility/option_parser.cpp | 14 ++++++++++++++ src/utility/option_parser.h | 15 +++++++++++++-- 8 files changed, 93 insertions(+), 15 deletions(-) diff --git a/doc/config b/doc/config index 57c8f9a9b..23720fc7e 100644 --- a/doc/config +++ b/doc/config @@ -465,6 +465,20 @@ # #ask_for_locked_screen_width_part = yes # +## +## Width of media_library screen columns +## +# +#media_library_column_width_ratio_two = 1:1 +# +#media_library_column_width_ratio_three = 1:1:1 +# +## +## Width of playlist_editor screen columns +## +# +#playlist_editor_column_width_ratio = 1:2 +# #jump_to_now_playing_song_at_start = yes # #ask_before_clearing_playlists = yes diff --git a/doc/ncmpcpp.1 b/doc/ncmpcpp.1 index ff2669aca..e1889f802 100644 --- a/doc/ncmpcpp.1 +++ b/doc/ncmpcpp.1 @@ -335,6 +335,15 @@ If you want to lock a screen, ncmpcpp asks for % of locked screen's width to be .B ask_for_locked_screen_width_part = yes/no If enabled, ncmpcpp will ask for % of locked screen's width each time you want to lock a screen. If you disable that, it'll silently attempt to use default value. .TP +.B media_library_column_width_ratio_two = a:b +The ratio of the column widths in the media library, when there are two columns. +.TP +.B media_library_column_width_ratio_three = a:b:c +The ratio of the column widths in the media library, when there are three columns. +.TP +.B playlist_editor_column_width_ratio = a:b +The ratio of the column widths in the playlist editor. +.TP .B jump_to_now_playing_song_at_start = yes/no If enabled, ncmpcpp will jump at start to now playing song if mpd is playing or paused. .TP diff --git a/src/screens/media_library.cpp b/src/screens/media_library.cpp index 558458d07..df2b58662 100644 --- a/src/screens/media_library.cpp +++ b/src/screens/media_library.cpp @@ -191,11 +191,16 @@ MediaLibrary::MediaLibrary() { hasTwoColumns = 0; isAlbumOnly = 0; - itsLeftColWidth = COLS/3-1; - itsMiddleColWidth = COLS/3; + + size_t ra = Config.media_library_column_width_ratio_three[0]; + size_t rb = Config.media_library_column_width_ratio_three[1]; + size_t rc = Config.media_library_column_width_ratio_three[2]; + + itsLeftColWidth = COLS*ra/(ra+rb+rc)-1; itsMiddleColStartX = itsLeftColWidth+1; - itsRightColWidth = COLS-COLS/3*2-1; - itsRightColStartX = itsLeftColWidth+itsMiddleColWidth+2; + itsMiddleColWidth = COLS*rb/(ra+rb+rc); + itsRightColStartX = itsMiddleColStartX+itsMiddleColWidth+1; + itsRightColWidth = COLS-itsLeftColWidth-itsMiddleColWidth-2; Tags = NC::Menu(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::Border()); setHighlightFixes(Tags); @@ -240,18 +245,25 @@ void MediaLibrary::resize() getWindowResizeParams(x_offset, width); if (!hasTwoColumns) { + size_t ra = Config.media_library_column_width_ratio_three[0]; + size_t rb = Config.media_library_column_width_ratio_three[1]; + size_t rc = Config.media_library_column_width_ratio_three[2]; + itsLeftColStartX = x_offset; - itsLeftColWidth = width/3-1; + itsLeftColWidth = width*ra/(ra+rb+rc)-1; itsMiddleColStartX = itsLeftColStartX+itsLeftColWidth+1; - itsMiddleColWidth = width/3; + itsMiddleColWidth = width*rb/(ra+rb+rc); itsRightColStartX = itsMiddleColStartX+itsMiddleColWidth+1; - itsRightColWidth = width-width/3*2-1; + itsRightColWidth = width-itsLeftColWidth-itsMiddleColWidth-2; } else { + size_t ra = Config.media_library_column_width_ratio_two[0]; + size_t rb = Config.media_library_column_width_ratio_two[1]; + itsMiddleColStartX = x_offset; - itsMiddleColWidth = width/2; - itsRightColStartX = x_offset+itsMiddleColWidth+1; + itsMiddleColWidth = width*ra/(ra+rb); + itsRightColStartX = itsMiddleColStartX+itsMiddleColWidth+1; itsRightColWidth = width-itsMiddleColWidth-1; } diff --git a/src/screens/playlist_editor.cpp b/src/screens/playlist_editor.cpp index 1c00d3b4e..7729fdc3f 100644 --- a/src/screens/playlist_editor.cpp +++ b/src/screens/playlist_editor.cpp @@ -66,10 +66,14 @@ PlaylistEditor::PlaylistEditor() , m_window_timeout(Config.data_fetching_delay ? 250 : BaseScreen::defaultWindowTimeout) , m_fetching_delay(boost::posix_time::milliseconds(Config.data_fetching_delay ? 250 : -1)) { - LeftColumnWidth = COLS/3-1; + size_t ra = Config.playlist_editor_column_width_ratio[0]; + size_t rb = Config.playlist_editor_column_width_ratio[1]; + + LeftColumnWidth = COLS*ra/(ra+rb)-1; RightColumnStartX = LeftColumnWidth+1; RightColumnWidth = COLS-LeftColumnWidth-1; - + + Playlists = NC::Menu(0, MainStartY, LeftColumnWidth, MainHeight, Config.titles_visibility ? "Playlists" : "", Config.main_color, NC::Border()); setHighlightFixes(Playlists); Playlists.cyclicScrolling(Config.use_cyclic_scrolling); @@ -108,11 +112,14 @@ void PlaylistEditor::resize() size_t x_offset, width; getWindowResizeParams(x_offset, width); + size_t ra = Config.playlist_editor_column_width_ratio[0]; + size_t rb = Config.playlist_editor_column_width_ratio[1]; + LeftColumnStartX = x_offset; - LeftColumnWidth = width/3-1; + LeftColumnWidth = width*ra/(ra+rb)-1; RightColumnStartX = LeftColumnStartX+LeftColumnWidth+1; RightColumnWidth = width-LeftColumnWidth-1; - + Playlists.resize(LeftColumnWidth, MainHeight); Content.resize(RightColumnWidth, MainHeight); diff --git a/src/settings.cpp b/src/settings.cpp index f00326830..6fd3558b4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -534,6 +534,12 @@ bool Configuration::read(const std::vector &config_paths, bool igno }); p.add("ask_for_locked_screen_width_part", &ask_for_locked_screen_width_part, "yes", yes_no); + p.add("media_library_column_width_ratio_two", &media_library_column_width_ratio_two, + "1:1", std::bind(parse_ratio, ph::_1, 2)); + p.add("media_library_column_width_ratio_three", &media_library_column_width_ratio_three, + "1:1:1", std::bind(parse_ratio, ph::_1, 3)); + p.add("playlist_editor_column_width_ratio", &playlist_editor_column_width_ratio, + "1:2", std::bind(parse_ratio, ph::_1, 2)); p.add("jump_to_now_playing_song_at_start", &jump_to_now_playing_song_at_start, "yes", yes_no); p.add("ask_before_clearing_playlists", &ask_before_clearing_playlists, @@ -549,6 +555,7 @@ bool Configuration::read(const std::vector &config_paths, bool igno return boost::regex::icase | boost::regex::literal; else if (v == "basic") return boost::regex::icase | boost::regex::basic; + else if (v == "extended") return boost::regex::icase | boost::regex::extended; else if (v == "perl") diff --git a/src/settings.h b/src/settings.h index b8079adee..bc22b5887 100644 --- a/src/settings.h +++ b/src/settings.h @@ -93,6 +93,10 @@ struct Configuration std::string pattern; + std::vector playlist_editor_column_width_ratio; + std::vector media_library_column_width_ratio_two; + std::vector media_library_column_width_ratio_three; + std::vector columns; DisplayMode playlist_display_mode; diff --git a/src/utility/option_parser.cpp b/src/utility/option_parser.cpp index 826ed9373..316d91a2f 100644 --- a/src/utility/option_parser.cpp +++ b/src/utility/option_parser.cpp @@ -45,6 +45,20 @@ bool yes_no(const std::string &v) invalid_value(v); } +std::vector parse_ratio(const std::string &v, const std::vector::size_type length) +{ + std::vector ret = list_of(v, verbose_lexical_cast, length, "", ":", ""); + + size_t total = 0; + for (auto i : ret) + total += i; + if (total == 0) + invalid_value(v); + + return ret; +} + + //////////////////////////////////////////////////////////////////////////////// bool option_parser::run(std::istream &is, bool ignore_errors) diff --git a/src/utility/option_parser.h b/src/utility/option_parser.h index b68fd2ab1..86b944e7b 100644 --- a/src/utility/option_parser.h +++ b/src/utility/option_parser.h @@ -56,17 +56,26 @@ DestT verbose_lexical_cast(const std::string &v) } template -std::vector list_of(const std::string &v, ConvertT convert) +std::vector list_of(const std::string &v, ConvertT convert, const typename std::vector::size_type length, const std::string &escape, const std::string &sep, const std::string "e) { std::vector result; - boost::tokenizer> elems(v); + boost::escaped_list_separator esq(escape, sep, quote); + boost::tokenizer> elems(v, esq); for (auto &value : elems) result.push_back(convert(boost::trim_copy(value))); if (result.empty()) throw std::runtime_error("empty list"); + if (length > 0 && result.size() != length) + throw std::runtime_error("invalid list length"); return result; } +template +std::vector list_of(const std::string &v, ConvertT convert) +{ + return list_of(v, convert, 0, "\\", ",", "\""); +} + template std::vector list_of(const std::string &v) { @@ -75,6 +84,8 @@ std::vector list_of(const std::string &v) bool yes_no(const std::string &v); +std::vector parse_ratio(const std::string &v, const std::vector::size_type length); + //////////////////////////////////////////////////////////////////////////////// class option_parser From 35f9262a6e7c7321f6cfd9a55a1d7db920b81b22 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Sun, 25 Apr 2021 23:57:56 +0200 Subject: [PATCH 09/64] Fix compilation --- src/ncmpcpp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index a186c6aab..87bd54475 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -121,7 +121,6 @@ int main(int argc, char **argv) Config.statusbar_visibility = 0; Actions::setWindowsDimensions(); - Actions::validateScreenSize(); Actions::initializeScreens(); wHeader = new NC::Window(0, 0, COLS, Actions::HeaderHeight, "", Config.header_color, NC::Border()); From df1c4d7d7dee601a580169873ddb675d773777a5 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Mon, 26 Jul 2021 23:44:36 +0200 Subject: [PATCH 10/64] Fix genius lyrics fetcher --- src/lyrics_fetcher.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lyrics_fetcher.h b/src/lyrics_fetcher.h index dd08939d5..672352ffe 100644 --- a/src/lyrics_fetcher.h +++ b/src/lyrics_fetcher.h @@ -118,7 +118,7 @@ struct GeniusFetcher : public GoogleLyricsFetcher virtual const char *name() const override { return "genius.com"; } protected: - virtual const char *regex() const override { return "
(.*?)
"; } + virtual const char *regex() const override { return "(.*?)"; } }; struct JahLyricsFetcher : public GoogleLyricsFetcher From 70ca94f7395dceefcf745b079bc892a7e998f3ed Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Tue, 27 Jul 2021 00:47:46 +0200 Subject: [PATCH 11/64] Curl: stop setting user agent to ncmpcpp --- src/curl_handle.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/curl_handle.cpp b/src/curl_handle.cpp index 4e712825c..efbcafbc4 100644 --- a/src/curl_handle.cpp +++ b/src/curl_handle.cpp @@ -41,7 +41,6 @@ CURLcode Curl::perform(std::string &data, const std::string &URL, const std::str curl_easy_setopt(c, CURLOPT_WRITEDATA, &data); curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, timeout); curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(c, CURLOPT_USERAGENT, "ncmpcpp " VERSION); if (follow_redirect) curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L); if (!referer.empty()) From b023c737e3b1cc5e7a4adc0a412e092e52ed3e52 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Thu, 29 Jul 2021 13:19:35 +0200 Subject: [PATCH 12/64] Remove options deprecated in 0.9 --- src/settings.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 6fd3558b4..3677837cc 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -212,28 +212,6 @@ void deprecated(const char *option, const char *version_removal, bool Configuration::read(const std::vector &config_paths, bool ignore_errors) { option_parser p; - - // Deprecated options. - p.add("visualizer_fifo_path", &visualizer_fifo_path, "", [](std::string v) { - if (!v.empty()) - { - deprecated("visualizer_fifo_path", - "0.10", - "replaced by visualizer_data_source"); - } - return adjust_path(v); - }); - p.add("visualizer_sync_interval", nullptr, "", [](std::string v) { - if (!v.empty()) - { - deprecated("visualizer_sync_interval", - "0.10", - "set 'buffer_time' parameter of your MPD audio output to '100000' " - "(100ms) or lower if you experience synchronization issues " - "between audio and visualization"); - } - }); - // keep the same order of variables as in configuration file p.add("ncmpcpp_directory", &ncmpcpp_directory, "~/.config/ncmpcpp/", adjust_directory); p.add("lyrics_directory", &lyrics_directory, "~/.lyrics/", adjust_directory); From 40e4f98ee896ea10967eb1c4323bb718331eabea Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 25 Aug 2021 02:37:54 +0200 Subject: [PATCH 13/64] Support sockets without a socket inode --- src/mpdpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mpdpp.cpp b/src/mpdpp.cpp index ccdee31d2..9a92e5df4 100644 --- a/src/mpdpp.cpp +++ b/src/mpdpp.cpp @@ -147,7 +147,7 @@ unsigned Connection::Version() const void Connection::SetHostname(const std::string &host) { size_t at = host.find("@"); - if (at != std::string::npos) + if (at != 0 && at != std::string::npos) { m_password = host.substr(0, at); m_host = host.substr(at+1); From 02754534fd7f2f7fbc546f3faf2adc9d73c60914 Mon Sep 17 00:00:00 2001 From: Jeffrey Paul <408977+sneak@users.noreply.github.com> Date: Tue, 8 Feb 2022 06:50:38 -0800 Subject: [PATCH 14/64] update homepage link to https (#529) modern browsers don't support http --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64008f109..4a186de8b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # NCurses Music Player Client (Plus Plus) -Project page - http://rybczak.net/ncmpcpp/ +Project page - https://rybczak.net/ncmpcpp/ ## ncmpcpp – featureful ncurses based MPD client inspired by ncmpc From 26ee24a938433d4509db291228a128456b7cc198 Mon Sep 17 00:00:00 2001 From: squigz Date: Sun, 13 Feb 2022 18:42:13 -0500 Subject: [PATCH 15/64] Add italic text support (#527) --- doc/config | 1 + src/curses/formatted_color.cpp | 6 +++++- src/curses/window.cpp | 21 ++++++++++++++++++++- src/curses/window.h | 9 ++++++++- src/format.cpp | 4 ++++ 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/doc/config b/doc/config index 23720fc7e..57daa8ef2 100644 --- a/doc/config +++ b/doc/config @@ -215,6 +215,7 @@ ## - 9 - end of current color ## - b - bold text ## - u - underline text +## - i - italic text ## - r - reverse colors ## - a - use alternative character set ## diff --git a/src/curses/formatted_color.cpp b/src/curses/formatted_color.cpp index 56b22e61f..9c1e96c7f 100644 --- a/src/curses/formatted_color.cpp +++ b/src/curses/formatted_color.cpp @@ -30,7 +30,8 @@ void verifyFormats(const NC::FormattedColor::Formats &formats) if (fmt == NC::Format::NoBold || fmt == NC::Format::NoUnderline || fmt == NC::Format::NoReverse - || fmt == NC::Format::NoAltCharset) + || fmt == NC::Format::NoAltCharset + || fmt == NC::Format::NoItalic) throw std::logic_error("FormattedColor can't hold disabling formats"); } } @@ -71,6 +72,9 @@ std::istream &NC::operator>>(std::istream &is, NC::FormattedColor &fc) case 'a': formats.push_back(NC::Format::AltCharset); break; + case 'i': + formats.push_back(NC::Format::Italic); + break; default: is.setstate(std::ios::failbit); break; diff --git a/src/curses/window.cpp b/src/curses/window.cpp index d3f9efd66..59c45456d 100644 --- a/src/curses/window.cpp +++ b/src/curses/window.cpp @@ -346,6 +346,10 @@ NC::Format reverseFormat(NC::Format fmt) return NC::Format::NoAltCharset; case NC::Format::NoAltCharset: return NC::Format::AltCharset; + case NC::Format::Italic: + return NC::Format::NoItalic; + case NC::Format::NoItalic: + return NC::Format::Italic; } // Unreachable, silence GCC. return fmt; @@ -494,7 +498,8 @@ Window::Window(size_t startx, size_t starty, size_t width, size_t height, m_bold_counter(0), m_underline_counter(0), m_reverse_counter(0), - m_alt_charset_counter(0) + m_alt_charset_counter(0), + m_italic_counter(0) { if (m_start_x > size_t(COLS) || m_start_y > size_t(LINES) @@ -542,6 +547,7 @@ Window::Window(const Window &rhs) , m_underline_counter(rhs.m_underline_counter) , m_reverse_counter(rhs.m_reverse_counter) , m_alt_charset_counter(rhs.m_alt_charset_counter) +, m_italic_counter(rhs.m_italic_counter) { setColor(m_color); } @@ -566,6 +572,7 @@ Window::Window(Window &&rhs) , m_underline_counter(rhs.m_underline_counter) , m_reverse_counter(rhs.m_reverse_counter) , m_alt_charset_counter(rhs.m_alt_charset_counter) +, m_italic_counter(rhs.m_italic_counter) { rhs.m_window = nullptr; } @@ -591,6 +598,7 @@ Window &Window::operator=(Window rhs) std::swap(m_underline_counter, rhs.m_underline_counter); std::swap(m_reverse_counter, rhs.m_reverse_counter); std::swap(m_alt_charset_counter, rhs.m_alt_charset_counter); + std::swap(m_italic_counter, rhs.m_italic_counter); return *this; } @@ -780,6 +788,11 @@ void Window::altCharset(bool altcharset_state) const (altcharset_state ? wattron : wattroff)(m_window, A_ALTCHARSET); } +void Window::italic(bool italic_state) const +{ + (italic_state ? wattron : wattroff)(m_window, A_ITALIC); +} + void Window::setTimeout(int timeout) { m_window_timeout = timeout; @@ -1413,6 +1426,12 @@ Window &Window::operator<<(Format format) case Format::NoAltCharset: decrease_flag(*this, m_alt_charset_counter, &Window::altCharset); break; + case Format::Italic: + increase_flag(*this, m_italic_counter, &Window::italic); + break; + case Format::NoItalic: + decrease_flag(*this, m_italic_counter, &Window::italic); + break; } return *this; } diff --git a/src/curses/window.h b/src/curses/window.h index 380dc12ad..15728c41d 100644 --- a/src/curses/window.h +++ b/src/curses/window.h @@ -200,7 +200,8 @@ enum class Format { Bold, NoBold, Underline, NoUnderline, Reverse, NoReverse, - AltCharset, NoAltCharset + AltCharset, NoAltCharset, + Italic, NoItalic }; NC::Format reverseFormat(NC::Format fmt); @@ -531,6 +532,11 @@ struct Window /// @param altcharset_state state of altcharset attribute /// void altCharset(bool altcharset_state) const; + + /// Sets state of italic attribute (internal use only) + /// @param italic_state state of italic attribute + /// + void italic(bool italic_state) const; /// pointer to helper function used by getString() /// @see getString() @@ -562,6 +568,7 @@ struct Window int m_underline_counter; int m_reverse_counter; int m_alt_charset_counter; + int m_italic_counter; }; } diff --git a/src/format.cpp b/src/format.cpp index 2316bccdb..6050ab7b8 100644 --- a/src/format.cpp +++ b/src/format.cpp @@ -192,6 +192,8 @@ expressions parseBracket(const string &s, result.push_back(NC::Format::AltCharset); else if (flags & Format::Flags::Format && *it == 'r') result.push_back(NC::Format::Reverse); + else if (flags & Format::Flags::Format && *it == 'i') + result.push_back(NC::Format::Italic); else if (flags & Format::Flags::Format && *it == '/') { ++it; @@ -204,6 +206,8 @@ expressions parseBracket(const string &s, result.push_back(NC::Format::NoAltCharset); else if (*it == 'r') result.push_back(NC::Format::NoReverse); + else if (*it == 'i') + result.push_back(NC::Format::NoItalic); else throwError(s, it, invalidCharacter(*it)); } From 727ca95ca587ae04895208e28413ae85fad78c6b Mon Sep 17 00:00:00 2001 From: leg7 <68823179+leg7@users.noreply.github.com> Date: Sun, 13 Feb 2022 23:47:32 +0000 Subject: [PATCH 16/64] Increase the framerate limit for the visualizer. (#490) * Update settings.cpp * Update settings.cpp --- src/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.cpp b/src/settings.cpp index 3677837cc..7b9ebf9f5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -248,7 +248,7 @@ bool Configuration::read(const std::vector &config_paths, bool igno p.add("visualizer_fps", &visualizer_fps, "60", [](std::string v) { uint32_t result = verbose_lexical_cast(v); - boundsCheck(result, 30, 144); + boundsCheck(result, 30, 1000); return result; }); p.add("visualizer_autoscale", &visualizer_autoscale, "no", yes_no); From 417d7172e5587f4302f92ea6377268dca7f726ad Mon Sep 17 00:00:00 2001 From: Robert Kaspar Date: Sun, 13 Feb 2022 15:48:11 -0800 Subject: [PATCH 17/64] Read Performer from TPE4, not TPE3 (#497) --- src/tags.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tags.cpp b/src/tags.cpp index db0e113c1..6cd6a8c27 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -92,7 +92,7 @@ void readID3v2Tags(mpd_song *s, TagLib::ID3v2::Tag *tag) readFrame(frames["TRCK"], "Track"); readFrame(frames["TCON"], "Genre"); readFrame(frames["TCOM"], "Composer"); - readFrame(frames["TPE3"], "Performer"); + readFrame(frames["TPE4"], "Performer"); readFrame(frames["TPOS"], "Disc"); readFrame(frames["COMM"], "Comment"); } @@ -166,7 +166,7 @@ void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag) writeID3v2("TRCK", tagList(s, &MPD::Song::getTrack)); writeID3v2("TCON", tagList(s, &MPD::Song::getGenre)); writeID3v2("TCOM", tagList(s, &MPD::Song::getComposer)); - writeID3v2("TPE3", tagList(s, &MPD::Song::getPerformer)); + writeID3v2("TPE4", tagList(s, &MPD::Song::getPerformer)); writeID3v2("TPOS", tagList(s, &MPD::Song::getDisc)); writeID3v2("COMM", tagList(s, &MPD::Song::getComment)); } From d96e3e514d6243391eb034970c4ecd3f3de137b3 Mon Sep 17 00:00:00 2001 From: Zaidhaan Hussain Date: Mon, 21 Nov 2022 23:05:31 +0800 Subject: [PATCH 18/64] Fix MPD blocking when server error occurs by handling directly without blocking further execution by throwing --- src/screens/playlist_editor.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/screens/playlist_editor.cpp b/src/screens/playlist_editor.cpp index 7729fdc3f..359fa4b5d 100644 --- a/src/screens/playlist_editor.cpp +++ b/src/screens/playlist_editor.cpp @@ -169,10 +169,7 @@ void PlaylistEditor::update() } catch (MPD::ServerError &e) { - if (e.code() == MPD_SERVER_ERROR_SYSTEM) // no playlists directory - Statusbar::print(e.what()); - else - throw; + Status::handleServerError(e); } if (idx < Playlists.size()) Playlists.resizeList(idx); From dc46f7a49b5bd0fdd2b3b181ece88a3fb8482dc5 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 7 Feb 2024 14:28:26 +0100 Subject: [PATCH 19/64] Fix compilation with taglib 2.0 --- src/tags.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tags.cpp b/src/tags.cpp index 6cd6a8c27..6395807ae 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -123,12 +123,12 @@ void writeCommonTags(const MPD::MutableSong &s, TagLib::Tag *tag) tag->setArtist(ToWString(s.getArtist())); tag->setAlbum(ToWString(s.getAlbum())); try { - tag->setYear(boost::lexical_cast(s.getDate())); + tag->setYear(boost::lexical_cast(s.getDate())); } catch (boost::bad_lexical_cast &) { std::cerr << "writeCommonTags: couldn't write 'year' tag to '" << s.getURI() << "' as it's not a positive integer\n"; } try { - tag->setTrack(boost::lexical_cast(s.getTrack())); + tag->setTrack(boost::lexical_cast(s.getTrack())); } catch (boost::bad_lexical_cast &) { std::cerr << "writeCommonTags: couldn't write 'track' tag to '" << s.getURI() << "' as it's not a positive integer\n"; } @@ -261,7 +261,7 @@ void read(mpd_song *s) if (f.isNull()) return; - setAttribute(s, "Time", boost::lexical_cast(f.audioProperties()->length())); + setAttribute(s, "Time", boost::lexical_cast(f.audioProperties()->lengthInSeconds())); if (auto mpeg_file = dynamic_cast(f.file())) { @@ -306,7 +306,10 @@ bool write(MPD::MutableSong &s) { writeID3v2Tags(s, mpeg_file->ID3v2Tag(true)); // write id3v2.4 tags only - if (!mpeg_file->save(TagLib::MPEG::File::ID3v2, true, 4, false)) + if (!mpeg_file->save(TagLib::MPEG::File::ID3v2, + TagLib::File::StripOthers, + TagLib::ID3v2::v4, + TagLib::File::DoNotDuplicate)) return false; // do not call generic save() as it will duplicate tags saved = true; From 81e5cf58b44be4ec0dc50722e2ed6d534df3973d Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Thu, 30 May 2024 14:58:09 +0200 Subject: [PATCH 20/64] extras: fix compilation with taglib 2.0 --- extras/artist_to_albumartist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/artist_to_albumartist.cpp b/extras/artist_to_albumartist.cpp index c3ddfee3a..0349d0aed 100644 --- a/extras/artist_to_albumartist.cpp +++ b/extras/artist_to_albumartist.cpp @@ -34,7 +34,7 @@ enum class CopyResult { Success, NoArtist, AlbumArtistAlreadyInPlace }; bool is_framelist_empty(const TagLib::ID3v2::FrameList &list) { for (auto it = list.begin(); it != list.end(); ++it) - if ((*it)->toString() != TagLib::String::null) + if (!(*it)->toString().isEmpty()) return false; return true; } From 9c396d7ef09a86091f76ca4ef9ae0fe8b84f2a8a Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 24 Jul 2024 17:25:03 +0200 Subject: [PATCH 21/64] Fix removal of XiphComments --- src/tags.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tags.cpp b/src/tags.cpp index 6395807ae..967046427 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -174,6 +174,7 @@ void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag) void writeXiphComments(const MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag) { auto writeXiph = [&](const TagLib::String &type, const TagLib::StringList &list) { + tag->removeFields(type); for (auto it = list.begin(); it != list.end(); ++it) tag->addField(type, *it, it == list.begin()); }; From 51ebe9e52db78341d6ed77424c17da41bacedde8 Mon Sep 17 00:00:00 2001 From: Drew Marino <77032812+OneTrueC@users.noreply.github.com> Date: Wed, 24 Jul 2024 11:54:12 -0400 Subject: [PATCH 22/64] Fixed bug related to readline.h (#592) Terminals that don't restore the terminal state when a program exits display stdin incorrectly after quitting. This is not a bug in readline.h as its examples use termios in the same way. --- src/curses/window.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/curses/window.cpp b/src/curses/window.cpp index 59c45456d..b0cc96334 100644 --- a/src/curses/window.cpp +++ b/src/curses/window.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "utility/readline.h" @@ -216,6 +217,8 @@ Color Color::Cyan(COLOR_CYAN, Color::current); Color Color::White(COLOR_WHITE, Color::current); Color Color::End(0, 0, false, true); +struct termios orig_termios; + int Color::pairNumber() const { // If colors are disabled, return default pair value. @@ -400,6 +403,7 @@ int colorCount() void initScreen(bool enable_colors, bool enable_mouse) { + tcgetattr(STDIN_FILENO, &orig_termios); initscr(); if (has_colors() && enable_colors) { @@ -481,6 +485,7 @@ void destroyScreen() Mouse::disable(); curs_set(1); endwin(); + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } Window::Window(size_t startx, size_t starty, size_t width, size_t height, From afb150138d5bf5d6f464c31b2c61ebdc037b93bb Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 24 Jul 2024 17:55:35 +0200 Subject: [PATCH 23/64] Change visibility of orig_termios --- src/curses/window.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/curses/window.cpp b/src/curses/window.cpp index b0cc96334..5db41fb95 100644 --- a/src/curses/window.cpp +++ b/src/curses/window.cpp @@ -199,6 +199,8 @@ int add_base() int color_pair_counter; std::vector color_pair_map; +termios orig_termios; + } namespace NC { @@ -217,8 +219,6 @@ Color Color::Cyan(COLOR_CYAN, Color::current); Color Color::White(COLOR_WHITE, Color::current); Color Color::End(0, 0, false, true); -struct termios orig_termios; - int Color::pairNumber() const { // If colors are disabled, return default pair value. From e87c104dd6d742d87c19fd88fedf75e04084b595 Mon Sep 17 00:00:00 2001 From: Denys Tynok Date: Wed, 24 Jul 2024 18:56:14 +0300 Subject: [PATCH 24/64] fix: update obsolete macros in autoconf (#582) * fix: update obsolete macros in autoconf * fix: update obsolete macro 'AC_PROG_LIBTOOL' in autoconf --- configure.ac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index ef0c52b79..7ff1382b6 100644 --- a/configure.ac +++ b/configure.ac @@ -1,15 +1,15 @@ -AC_INIT([ncmpcpp], [0.10_dev]) +AC_INIT([ncmpcpp],[0.10_dev]) AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_HEADERS(config.h) AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_MACRO_DIR([m4]) -AC_PREREQ(2.59) +AC_PREREQ([2.71]) -AC_LANG_CPLUSPLUS +AC_LANG([C++]) AC_PROG_CXX -AC_PROG_LIBTOOL +LT_INIT AC_ARG_ENABLE(outputs, AS_HELP_STRING([--enable-outputs], [Enable outputs screen @<:@default=no@:>@]), [outputs=$enableval], [outputs=no]) AC_ARG_ENABLE(visualizer, AS_HELP_STRING([--enable-visualizer], [Enable music visualizer screen @<:@default=no@:>@]), [visualizer=$enableval], [visualizer=no]) From f76dd085b24ca487930c9c59637b1a5ab60cc035 Mon Sep 17 00:00:00 2001 From: Alex Balgavy <8124851+thezeroalpha@users.noreply.github.com> Date: Wed, 24 Jul 2024 17:57:38 +0200 Subject: [PATCH 25/64] Fix crash when background-fetching lyrics for streams (#561) When `fetch_lyrics_for_current_song_in_background` is set to `yes`, and an HTTP(S) stream is loaded with a long URL, ncmpcpp crashes due to the length of the filename generated to store lyrics. This commit makes the background lyrics fetcher ignore such streams, preventing the crash. Addresses issue #380. --- src/screens/lyrics.cpp | 26 ++++++++++++++++++++++++-- src/song.cpp | 6 ++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/screens/lyrics.cpp b/src/screens/lyrics.cpp index d34ba617b..5ca42869c 100644 --- a/src/screens/lyrics.cpp +++ b/src/screens/lyrics.cpp @@ -361,6 +361,25 @@ void Lyrics::toggleFetcher() Statusbar::print("Using all lyrics fetchers"); } +/* For HTTP(S) streams, fetchInBackground makes ncmpcpp crash: the lyrics_file + * gets set to the stream URL, which is too long, hence + * boost::filesystem::exists() fails. + * Possible solutions: + * - truncate the URL and use that as a filename. Problem: resulting filename + * might not be unique. + * - generate filenames in a different way for streams. Problem: what is a good + * method for this? Perhaps hashing -- but then the lyrics filenames are not + * informative. + * - skip fetching lyrics for streams with URLs that are too long. Problem: + * this leads to inconsistent behavior since lyrics will be fetched for some + * streams but not others. + * - avoid fetching lyrics for streams altogether. + * + * We choose the last solution, and ignore streams when fetching lyrics. This + * is because fetching lyrics for a stream may not be accurate (streams do not + * always provide the necessary metadata to look up lyrics reliably). + * Furthermore, fetching lyrics for streams is not necessarily desirable. + */ void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_) { auto consumer_impl = [this] { @@ -377,7 +396,10 @@ void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_) break; } lyrics_file = lyricsFilename(consumer->songs.front().song()); - if (!boost::filesystem::exists(lyrics_file)) + + // For long filenames (e.g. http(s) stream URLs), boost::filesystem::exists() fails. + // This if condition is fine, because evaluation order is guaranteed. + if (!consumer->songs.front().song().isStream() && !boost::filesystem::exists(lyrics_file)) { cs = consumer->songs.front(); if (cs.notify()) @@ -389,7 +411,7 @@ void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_) } consumer->songs.pop(); } - if (!cs.song().empty()) + if (!cs.song().empty() && !cs.song().isStream()) { auto lyrics = downloadLyrics(cs.song(), nullptr, nullptr, m_fetcher); if (lyrics) diff --git a/src/song.cpp b/src/song.cpp index 70822d109..c274d43f1 100644 --- a/src/song.cpp +++ b/src/song.cpp @@ -293,7 +293,9 @@ bool Song::isFromDatabase() const bool Song::isStream() const { assert(m_song); - return !strncmp(mpd_song_get_uri(m_song.get()), "http://", 7); + const char *song_uri = mpd_song_get_uri(m_song.get()); + // Stream schemas: http, https + return !strncmp(song_uri, "http://", 7) || !strncmp(song_uri, "https://", 8); } bool Song::empty() const @@ -308,7 +310,7 @@ std::string Song::ShowTime(unsigned length) int minutes = length/60; length -= minutes*60; int seconds = length; - + std::string result; if (hours > 0) result = (boost::format("%d:%02d:%02d") % hours % minutes % seconds).str(); From 8746ded880e03a382e3712fbb2a3e5d13f7391a0 Mon Sep 17 00:00:00 2001 From: George Abbott Date: Wed, 24 Jul 2024 17:01:07 +0100 Subject: [PATCH 26/64] Added hh:mm:ss to g command (#550) * Added hh:mm:ss to g command * Updated statusbar prompt with h:m:ss as well * corrected spaces to tabs --- src/actions.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 9bcb15ecc..578172bd8 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1746,7 +1746,7 @@ void JumpToPositionInSong::run() std::string spos; { Statusbar::ScopedLock slock; - Statusbar::put() << "Position to go (in %/m:ss/seconds(s)): "; + Statusbar::put() << "Position to go (in %/h:m:ss/m:ss/seconds(s)): "; spos = wFooter->prompt(); } @@ -1771,8 +1771,17 @@ void JumpToPositionInSong::run() int secs = (percent * s.getDuration()) / 100.0; Mpd.Seek(s.getPosition(), secs); } + else if (boost::regex_match(spos, what, rx.assign("([0-9]+):([0-9]{1,2}):([0-9]{2})"))) // position in hh:mm:ss + { + auto hours = fromString(what[1]); + auto mins = fromString(what[2]); + auto secs = fromString(what[3]); + boundsCheck(mins, 0u, 60u); + boundsCheck(secs, 0u, 60u); + Mpd.Seek(s.getPosition(), hours * 3600 + mins * 60 + secs); + } else - Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)"); + Statusbar::print("Invalid format ([h]:[m]:[ss], [m]:[ss], [s]s, [%]%, [%] accepted)"); } bool SelectItem::canBeRun() From 95bca25cb6696724774474a35f5c316c127f3423 Mon Sep 17 00:00:00 2001 From: gardockt <42496375+gardockt@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:04:51 +0000 Subject: [PATCH 27/64] Tag editor: only write files with modified tags (#536) --- src/screens/tag_editor.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/screens/tag_editor.cpp b/src/screens/tag_editor.cpp index d754f4990..5ad68382b 100644 --- a/src/screens/tag_editor.cpp +++ b/src/screens/tag_editor.cpp @@ -798,13 +798,16 @@ void TagEditor::runAction() Statusbar::print("Writing changes..."); for (auto it = EditedSongs.begin(); it != EditedSongs.end(); ++it) { - Statusbar::printf("Writing tags in \"%1%\"...", (*it)->getName()); - if (!Tags::write(**it)) + if ((*it)->isModified()) { - Statusbar::printf("Error while writing tags to \"%1%\": %2%", - (*it)->getName(), strerror(errno)); - success = 0; - break; + Statusbar::printf("Writing tags in \"%1%\"...", (*it)->getName()); + if (!Tags::write(**it)) + { + Statusbar::printf("Error while writing tags to \"%1%\": %2%", + (*it)->getName(), strerror(errno)); + success = 0; + break; + } } } if (success) From b218d901934f0f729b228e5a7475c25c69e434a2 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Wed, 24 Jul 2024 17:07:34 +0100 Subject: [PATCH 28/64] Check for taglib with pkg-config before trying taglib-config (#558) Programs like taglib-config are not good when cross-compiling. --- configure.ac | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 7ff1382b6..b11213aa1 100644 --- a/configure.ac +++ b/configure.ac @@ -257,19 +257,30 @@ PKG_CHECK_MODULES([libcurl], [libcurl], [ # taglib if test "$taglib" != "no" ; then - AC_PATH_PROG(TAGLIB_CONFIG, taglib-config) - if test "$TAGLIB_CONFIG" != "" ; then - CPPFLAGS="$CPPFLAGS `$TAGLIB_CONFIG --cflags`" - LIBS="$LIBS `$TAGLIB_CONFIG --libs`" + PKG_CHECK_MODULES([taglib], [taglib], [ + AC_SUBST(taglib_CFLAGS) + AC_SUBST(taglib_LIBS) + ], [ + AC_PATH_PROG([TAGLIB_CONFIG], [taglib-config]) + if test "$TAGLIB_CONFIG" != ""; then + taglib_CFLAGS=`$TAGLIB_CONFIG --cflags` + taglib_LIBS=`$TAGLIB_CONFIG --libs` + else + if test "$taglib" = "yes" ; then + AC_MSG_ERROR([could not find taglib.pc or taglib-config executable]) + fi + fi + ]) + + if test "$TAGLIB_CONFIG$taglib_LIBS" != "" ; then + CPPFLAGS="$CPPFLAGS $taglib_CFLAGS" + LIBS="$LIBS $taglib_LIBS" + AC_CHECK_HEADERS([taglib.h], , if test "$taglib" = "yes" ; then AC_MSG_ERROR([missing taglib.h header]) fi ) - else - if test "$taglib" = "yes" ; then - AC_MSG_ERROR([taglib-config executable is missing]) - fi fi fi From 8ecbb8b49b47a6adf29a7ad79a2fd8d295f27c7b Mon Sep 17 00:00:00 2001 From: djvs <2954343+djvs@users.noreply.github.com> Date: Wed, 24 Jul 2024 12:15:02 -0400 Subject: [PATCH 29/64] Expose filepath in columns view (#530) * Expose filepath in columns view * update --- doc/config | 1 + doc/ncmpcpp.1 | 1 + src/display.cpp | 2 ++ src/utility/type_conversions.cpp | 2 ++ 4 files changed, 6 insertions(+) diff --git a/doc/config b/doc/config index 57daa8ef2..747f20551 100644 --- a/doc/config +++ b/doc/config @@ -172,6 +172,7 @@ ## ## %l - length ## %f - filename +## %F - full filepath ## %D - directory ## %a - artist ## %A - album artist diff --git a/doc/ncmpcpp.1 b/doc/ncmpcpp.1 index e1889f802..7ab7cf90b 100644 --- a/doc/ncmpcpp.1 +++ b/doc/ncmpcpp.1 @@ -472,6 +472,7 @@ For song format you can use: %l - length %f - filename + %F - full filepath %D - directory %a - artist %A - album artist diff --git a/src/display.cpp b/src/display.cpp index 4a52d8ced..ddf87932c 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -47,6 +47,8 @@ const wchar_t *toColumnName(char c) return L"Filename"; case 'D': return L"Directory"; + case 'F': + return L"Filepath"; case 'a': return L"Artist"; case 'A': diff --git a/src/utility/type_conversions.cpp b/src/utility/type_conversions.cpp index d4bfb8cdc..b546fa4a1 100644 --- a/src/utility/type_conversions.cpp +++ b/src/utility/type_conversions.cpp @@ -168,6 +168,8 @@ MPD::Song::GetFunction charToGetFunction(char c) return &MPD::Song::getDirectory; case 'f': return &MPD::Song::getName; + case 'F': + return &MPD::Song::getURI; case 'a': return &MPD::Song::getArtist; case 'A': From af7550a69f98a1872ad1a8cc93cd6f250d139943 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 24 Jul 2024 18:02:35 +0200 Subject: [PATCH 30/64] Adjust the new regex to require 2 minute digits --- src/actions.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/actions.cpp b/src/actions.cpp index 578172bd8..894059200 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1752,27 +1752,31 @@ void JumpToPositionInSong::run() boost::regex rx; boost::smatch what; - if (boost::regex_match(spos, what, rx.assign("([0-9]+):([0-9]{2})"))) // mm:ss + // mm:ss + if (boost::regex_match(spos, what, rx.assign("([0-9]+):([0-9]{2})"))) { auto mins = fromString(what[1]); auto secs = fromString(what[2]); boundsCheck(secs, 0u, 60u); Mpd.Seek(s.getPosition(), mins * 60 + secs); } - else if (boost::regex_match(spos, what, rx.assign("([0-9]+)s"))) // position in seconds + // position in seconds + else if (boost::regex_match(spos, what, rx.assign("([0-9]+)s"))) { auto secs = fromString(what[1]); Mpd.Seek(s.getPosition(), secs); } - else if (boost::regex_match(spos, what, rx.assign("([0-9]+)[%]{0,1}"))) // position in % + // position in% + else if (boost::regex_match(spos, what, rx.assign("([0-9]+)[%]{0,1}"))) { auto percent = fromString(what[1]); boundsCheck(percent, 0u, 100u); int secs = (percent * s.getDuration()) / 100.0; Mpd.Seek(s.getPosition(), secs); } - else if (boost::regex_match(spos, what, rx.assign("([0-9]+):([0-9]{1,2}):([0-9]{2})"))) // position in hh:mm:ss - { + // position in hh:mm:ss + else if (boost::regex_match(spos, what, rx.assign("([0-9]+):([0-9]{2}):([0-9]{2})"))) + { auto hours = fromString(what[1]); auto mins = fromString(what[2]); auto secs = fromString(what[3]); @@ -1781,7 +1785,7 @@ void JumpToPositionInSong::run() Mpd.Seek(s.getPosition(), hours * 3600 + mins * 60 + secs); } else - Statusbar::print("Invalid format ([h]:[m]:[ss], [m]:[ss], [s]s, [%]%, [%] accepted)"); + Statusbar::print("Invalid format ([h]:[mm]:[ss], [m]:[ss], [s]s, [%]%, [%] accepted)"); } bool SelectItem::canBeRun() From 6f8d12da8ce6a7c7a7e1494c773f7219bddb9049 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 24 Jul 2024 19:18:00 +0200 Subject: [PATCH 31/64] If both strings are numbers, compare them as such --- src/screens/media_library.cpp | 18 ++++-------------- src/utility/comparators.cpp | 12 ++++++++++++ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/screens/media_library.cpp b/src/screens/media_library.cpp index df2b58662..1b0461178 100644 --- a/src/screens/media_library.cpp +++ b/src/screens/media_library.cpp @@ -98,7 +98,7 @@ bool MoveToAlbum(NC::Menu &albums, const std::string &primary_tag, c struct SortSongs { typedef NC::Menu::Item SongItem; - static const std::array GetFuns; + static const std::array GetFuns; LocaleStringComparison m_cmp; @@ -117,26 +117,16 @@ struct SortSongs { return ret < 0; } - // Sort by track numbers. - try { - ret = boost::lexical_cast(a.getTags(&MPD::Song::getTrackNumber)) - - boost::lexical_cast(b.getTags(&MPD::Song::getTrackNumber)); - } catch (boost::bad_lexical_cast &) { - ret = a.getTrackNumber().compare(b.getTrackNumber()); - } - if (ret != 0) - return ret < 0; - - // If track numbers are equal, sort by the display format. return Format::stringify(Config.song_library_format, &a) < Format::stringify(Config.song_library_format, &b); } }; -const std::array SortSongs::GetFuns = {{ +const std::array SortSongs::GetFuns = {{ &MPD::Song::getDate, &MPD::Song::getAlbum, - &MPD::Song::getDisc + &MPD::Song::getDisc, + &MPD::Song::getTrackNumber, }}; class SortAlbumEntries { diff --git a/src/utility/comparators.cpp b/src/utility/comparators.cpp index a9c0d3394..49e3b97d0 100644 --- a/src/utility/comparators.cpp +++ b/src/utility/comparators.cpp @@ -34,10 +34,22 @@ bool hasTheWord(const std::string &s) && (s[3] == ' '); } +long long strToLL(const char *s, size_t len) +{ + long long n = 0; + while (len--) + n = 10*n + *s++ - '0'; + return n; +} + } int LocaleStringComparison::compare(const char *a, size_t a_len, const char *b, size_t b_len) const { + // If both strings are numbers, compare them as such. + if (std::all_of(a, a + a_len, isdigit) && std::all_of(b, b + b_len, isdigit)) + return strToLL(a, a_len) - strToLL(b, b_len); + size_t ac_off = 0, bc_off = 0; if (m_ignore_the) { From b2c8ca91c49dd3e198f772bc5d70b322fa6491ec Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 24 Jul 2024 19:38:30 +0200 Subject: [PATCH 32/64] Don't treat empty strings as numbers --- src/utility/comparators.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utility/comparators.cpp b/src/utility/comparators.cpp index 49e3b97d0..3959962b2 100644 --- a/src/utility/comparators.cpp +++ b/src/utility/comparators.cpp @@ -47,7 +47,8 @@ long long strToLL(const char *s, size_t len) int LocaleStringComparison::compare(const char *a, size_t a_len, const char *b, size_t b_len) const { // If both strings are numbers, compare them as such. - if (std::all_of(a, a + a_len, isdigit) && std::all_of(b, b + b_len, isdigit)) + if ( a_len > 0 && std::all_of(a, a + a_len, isdigit) + && b_len > 0 && std::all_of(b, b + b_len, isdigit)) return strToLL(a, a_len) - strToLL(b, b_len); size_t ac_off = 0, bc_off = 0; From 985bc09b5c61d67cd179b1a6562c5949da2f368d Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 24 Jul 2024 20:33:32 +0200 Subject: [PATCH 33/64] Update lyrics fetchers Removes azlyrics, musixmatch, sing365/lyrics007 and metrolyrics. --- doc/config | 2 +- src/configuration.cpp | 6 +----- src/lyrics_fetcher.cpp | 24 +++++------------------- src/lyrics_fetcher.h | 40 ++-------------------------------------- src/settings.cpp | 2 +- 5 files changed, 10 insertions(+), 64 deletions(-) diff --git a/doc/config b/doc/config index 747f20551..82a80fcb9 100644 --- a/doc/config +++ b/doc/config @@ -409,7 +409,7 @@ # #cyclic_scrolling = no # -#lyrics_fetchers = azlyrics, genius, musixmatch, sing365, metrolyrics, justsomelyrics, jahlyrics, plyrics, tekstowo, zeneszoveg, internet +#lyrics_fetchers = genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet # #follow_now_playing_lyrics = no # diff --git a/src/configuration.cpp b/src/configuration.cpp index c9eb3fef3..c1e26913a 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -156,14 +156,10 @@ bool configure(int argc, char **argv) if (vm.count("test-lyrics-fetchers")) { std::vector> fetcher_data = { - std::make_tuple("azlyrics", "rihanna", "umbrella"), std::make_tuple("genius", "rihanna", "umbrella"), - std::make_tuple("musixmatch", "rihanna", "umbrella"), - std::make_tuple("sing365", "rihanna", "umbrella"), - std::make_tuple("metrolyrics", "rihanna", "umbrella"), std::make_tuple("justsomelyrics", "rihanna", "umbrella"), std::make_tuple("jahlyrics", "sean kingston", "dry your eyes"), - std::make_tuple("plyrics", "offspring", "genocide"), + std::make_tuple("plyrics", "rihanna", "umbrella"), std::make_tuple("tekstowo", "rihanna", "umbrella"), std::make_tuple("zeneszoveg", "rihanna", "umbrella"), }; diff --git a/src/lyrics_fetcher.cpp b/src/lyrics_fetcher.cpp index 12a6fb2fd..0f30f40df 100644 --- a/src/lyrics_fetcher.cpp +++ b/src/lyrics_fetcher.cpp @@ -38,16 +38,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher) { std::string s; is >> s; - if (s == "azlyrics") - fetcher = std::make_unique(); - else if (s == "genius") + if (s == "genius") fetcher = std::make_unique(); - else if (s == "musixmatch") - fetcher = std::make_unique(); - else if (s == "sing365") - fetcher = std::make_unique(); - else if (s == "metrolyrics") - fetcher = std::make_unique(); else if (s == "justsomelyrics") fetcher = std::make_unique(); else if (s == "jahlyrics") @@ -76,7 +68,7 @@ LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist, std::string url = urlTemplate(); boost::replace_all(url, "%artist%", Curl::escape(artist)); boost::replace_all(url, "%title%", Curl::escape(title)); - + std::string data; CURLcode code = Curl::perform(data, url, "", true); @@ -88,9 +80,11 @@ LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist, auto lyrics = getContent(regex(), data); + //std::cerr << "URL: " << url << "\n"; + //std::cerr << "Data: " << data << "\n"; + if (lyrics.empty() || notLyrics(data)) { - //std::cerr << "Data: " << data << "\n"; //std::cerr << "Empty: " << lyrics.empty() << "\n"; //std::cerr << "Not Lyrics: " << notLyrics(data) << "\n"; result.second = msgNotFound; @@ -202,14 +196,6 @@ bool GoogleLyricsFetcher::isURLOk(const std::string &url) /**********************************************************************/ -bool MetrolyricsFetcher::isURLOk(const std::string &url) -{ - // it sometimes return link to sitemap.xml, which is huge so we need to discard it - return GoogleLyricsFetcher::isURLOk(url) && url.find("sitemap") == std::string::npos; -} - -/**********************************************************************/ - LyricsFetcher::Result InternetLyricsFetcher::fetch(const std::string &artist, const std::string &title) { diff --git a/src/lyrics_fetcher.h b/src/lyrics_fetcher.h index 672352ffe..f4cfc0b2a 100644 --- a/src/lyrics_fetcher.h +++ b/src/lyrics_fetcher.h @@ -69,34 +69,6 @@ struct GoogleLyricsFetcher : public LyricsFetcher const char *URL; }; -struct MusixmatchFetcher : public GoogleLyricsFetcher -{ - virtual const char *name() const override { return "musixmatch.com"; } - -protected: - virtual const char *regex() const override { return "(.*?)"; } - - virtual void postProcess(std::string &) const override { } -}; - -struct MetrolyricsFetcher : public GoogleLyricsFetcher -{ - virtual const char *name() const override { return "metrolyrics.com"; } - -protected: - virtual const char *regex() const override { return "
(.*?)(.*?)(.*?)
"; } - - virtual bool isURLOk(const std::string &url) override; -}; - -struct Sing365Fetcher : public GoogleLyricsFetcher -{ - virtual const char *name() const override { return "lyrics007.com"; } - -protected: - virtual const char *regex() const override { return "
(.*?)
"; } -}; - struct JustSomeLyricsFetcher : public GoogleLyricsFetcher { virtual const char *name() const override { return "justsomelyrics.com"; } @@ -105,14 +77,6 @@ struct JustSomeLyricsFetcher : public GoogleLyricsFetcher virtual const char *regex() const override { return "
(.*?)See also"; } }; -struct AzLyricsFetcher : public GoogleLyricsFetcher -{ - virtual const char *name() const override { return "azlyrics.com"; } - -protected: - virtual const char *regex() const override { return "
.*?.*
(.*?)
"; } -}; - struct GeniusFetcher : public GoogleLyricsFetcher { virtual const char *name() const override { return "genius.com"; } @@ -142,7 +106,7 @@ struct TekstowoFetcher : public GoogleLyricsFetcher virtual const char *name() const override { return "tekstowo.pl"; } protected: - virtual const char *regex() const override { return "
.*?(.*?)(.*?)
"; } }; struct ZeneszovegFetcher : public GoogleLyricsFetcher @@ -150,7 +114,7 @@ struct ZeneszovegFetcher : public GoogleLyricsFetcher virtual const char *name() const override { return "zeneszoveg.hu"; } protected: - virtual const char *regex() const override { return "
(.*?)
"; } + virtual const char *regex() const override { return "
(.*?)