Merge branch 'ncmpcpp:master' into master

This commit is contained in:
Salastil
2025-02-22 21:19:27 -05:00
committed by GitHub
14 changed files with 190 additions and 181 deletions

79
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
name: build ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, ubuntu-24.04]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install build dependencies
env:
PACKAGES: >
ccache
libboost-dev
libboost-filesystem-dev
libboost-locale-dev
libboost-program-options-dev
libboost-regex-dev
libboost-thread-dev
libcurl4-gnutls-dev
libfftw3-dev
libmpdclient-dev
libncurses-dev
libreadline-dev
libtag1-dev
zlib1g-dev
run: |
sudo apt update -qq
sudo apt install -y --no-install-recommends ${{ env.PACKAGES }}
- name: Set up ccache
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.ccache
key: ccache-${{ matrix.os }}-${{ github.run_id }}
restore-keys: ccache-${{ matrix.os }}-
- name: Run autoreconf
run: |
autoreconf -fiv
- name: Build ncmpcpp (light)
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache/light
run: |
CC='ccache gcc' CXX='ccache g++' ./configure \
--disable-outputs \
--disable-visualizer \
--disable-clock \
--without-fftw \
--without-taglib \
--with-lto=$(nproc)
make -j$(nproc) || exit 1
- name: Build ncmpcpp (full)
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache/full
run: |
CC='ccache gcc' CXX='ccache g++' ./configure \
--enable-outputs \
--enable-visualizer \
--enable-clock \
--with-fftw \
--with-taglib \
--with-lto=$(nproc)
make -j$(nproc) || exit 1

View File

@@ -1,4 +1,9 @@
# ncmpcpp-0.10 (????-??-??)
# ncmpcpp-0.10.1 (2024-10-24)
* Fix compilation with `libc++`.
* Remove `autogen.sh` in favour of `autoreconf`.
* Do not crash when trying to reverse an empty playlist.
# ncmpcpp-0.10 (2024-09-03)
* 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
@@ -19,6 +24,7 @@
* Add `visualizer_spectrum_smooth_look_legacy_chars` option (enabled by default)
for potentially improved bottom part of the spectrum visualizer in terminals
with transparent background.
* Add support for fetching lyrics from tags.
# ncmpcpp-0.9.2 (2021-01-24)
* Revert suppression of output of all external commands as that makes e.g album

View File

