Merge branch 'ncmpcpp:master' into master
This commit is contained in:
79
.github/workflows/build.yml
vendored
Normal file
79
.github/workflows/build.yml
vendored
Normal 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
|
||||||
@@ -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`.
|
* Add the configuration option `mpd_password`.
|
||||||
* Separate chunks of lyrics with a double newline.
|
* Separate chunks of lyrics with a double newline.
|
||||||
* Fix separator between albums with the same name, to check for album artist
|
* 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)
|
* Add `visualizer_spectrum_smooth_look_legacy_chars` option (enabled by default)
|
||||||
for potentially improved bottom part of the spectrum visualizer in terminals
|
for potentially improved bottom part of the spectrum visualizer in terminals
|
||||||
with transparent background.
|
with transparent background.
|
||||||
|
* Add support for fetching lyrics from tags.
|
||||||
|
|
||||||
# ncmpcpp-0.9.2 (2021-01-24)
|
# ncmpcpp-0.9.2 (2021-01-24)
|
||||||
* Revert suppression of output of all external commands as that makes e.g album
|
* Revert suppression of output of all external commands as that makes e.g album
|
||||||
|
|||||||
2
INSTALL
2
INSTALL
@@ -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
|
For the next two commands, `csh' users will need to prefix them with
|
||||||
`sh '.
|
`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
|
3. Run `./configure' to configure the package for your system. This
|
||||||
will take a while. While running, it prints some messages
|
will take a while. While running, it prints some messages
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
## ncmpcpp – featureful ncurses based MPD client inspired by ncmpc
|
## 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:
|
### Main features:
|
||||||
* tag editor
|
* tag editor
|
||||||
* playlist 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
|
For the next two commands, `csh` users will need to prefix them with
|
||||||
`sh `.
|
`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
|
3. Run `./configure` to configure the package for your system. This
|
||||||
will take a while. While running, it prints some messages
|
will take a while. While running, it prints some messages
|
||||||
|
|||||||
158
autogen.sh
158
autogen.sh
@@ -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
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT([ncmpcpp],[0.10_dev])
|
AC_INIT([ncmpcpp],[0.10.1])
|
||||||
AC_CONFIG_SRCDIR([configure.ac])
|
AC_CONFIG_SRCDIR([configure.ac])
|
||||||
AC_CONFIG_HEADERS(config.h)
|
AC_CONFIG_HEADERS(config.h)
|
||||||
AM_INIT_AUTOMAKE([subdir-objects])
|
AM_INIT_AUTOMAKE([subdir-objects])
|
||||||
@@ -142,7 +142,7 @@ LIBS="$LIBS $BOOST_REGEX_LIBS"
|
|||||||
BOOST_THREAD
|
BOOST_THREAD
|
||||||
AC_SUBST(BOOST_THREAD_LDFLAGS)
|
AC_SUBST(BOOST_THREAD_LDFLAGS)
|
||||||
AC_SUBST(BOOST_THREAD_LIBS)
|
AC_SUBST(BOOST_THREAD_LIBS)
|
||||||
LDFLAGS+="$LDFLAGS $BOOST_THREAD_LDFLAGS"
|
LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS"
|
||||||
LIBS="$LIBS $BOOST_THREAD_LIBS"
|
LIBS="$LIBS $BOOST_THREAD_LIBS"
|
||||||
|
|
||||||
# icu
|
# icu
|
||||||
|
|||||||
@@ -422,7 +422,7 @@
|
|||||||
#
|
#
|
||||||
#cyclic_scrolling = no
|
#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
|
#follow_now_playing_lyrics = no
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -2007,6 +2007,9 @@ bool ReversePlaylist::canBeRun()
|
|||||||
return false;
|
return false;
|
||||||
m_begin = myPlaylist->main().begin();
|
m_begin = myPlaylist->main().begin();
|
||||||
m_end = myPlaylist->main().end();
|
m_end = myPlaylist->main().end();
|
||||||
|
if (m_begin == m_end)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
return findSelectedRangeAndPrintInfoIfNot(m_begin, m_end);
|
return findSelectedRangeAndPrintInfoIfNot(m_begin, m_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ bool configure(int argc, char **argv)
|
|||||||
<< fetcher->name()
|
<< fetcher->name()
|
||||||
<< " : "
|
<< " : "
|
||||||
<< std::flush;
|
<< 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")
|
std::cout << (result.first ? "ok" : "failed")
|
||||||
<< "\n";
|
<< "\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ SongIterator makeSongIterator(IteratorT it)
|
|||||||
> Extractor;
|
> Extractor;
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_convertible<
|
std::is_convertible<
|
||||||
typename std::result_of<Extractor(typename IteratorT::reference)>::type,
|
std::invoke_result_t<Extractor, typename IteratorT::reference>,
|
||||||
SongProperties &
|
SongProperties &
|
||||||
>::value, "invalid result type of SongPropertiesExtractor");
|
>::value, "invalid result type of SongPropertiesExtractor");
|
||||||
return SongIterator(boost::make_transform_iterator(it, Extractor{}));
|
return SongIterator(boost::make_transform_iterator(it, Extractor{}));
|
||||||
@@ -60,7 +60,7 @@ ConstSongIterator makeConstSongIterator(ConstIteratorT it)
|
|||||||
> Extractor;
|
> Extractor;
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_convertible<
|
std::is_convertible<
|
||||||
typename std::result_of<Extractor(typename ConstIteratorT::reference)>::type,
|
std::invoke_result_t<Extractor, typename ConstIteratorT::reference>,
|
||||||
const SongProperties &
|
const SongProperties &
|
||||||
>::value, "invalid result type of SongPropertiesExtractor");
|
>::value, "invalid result type of SongPropertiesExtractor");
|
||||||
return ConstSongIterator(boost::make_transform_iterator(it, Extractor{}));
|
return ConstSongIterator(boost::make_transform_iterator(it, Extractor{}));
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "curl_handle.h"
|
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -29,8 +28,15 @@
|
|||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGLIB_H
|
||||||
|
#include <fileref.h>
|
||||||
|
#include <tpropertymap.h>
|
||||||
|
#endif // HAVE_TAGLIB_H
|
||||||
|
|
||||||
#include "charset.h"
|
#include "charset.h"
|
||||||
|
#include "curl_handle.h"
|
||||||
#include "lyrics_fetcher.h"
|
#include "lyrics_fetcher.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "utility/html.h"
|
#include "utility/html.h"
|
||||||
#include "utility/string.h"
|
#include "utility/string.h"
|
||||||
|
|
||||||
@@ -52,6 +58,10 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
|
|||||||
fetcher = std::make_unique<ZeneszovegFetcher>();
|
fetcher = std::make_unique<ZeneszovegFetcher>();
|
||||||
else if (s == "internet")
|
else if (s == "internet")
|
||||||
fetcher = std::make_unique<InternetLyricsFetcher>();
|
fetcher = std::make_unique<InternetLyricsFetcher>();
|
||||||
|
#ifdef HAVE_TAGLIB_H
|
||||||
|
else if (s == "tags")
|
||||||
|
fetcher = std::make_unique<TagsLyricsFetcher>();
|
||||||
|
#endif // HAVE_TAGLIB_H
|
||||||
else
|
else
|
||||||
is.setstate(std::ios::failbit);
|
is.setstate(std::ios::failbit);
|
||||||
return is;
|
return is;
|
||||||
@@ -60,7 +70,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
|
|||||||
const char LyricsFetcher::msgNotFound[] = "Not found";
|
const char LyricsFetcher::msgNotFound[] = "Not found";
|
||||||
|
|
||||||
LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist,
|
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 result;
|
||||||
result.first = false;
|
result.first = false;
|
||||||
@@ -131,7 +142,7 @@ void LyricsFetcher::postProcess(std::string &data) const
|
|||||||
stripHtmlTags(data);
|
stripHtmlTags(data);
|
||||||
// Remove indentation from each line and collapse multiple newlines into one.
|
// Remove indentation from each line and collapse multiple newlines into one.
|
||||||
std::vector<std::string> lines;
|
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)
|
for (auto &line : lines)
|
||||||
boost::trim(line);
|
boost::trim(line);
|
||||||
auto last = std::unique(
|
auto last = std::unique(
|
||||||
@@ -146,7 +157,8 @@ void LyricsFetcher::postProcess(std::string &data) const
|
|||||||
/**********************************************************************/
|
/**********************************************************************/
|
||||||
|
|
||||||
LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
|
LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
|
||||||
const std::string &title)
|
const std::string &title,
|
||||||
|
const MPD::Song &song)
|
||||||
{
|
{
|
||||||
Result result;
|
Result result;
|
||||||
result.first = false;
|
result.first = false;
|
||||||
@@ -188,7 +200,7 @@ LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
|
|||||||
data = unescapeHtmlUtf8(urls[0]);
|
data = unescapeHtmlUtf8(urls[0]);
|
||||||
|
|
||||||
URL = data.c_str();
|
URL = data.c_str();
|
||||||
return LyricsFetcher::fetch("", "");
|
return LyricsFetcher::fetch("", "", song);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GoogleLyricsFetcher::isURLOk(const std::string &url)
|
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,
|
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;
|
LyricsFetcher::Result result;
|
||||||
result.first = false;
|
result.first = false;
|
||||||
result.second = "The following site may contain lyrics for this song: ";
|
result.second = "The following site may contain lyrics for this song: ";
|
||||||
@@ -214,3 +227,42 @@ bool InternetLyricsFetcher::isURLOk(const std::string &url)
|
|||||||
URL = url;
|
URL = url;
|
||||||
return false;
|
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
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "song.h"
|
||||||
|
|
||||||
struct LyricsFetcher
|
struct LyricsFetcher
|
||||||
{
|
{
|
||||||
typedef std::pair<bool, std::string> Result;
|
typedef std::pair<bool, std::string> Result;
|
||||||
@@ -33,7 +35,7 @@ struct LyricsFetcher
|
|||||||
virtual ~LyricsFetcher() { }
|
virtual ~LyricsFetcher() { }
|
||||||
|
|
||||||
virtual const char *name() const = 0;
|
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:
|
protected:
|
||||||
virtual const char *urlTemplate() const = 0;
|
virtual const char *urlTemplate() const = 0;
|
||||||
@@ -57,7 +59,7 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher);
|
|||||||
|
|
||||||
struct GoogleLyricsFetcher : public LyricsFetcher
|
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:
|
protected:
|
||||||
virtual const char *urlTemplate() const { return URL; }
|
virtual const char *urlTemplate() const { return URL; }
|
||||||
@@ -120,7 +122,7 @@ protected:
|
|||||||
struct InternetLyricsFetcher : public GoogleLyricsFetcher
|
struct InternetLyricsFetcher : public GoogleLyricsFetcher
|
||||||
{
|
{
|
||||||
virtual const char *name() const override { return "the Internet"; }
|
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:
|
protected:
|
||||||
virtual const char *siteKeyword() const override { return nullptr; }
|
virtual const char *siteKeyword() const override { return nullptr; }
|
||||||
@@ -132,4 +134,16 @@ private:
|
|||||||
std::string URL;
|
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
|
#endif // NCMPCPP_LYRICS_FETCHER_H
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ boost::optional<std::string> downloadLyrics(
|
|||||||
<< NC::Format::NoBold << "... ";
|
<< 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 (result_.first == false)
|
||||||
{
|
{
|
||||||
if (shared_buffer)
|
if (shared_buffer)
|
||||||
|
|||||||
@@ -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("header_text_scrolling", &header_text_scrolling, "yes", yes_no);
|
||||||
p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
|
p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
|
||||||
p.add<void>("lyrics_fetchers", nullptr,
|
p.add<void>("lyrics_fetchers", nullptr,
|
||||||
|
#ifdef HAVE_TAGLIB_H
|
||||||
|
"tags, "
|
||||||
|
#endif
|
||||||
"genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet", [this](std::string v) {
|
"genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet", [this](std::string v) {
|
||||||
lyrics_fetchers = list_of<LyricsFetcher_>(v, [](std::string s) {
|
lyrics_fetchers = list_of<LyricsFetcher_>(v, [](std::string s) {
|
||||||
LyricsFetcher_ fetcher;
|
LyricsFetcher_ fetcher;
|
||||||
|
|||||||
Reference in New Issue
Block a user