@@ -30,7 +30,7 @@ The simplest way to compile this package is:
For the next two commands, `csh' users will need to prefix them with
`sh '.
2. Run `./autogen.sh' to generate the `configure' script.
2. Run `autoreconf -fiv' to generate the `configure' script.
3. Run `./configure' to configure the package for your system. This
will take a while. While running, it prints some messages

View File

@@ -2,6 +2,16 @@
## ncmpcpp featureful ncurses based MPD client inspired by ncmpc
### Project status
The project is officially in maintenance mode. I (Andrzej Rybczak) still use it
daily, but it's feature complete for me and there is very limited time I have
for tending to the issue tracker and open pull requests.
No new, substantial features should be expected (at least from me). However, if
there are any serious bugs or the project outright stops compiling because of
new, incompatible versions of dependencies, it will be fixed.
### Main features:
* tag editor
* playlist editor
@@ -34,7 +44,7 @@ The simplest way to compile this package is:
For the next two commands, `csh` users will need to prefix them with
`sh `.
2. Run `./autogen.sh` to generate the `configure` script.
2. Run `autoreconf -fiv` to generate the `configure` script.
3. Run `./configure` to configure the package for your system. This
will take a while. While running, it prints some messages

View File

@@ -1,158 +0,0 @@
#!/bin/sh
# Run this to set up the build system: configure, makefiles, etc.
# (at one point this was based on the version in enlightenment's cvs)
package="ncmpcpp"
olddir="`pwd`"
srcdir="`dirname $0`"
test -z "$srcdir" && srcdir=.
cd "$srcdir"
DIE=
AM_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9]\).*/\1/"
AC_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9][0-9]\).*/\1/"
VERSIONMKINT="sed -e s/[^0-9]//"
if test -n "$AM_FORCE_VERSION"
then
AM_VERSIONS="$AM_FORCE_VERSION"
else
AM_VERSIONS='1.6 1.7 1.8 1.9'
fi
if test -n "$AC_FORCE_VERSION"
then
AC_VERSIONS="$AC_FORCE_VERSION"
else
AC_VERSIONS='2.58 2.59'
fi
versioned_bins ()
{
bin="$1"
needed_int=`echo $VERNEEDED | $VERSIONMKINT`
for i in $VERSIONS
do
i_int=`echo $i | $VERSIONMKINT`
if test $i_int -ge $needed_int
then
echo $bin-$i $bin$i $bin-$i_int $bin$i_int
fi
done
echo $bin
}
for c in autoconf autoheader automake aclocal
do
uc=`echo $c | tr a-z A-Z`
eval "val=`echo '$'$uc`"
if test -n "$val"
then
echo "$uc=$val in environment, will not attempt to auto-detect"
continue
fi
case "$c" in
autoconf|autoheader)
VERNEEDED=`fgrep AC_PREREQ configure.ac | $AC_VERSIONGREP`
VERSIONS="$AC_VERSIONS"
pkg=autoconf
;;
automake|aclocal)
VERNEEDED=`fgrep AUTOMAKE_OPTIONS Makefile.am | $AM_VERSIONGREP`
VERSIONS="$AM_VERSIONS"
pkg=automake
;;
esac
printf "checking for $c ... "
for x in `versioned_bins $c`; do
($x --version < /dev/null > /dev/null 2>&1) > /dev/null 2>&1
if test $? -eq 0
then
echo $x
eval $uc=$x
break
fi
done
eval "val=`echo '$'$uc`"
if test -z "$val"
then
if test $c = $pkg
then
DIE="$DIE $c=$VERNEEDED"
else
DIE="$DIE $c($pkg)=$VERNEEDED"
fi
fi
done
if test -n "$LIBTOOLIZE"
then
echo "LIBTOOLIZE=$LIBTOOLIZE in environment," \
"will not attempt to auto-detect"
else
printf "checking for libtoolize ... "
for x in libtoolize glibtoolize
do
($x --version < /dev/null > /dev/null 2>&1) > /dev/null 2>&1
if test $? -eq 0
then
echo $x
LIBTOOLIZE=$x
break
fi
done
fi
if test -z "$LIBTOOLIZE"
then
DIE="$DIE libtoolize(libtool)"
fi
if test -n "$DIE"
then
echo "You must have the following installed to compile $package:"
for i in $DIE
do
printf ' '
echo $i | sed -e 's/(/ (from /' -e 's/=\(.*\)/ (>= \1)/'
done
echo "Download the appropriate package(s) for your system,"
echo "or get the source from one of the GNU ftp sites"
echo "listed in http://www.gnu.org/order/ftp.html"
exit 1
fi
echo "Generating configuration files for $package, please wait...."
ACLOCAL_FLAGS="$ACLOCAL_FLAGS"
# /usr/share/aclocal is most likely included by default, already...
ac_local_paths='
/usr/local/share/aclocal
/sw/share/aclocal
/usr/pkg/share/aclocal
/opt/share/aclocal
/usr/gnu/share/aclocal
'
for i in $ac_local_paths; do
if test -d "$i"; then
ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I $i"
# we probably only want one of these...
break
fi
done
echo " $ACLOCAL $ACLOCAL_FLAGS"
$ACLOCAL $ACLOCAL_FLAGS || exit 1
echo " $AUTOHEADER"
$AUTOHEADER || exit 1
echo " $LIBTOOLIZE --automake"
$LIBTOOLIZE --automake || exit 1
echo " $AUTOMAKE --add-missing $AUTOMAKE_FLAGS"
$AUTOMAKE --add-missing $AUTOMAKE_FLAGS || exit 1
echo " $AUTOCONF"
$AUTOCONF || exit 1

View File

@@ -1,4 +1,4 @@
AC_INIT([ncmpcpp],[0.10_dev])
AC_INIT([ncmpcpp],[0.10.1])
AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_HEADERS(config.h)
AM_INIT_AUTOMAKE([subdir-objects])
@@ -142,7 +142,7 @@ LIBS="$LIBS $BOOST_REGEX_LIBS"
BOOST_THREAD
AC_SUBST(BOOST_THREAD_LDFLAGS)
AC_SUBST(BOOST_THREAD_LIBS)
LDFLAGS+="$LDFLAGS $BOOST_THREAD_LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS"
LIBS="$LIBS $BOOST_THREAD_LIBS"
# icu

View File

@@ -422,7 +422,7 @@
#
#cyclic_scrolling = no
#
#lyrics_fetchers = genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet
#lyrics_fetchers = tags, genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet
#
#follow_now_playing_lyrics = no
#

View File

@@ -2007,6 +2007,9 @@ bool ReversePlaylist::canBeRun()
return false;
m_begin = myPlaylist->main().begin();
m_end = myPlaylist->main().end();
if (m_begin == m_end)
return false;
else
return findSelectedRangeAndPrintInfoIfNot(m_begin, m_end);
}

View File

@@ -171,7 +171,7 @@ bool configure(int argc, char **argv)
<< fetcher->name()
<< " : "
<< std::flush;
auto result = fetcher->fetch(std::get<1>(data), std::get<2>(data));
auto result = fetcher->fetch(std::get<1>(data), std::get<2>(data), {});
std::cout << (result.first ? "ok" : "failed")
<< "\n";
}

View File

@@ -46,7 +46,7 @@ SongIterator makeSongIterator(IteratorT it)
> Extractor;
static_assert(
std::is_convertible<
typename std::result_of<Extractor(typename IteratorT::reference)>::type,
std::invoke_result_t<Extractor, typename IteratorT::reference>,
SongProperties &
>::value, "invalid result type of SongPropertiesExtractor");
return SongIterator(boost::make_transform_iterator(it, Extractor{}));
@@ -60,7 +60,7 @@ ConstSongIterator makeConstSongIterator(ConstIteratorT it)
> Extractor;
static_assert(
std::is_convertible<
typename std::result_of<Extractor(typename ConstIteratorT::reference)>::type,
std::invoke_result_t<Extractor, typename ConstIteratorT::reference>,
const SongProperties &
>::value, "invalid result type of SongPropertiesExtractor");
return ConstSongIterator(boost::make_transform_iterator(it, Extractor{}));

View File

@@ -19,7 +19,6 @@
***************************************************************************/
#include "config.h"
#include "curl_handle.h"
#include <cstdlib>
#include <cstring>
@@ -29,8 +28,15 @@
#include <boost/algorithm/string/trim.hpp>
#include <boost/regex.hpp>
#ifdef HAVE_TAGLIB_H
#include <fileref.h>
#include <tpropertymap.h>
#endif // HAVE_TAGLIB_H
#include "charset.h"
#include "curl_handle.h"
#include "lyrics_fetcher.h"
#include "settings.h"
#include "utility/html.h"
#include "utility/string.h"
@@ -52,6 +58,10 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
fetcher = std::make_unique<ZeneszovegFetcher>();
else if (s == "internet")
fetcher = std::make_unique<InternetLyricsFetcher>();
#ifdef HAVE_TAGLIB_H
else if (s == "tags")
fetcher = std::make_unique<TagsLyricsFetcher>();
#endif // HAVE_TAGLIB_H
else
is.setstate(std::ios::failbit);
return is;
@@ -60,7 +70,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
const char LyricsFetcher::msgNotFound[] = "Not found";
LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist,
const std::string &title)
const std::string &title,
[[maybe_unused]] const MPD::Song &song)
{
Result result;
result.first = false;
@@ -131,7 +142,7 @@ void LyricsFetcher::postProcess(std::string &data) const
stripHtmlTags(data);
// Remove indentation from each line and collapse multiple newlines into one.
std::vector<std::string> lines;
boost::split(lines, data, boost::is_any_of("\n"));
boost::split(lines, data, boost::is_any_of("\r\n"));
for (auto &line : lines)
boost::trim(line);
auto last = std::unique(
@@ -146,7 +157,8 @@ void LyricsFetcher::postProcess(std::string &data) const
/**********************************************************************/
LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
const std::string &title)
const std::string &title,
const MPD::Song &song)
{
Result result;
result.first = false;
@@ -188,7 +200,7 @@ LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
data = unescapeHtmlUtf8(urls[0]);
URL = data.c_str();
return LyricsFetcher::fetch("", "");
return LyricsFetcher::fetch("", "", song);
}
bool GoogleLyricsFetcher::isURLOk(const std::string &url)
@@ -199,9 +211,10 @@ bool GoogleLyricsFetcher::isURLOk(const std::string &url)
/**********************************************************************/
LyricsFetcher::Result InternetLyricsFetcher::fetch(const std::string &artist,
const std::string &title)
const std::string &title,
const MPD::Song &song)
{
GoogleLyricsFetcher::fetch(artist, title);
GoogleLyricsFetcher::fetch(artist, title, song);
LyricsFetcher::Result result;
result.first = false;
result.second = "The following site may contain lyrics for this song: ";
@@ -214,3 +227,42 @@ bool InternetLyricsFetcher::isURLOk(const std::string &url)
URL = url;
return false;
}
#ifdef HAVE_TAGLIB_H
LyricsFetcher::Result TagsLyricsFetcher::fetch([[maybe_unused]] const std::string &artist,
[[maybe_unused]] const std::string &title,
const MPD::Song &song)
{
LyricsFetcher::Result result;
result.first = false;
std::string path;
if (song.isFromDatabase())
path += Config.mpd_music_dir;
path += song.getURI();
TagLib::FileRef f(path.c_str());
if (f.isNull())
{
result.second = "Could not open file";
return result;
}
TagLib::PropertyMap properties = f.file()->properties();
if (properties.contains("LYRICS"))
{
result.first = true;
result.second = properties["LYRICS"].toString("\n\n").to8Bit(true);
}
else if (properties.contains("UNSYNCEDLYRICS"))
{
result.first = true;
result.second = properties["UNSYNCEDLYRICS"].toString("\n\n").to8Bit(true);
}
else
result.second = "No lyrics in tags";
return result;
}
#endif // HAVE_TAGLIB_H

View File

@@ -26,6 +26,8 @@
#include <memory>
#include <string>
#include "song.h"
struct LyricsFetcher
{
typedef std::pair<bool, std::string> Result;
@@ -33,7 +35,7 @@ struct LyricsFetcher
virtual ~LyricsFetcher() { }
virtual const char *name() const = 0;
virtual Result fetch(const std::string &artist, const std::string &title);
virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song);
protected:
virtual const char *urlTemplate() const = 0;
@@ -57,7 +59,7 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher);
struct GoogleLyricsFetcher : public LyricsFetcher
{
virtual Result fetch(const std::string &artist, const std::string &title);
virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song);
protected:
virtual const char *urlTemplate() const { return URL; }
@@ -120,7 +122,7 @@ protected:
struct InternetLyricsFetcher : public GoogleLyricsFetcher
{
virtual const char *name() const override { return "the Internet"; }
virtual Result fetch(const std::string &artist, const std::string &title) override;
virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song) override;
protected:
virtual const char *siteKeyword() const override { return nullptr; }
@@ -132,4 +134,16 @@ private:
std::string URL;
};
#ifdef HAVE_TAGLIB_H
struct TagsLyricsFetcher : public LyricsFetcher
{
virtual const char *name() const override { return "tags"; }
virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song) override;
protected:
virtual const char *urlTemplate() const override { return ""; }
virtual const char *regex() const override { return ""; }
};
#endif // HAVE_TAGLIB_H
#endif // NCMPCPP_LYRICS_FETCHER_H

View File

@@ -151,7 +151,7 @@ boost::optional<std::string> downloadLyrics(
<< NC::Format::NoBold << "... ";
}
}
auto result_ = fetcher_->fetch(s_artist, s_title);
auto result_ = fetcher_->fetch(s_artist, s_title, s);
if (result_.first == false)
{
if (shared_buffer)

View File

@@ -462,6 +462,9 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
p.add("header_text_scrolling", &header_text_scrolling, "yes", yes_no);
p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
p.add<void>("lyrics_fetchers", nullptr,
#ifdef HAVE_TAGLIB_H
"tags, "
#endif
"genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet", [this](std::string v) {
lyrics_fetchers = list_of<LyricsFetcher_>(v, [](std::string s) {
LyricsFetcher_ fetcher;