64 Commits

Author SHA1 Message Date
Salastil
5e31b41f1f Update README with yt-dlp fork details
All checks were successful
CI / build ubuntu-22.04 (push) Successful in 16m17s
CI / build ubuntu-24.04 (push) Successful in 16m2s
Added information about yt-dlp fork and project status.
2025-11-30 22:18:53 -05:00
Salastil
b36a41d4e9 Align yt-dlp to the boost removal changes
Some checks failed
CI / build ubuntu-24.04 (push) Has been cancelled
CI / build ubuntu-22.04 (push) Has been cancelled
2025-11-30 22:13:06 -05:00
Salastil
900939f4ad Merge branch 'ncmpcpp:master' into master 2025-09-11 13:46:06 -04:00
Michael Cho
2b4bd89e2e Update boost.m4 to fix build with Boost >= 1.89 (#636) 2025-09-11 16:36:41 +02:00
Salastil
d885583d6e Merge branch 'ncmpcpp:master' into master 2025-04-21 18:56:55 -04:00
Andrzej Rybczak
e8f1c4bd48 Add support for hexadecimal HTML escape codes 2025-02-26 02:35:09 +01:00
Andrzej Rybczak
2f9a65e177 Update lyrics fetchers 2025-02-24 01:13:48 +01:00
Salastil
27b107ee37 Merge branch 'ncmpcpp:master' into master 2025-02-22 21:19:27 -05:00
Andrzej Rybczak
3e6d992b93 Release 0.10.1 (#617) 2024-10-24 14:28:08 +02:00
Andrzej Rybczak
4fdafc5320 Do not crash when trying to reverse an empty playlist
Fixes https://github.com/ncmpcpp/ncmpcpp/issues/616.
2024-10-24 01:18:16 +02:00
Andrzej Rybczak
440e9a5768 Remove autogen.sh in favour of autoreconf 2024-10-05 09:23:49 +02:00
Andrzej Rybczak
4ac9a0f0dd Add CI (#610)
Adaptation of 2148c716c7/.github/workflows/build.yml
2024-10-05 09:07:38 +02:00
Andrzej Rybczak
92b478ef31 Fix compilation with libc++ 2024-10-05 08:42:08 +02:00
fkobi
72d990fe25 fix stray + in LDFLAGS assignment (#608)
Signed-off-by: Filip Kobierski <fkobi@pm.me>
2024-09-12 21:02:27 +02:00
Andrzej Rybczak
f9d0fb608c Put project status in the README
Fixes https://github.com/ncmpcpp/ncmpcpp/issues/607.
2024-09-09 12:29:26 +02:00
Andrzej Rybczak
1d7d5152c6 Release 0.10 2024-09-03 03:34:57 +02:00
Andrzej Rybczak
9c27eec846 Add support for fetching lyrics from tags
Adaptation of https://github.com/ncmpcpp/ncmpcpp/pull/482.
2024-09-03 03:15:09 +02:00
Salastil
4b9eb741ea Merge branch 'ncmpcpp:master' into master 2024-08-25 21:35:43 -04:00
Andrzej Rybczak
510e28c65a Remove link to project page, it's deprecated 2024-08-21 19:18:21 +02:00
Andrzej Rybczak
37aea91be1 Don't throw when window doesn't fit, ncurses can deal with it 2024-08-21 19:13:21 +02:00
Andrzej Rybczak
377994daca Remove Larson from assignes in issue templates 2024-08-21 18:36:13 +02:00
Andrzej Rybczak
33145940c8 curl is not optional 2024-08-21 18:29:36 +02:00
Andrzej Rybczak
cd9ae93c3e Fix boost::regex ICU support 2024-08-21 18:19:02 +02:00
Andrzej Rybczak
ba484cff1e Use C++20 2024-08-21 18:09:24 +02:00
Andrzej Rybczak
603735d80e Clear warnings 2024-08-21 17:42:30 +02:00
o0nd7ots
5238d0f6ed Update README.md (#602)
updated links
2024-08-21 17:13:22 +02:00
Will Huie
7078ed7f55 Tweaks to frequency spectrum and sound ellipse in visualizer (#464)
* use visualizer_spectrum_log_scale_{x,y} to control log scaling

* change sound ellipse character to the circle instead of the bar

* oops, should be determined by x-axis scaling

* rebase to upstream

* use values from Config directly

* fix that weird first-column screen-wrapping issue when drawing

* rebase to upstream

---------

Co-authored-by: whooie <wchooie@gmail.com>
Co-authored-by: Andrzej Rybczak <arybczak@users.noreply.github.com>
2024-08-21 17:12:45 +02:00
Andrzej Rybczak
f51968b98b Rename the new option, slightly reword the description, add changelog 2024-08-21 16:45:37 +02:00
TilCreator
f83428a665 Improve specrum visulaizer for transparent backgrounds (#467)
* Replace "hack" with hacky chars

* Remove unnecessary spaces

* Add config option visualizer_spectrum_use_legacy_chars
2024-08-21 16:34:12 +02:00
Andrzej Rybczak
1c77138f51 Update changelog 2024-08-21 16:26:07 +02:00
Andrzej Rybczak
838f600b57 Fix compilation with taglib 1.11 2024-08-21 16:14:32 +02:00
Andrzej Rybczak
68daf44032 Don't treat unknown lyrics fetchers as errors 2024-07-24 21:39:11 +02:00
Andrzej Rybczak
985bc09b5c Update lyrics fetchers
Removes azlyrics, musixmatch, sing365/lyrics007 and metrolyrics.
2024-07-24 20:36:11 +02:00
Andrzej Rybczak
b2c8ca91c4 Don't treat empty strings as numbers 2024-07-24 19:38:30 +02:00
Andrzej Rybczak
6f8d12da8c If both strings are numbers, compare them as such 2024-07-24 19:18:00 +02:00
Andrzej Rybczak
af7550a69f Adjust the new regex to require 2 minute digits 2024-07-24 18:33:49 +02:00
djvs
8ecbb8b49b Expose filepath in columns view (#530)
* Expose filepath in columns view

* update
2024-07-24 18:15:02 +02:00
James Le Cuirot
b218d90193 Check for taglib with pkg-config before trying taglib-config (#558)
Programs like taglib-config are not good when cross-compiling.
2024-07-24 18:07:34 +02:00
gardockt
95bca25cb6 Tag editor: only write files with modified tags (#536) 2024-07-24 18:04:51 +02:00
George Abbott
8746ded880 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
2024-07-24 18:01:07 +02:00
Alex Balgavy
f76dd085b2 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.
2024-07-24 17:57:38 +02:00
Denys Tynok
e87c104dd6 fix: update obsolete macros in autoconf (#582)
* fix: update obsolete macros in autoconf

* fix: update obsolete macro 'AC_PROG_LIBTOOL' in autoconf
2024-07-24 17:56:14 +02:00
Andrzej Rybczak
afb150138d Change visibility of orig_termios 2024-07-24 17:55:35 +02:00
Drew Marino
51ebe9e52d 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.
2024-07-24 17:54:12 +02:00
Andrzej Rybczak
9c396d7ef0 Fix removal of XiphComments 2024-07-24 17:25:03 +02:00
Andrzej Rybczak
81e5cf58b4 extras: fix compilation with taglib 2.0 2024-05-30 14:58:44 +02:00
Andrzej Rybczak
dc46f7a49b Fix compilation with taglib 2.0 2024-02-07 14:29:09 +01:00
Salastil
c7593a2087 Missing quote fix 2023-09-21 21:41:43 -04:00
Salastil
73f5de7511 Merge branch 'lenerd/youtube-dl' 2023-08-27 03:25:28 -04:00
Larson Carter
9f44edf0b1 Merge pull request #560 from zaidhaan/fix-mpd-block
Fix unresponsive stall when server error occurs
2022-12-31 12:52:39 -06:00
Zaidhaan Hussain
d96e3e514d Fix MPD blocking when server error occurs by handling directly without blocking further execution by throwing 2022-11-21 23:05:31 +08:00
Robert Kaspar
417d7172e5 Read Performer from TPE4, not TPE3 (#497) 2022-02-14 00:48:11 +01:00
leg7
727ca95ca5 Increase the framerate limit for the visualizer. (#490)
* Update settings.cpp

* Update settings.cpp
2022-02-14 00:47:32 +01:00
squigz
26ee24a938 Add italic text support (#527) 2022-02-14 00:42:13 +01:00
Jeffrey Paul
02754534fd update homepage link to https (#529)
modern browsers don't support http
2022-02-08 15:50:38 +01:00
Andrzej Rybczak
40e4f98ee8 Support sockets without a socket inode 2021-08-25 02:37:54 +02:00
Andrzej Rybczak
b023c737e3 Remove options deprecated in 0.9 2021-07-29 13:19:54 +02:00
Andrzej Rybczak
70ca94f739 Curl: stop setting user agent to ncmpcpp 2021-07-27 00:47:46 +02:00
Andrzej Rybczak
df1c4d7d7d Fix genius lyrics fetcher 2021-07-26 23:44:36 +02:00
Andrzej Rybczak
35f9262a6e Fix compilation 2021-04-25 23:57:56 +02:00
Jinwoo Park
f47cf7f37c 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
2021-04-25 19:42:56 +02:00
Jonathan Neidel
22fd919ce4 Fix separator between albums with the same name, to check for album artist instead of artist (#472) 2021-04-25 19:41:07 +02:00
John Ferguson
71970fa73c 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
2021-04-25 19:39:52 +02:00
John Ferguson
295663fc1c update .gitignore to ignore more build artifacts (#475) 2021-04-25 19:37:27 +02:00
47 changed files with 1218 additions and 619 deletions

View File

@@ -3,7 +3,7 @@ name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: larson-carter
assignees:
---

View File

@@ -3,7 +3,7 @@ name: Feature request
about: Suggest an idea for this project
title: "[FEATURE]"
labels: enhancement
assignees: larson-carter
assignees:
---

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

5
.gitignore vendored
View File

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

View File

@@ -1,6 +1,34 @@
# ncmpcpp-0.10 (????-??-??)
# ncmpcpp-0.10.2 (2025-??-??)
* Update lyrics fetchers.
* Add support for hexadecimal HTML escape codes.
# 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
instead of artist.
* Column widths are now configurable via `media_library_column_width_ratio_two`,
`media_library_column_width_ratio_three` and
`playlist_editor_column_width_ratio`.
* Removed deprecated `visualizer_fifo_path` and `visualizer_sync_interval`
options.
* Added support for italic text.
* Framerate limit for visualizer was removed.
* Performer tag is now read from the `TPE4` frame instead of `TPE3`.
* Potential crash when fetching lyrics for streams in the background was fixed.
* Seeking now accepts the `hh:mm:ss` format.
* Tag editor now only writes to files with modified tags.
* Column view can now display full filepaths.
* Updated the list of working lyrics fetchers.
* 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

@@ -1,11 +1,22 @@
# NCurses Music Player Client (Plus Plus)
Project page - http://rybczak.net/ncmpcpp/
# NCurses Music Player Client (Plus Plus) (Plus yt-dlp)
## ncmpcpp featureful ncurses based MPD client inspired by ncmpc
### Main features:
### yt-dlp fork
This fork allows adding items with yt-dlp via shift+d at the playlist screen, add a single item or a playlist (with a cap of 100 items)
## 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
* easy to use search engine
@@ -18,19 +29,18 @@ Project page - http://rybczak.net/ncmpcpp/
…and a lot more minor functions.
### Dependencies:
* boost library [https://www.boost.org/]
* ncurses library [http://www.gnu.org/software/ncurses/ncurses.html]
* readline library [https://tiswww.case.edu/php/chet/readline/rltop.html]
* curl library (optional, required for fetching lyrics and last.fm data) [https://curl.haxx.se/]
* fftw library (optional, required for frequency spectrum music visualization mode) [http://www.fftw.org/]
* tag library (optional, required for tag editing) [https://taglib.org/]
* [boost](https://www.boost.org/)
* [ncurses](https://invisible-island.net/ncurses/announce.html)
* [readline](https://tiswww.case.edu/php/chet/readline/rltop.html)
* [curl](https://curl.se), for fetching lyrics and last.fm data
#### Optional libraries
* [fftw](http://www.fftw.org), for frequency spectrum music visualization mode
* [taglib](https://taglib.org/), for tag editing
### Known issues:
* No full support for handling encodings other than UTF-8.
### Installation:
The simplest way to compile this package is:
1. `cd` to the directory containing the package's source code.
@@ -38,7 +48,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
@@ -55,7 +65,6 @@ The simplest way to compile this package is:
Detailed intallation instructions can be found in the `INSTALL` file.
### Optional features:
Optional features can be enable by specifying them during configure. For
example, to enable visualizer run `./configure --enable-visualizer`.

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,15 +1,15 @@
AC_INIT([ncmpcpp], [0.10_dev])
AC_INIT([ncmpcpp],[0.10.2_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])
@@ -70,15 +70,15 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]])],
)
CXXFLAGS="$old_CXXFLAGS $fast_math"
# -std=c++14
AC_MSG_CHECKING([whether compiler supports -std=c++14])
# -std=c++20
AC_MSG_CHECKING([whether compiler supports -std=c++20])
old_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="-std=c++14"
CXXFLAGS="-std=c++20"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]])],
AC_MSG_RESULT([yes])
std_cpp14="-std=c++14",
std_cpp14="-std=c++20",
AC_MSG_RESULT([no])
AC_MSG_ERROR([[Your compiler doesn't seem to support C++14, please upgrade (GCC >= 5)]])
AC_MSG_ERROR([[Your compiler doesn't seem to support C++20, please upgrade]])
)
CXXFLAGS="$old_CXXFLAGS $std_cpp14"
@@ -90,6 +90,9 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[auto f = [](auto n) { return n*n; }; f(
AC_MSG_ERROR([[Your compiler doesn't seem to support generic lambda expressions, please upgrade (GCC >= 5)]])
)
# warnings
CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wshadow -Wimplicit-fallthrough"
# boost
BOOST_REQUIRE([1.60])
AC_SUBST(BOOST_CPPFLAGS)
@@ -139,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
@@ -149,7 +152,7 @@ PKG_CHECK_MODULES([ICU], [icu-i18n icu-uc], [
old_LIBS="$LIBS"
AC_SUBST(ICU_CFLAGS)
AC_SUBST(ICU_LIBS)
CPPFLAGS="$CPPFLAGS $ICU_CFLAGS -DU_USING_ICU_NAMESPACE=0"
CPPFLAGS="$CPPFLAGS $ICU_CFLAGS"
LIBS="$LIBS $ICU_LIBS"
AC_MSG_CHECKING([whether boost.regex was compiled with ICU support])
AC_LINK_IFELSE([AC_LANG_PROGRAM([
@@ -257,19 +260,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

View File

@@ -125,6 +125,14 @@
#
#visualizer_spectrum_smooth_look = yes
#
## Use unicode block characters from "symbols for legacy computing". This
## improves the smooth look on transparent terminals by using special unicode
## chars instead of reversing the background and foreground color on the bottom
## edge of the spectrum. If it leads to a garbled output on the bottom edge of
## the spectrum, you can either change the font or disable this option.
#
#visualizer_spectrum_smooth_look_legacy_chars = yes
#
## A value between 1 and 5 inclusive. Specifying a larger value makes the
## visualizer look at a larger slice of time, which results in less jumpy
## visualizer output.
@@ -141,6 +149,11 @@
#
#visualizer_spectrum_hz_max = 20000
#
## Use log scaling for the frequency spectrum axes
#
#visualizer_spectrum_log_scale_x = yes
#visualizer_spectrum_log_scale_y = yes
#
##### system encoding #####
##
## ncmpcpp should detect your charset encoding but if it failed to do so, you
@@ -172,6 +185,7 @@
##
## %l - length
## %f - filename
## %F - full filepath
## %D - directory
## %a - artist
## %A - album artist
@@ -215,6 +229,7 @@
## - 9 - end of current color
## - b - bold text
## - u - underline text
## - i - italic text
## - r - reverse colors
## - a - use alternative character set
##
@@ -407,7 +422,7 @@
#
#cyclic_scrolling = no
#
#lyrics_fetchers = azlyrics, genius, musixmatch, sing365, metrolyrics, justsomelyrics, jahlyrics, plyrics, tekstowo, zeneszoveg, internet
#lyrics_fetchers = tags, genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet
#
#follow_now_playing_lyrics = no
#
@@ -465,6 +480,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

View File

@@ -114,6 +114,13 @@ Automatically scale visualizer size.
.B visualizer_spectrum_smooth_look = yes/no
For spectrum visualizer, use unicode block characters for a smoother, more continuous look. This will override the visualizer_look option. With transparent terminals and visualizer_in_stereo set, artifacts may be visible on the bottom half of the visualization.
.TP
.B visualizer_spectrum_smooth_look_legacy_chars = yes/no
Use unicode block characters from "symbols for legacy computing". This improves
the smooth look on transparent terminals by using special unicode chars instead
of reversing the background and foreground color on the bottom edge of the
spectrum. If it leads to a garbled output on the bottom edge of the spectrum,
you can either change the font or disable this option.
.TP
.B visualizer_spectrum_dft_size = NUMBER
For spectrum visualizer, a value between 1 and 5 inclusive. Specifying a larger value makes the visualizer look at a larger slice of time, which results in less jumpy visualizer output.
.TP
@@ -335,6 +342,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
@@ -463,6 +479,7 @@ For song format you can use:
%l - length
%f - filename
%F - full filepath
%D - directory
%a - artist
%A - album artist

View File

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

View File

@@ -22,7 +22,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
m4_define([_BOOST_SERIAL], [m4_translit([
# serial 26
# serial 39
], [#
], [])])
@@ -226,7 +226,7 @@ AC_LANG_POP([C++])dnl
AC_CACHE_CHECK([for Boost's header version],
[boost_cv_lib_version],
[m4_pattern_allow([^BOOST_LIB_VERSION$])dnl
_BOOST_SED_CPP([[/^boost-lib-version = /{s///;s/[\" ]//g;p;q;}]],
_BOOST_SED_CPP([[/^.*boost-lib-version = /{s///;s/[\" ]//g;p;q;}]],
[#include <boost/version.hpp>
boost-lib-version = BOOST_LIB_VERSION],
[boost_cv_lib_version=`cat conftest.i`])])
@@ -288,14 +288,17 @@ fi
# BOOST_FIND_LIBS([COMPONENT-NAME], [CANDIDATE-LIB-NAMES],
# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST],
# [CXX-PROLOGUE])
# [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE],
# [ERROR_ON_UNUSABLE])
# --------------------------------------------------------------
# Look for the Boost library COMPONENT-NAME (e.g., `thread', for
# libboost_thread) under the possible CANDIDATE-LIB-NAMES (e.g.,
# "thread_win32 thread"). Check that HEADER-NAME works and check that
# libboost_LIB-NAME can link with the code CXX-TEST. The optional
# argument CXX-PROLOGUE can be used to include some C++ code before
# the `main' function.
# the `main' function. The CXX-POST-INCLUDE-PROLOGUE can be used to
# include some code before the `main' function, but after the
# `#include <HEADER-NAME>'.
#
# Invokes BOOST_FIND_HEADER([HEADER-NAME]) (see above).
#
@@ -309,6 +312,9 @@ fi
# builds. Some sample values for PREFERRED-RT-OPT: (nothing), mt, d, mt-d, gdp
# ... If you want to make sure you have a specific version of Boost
# (eg, >= 1.33) you *must* invoke BOOST_REQUIRE before this macro.
#
# ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their
# configure to fail
AC_DEFUN([BOOST_FIND_LIBS],
[AC_REQUIRE([BOOST_REQUIRE])dnl
AC_REQUIRE([_BOOST_FIND_COMPILER_TAG])dnl
@@ -317,26 +323,32 @@ AC_REQUIRE([_BOOST_GUESS_WHETHER_TO_USE_MT])dnl
if test x"$boost_cv_inc_path" = xno; then
AC_MSG_NOTICE([Boost not available, not searching for the Boost $1 library])
else
dnl The else branch is huge and wasn't intended on purpose.
dnl The else branch is huge and wasn't indented on purpose.
AC_LANG_PUSH([C++])dnl
AS_VAR_PUSHDEF([Boost_lib], [boost_cv_lib_$1])dnl
AS_VAR_PUSHDEF([Boost_lib_LDFLAGS], [boost_cv_lib_$1_LDFLAGS])dnl
AS_VAR_PUSHDEF([Boost_lib_LDPATH], [boost_cv_lib_$1_LDPATH])dnl
AS_VAR_PUSHDEF([Boost_lib_LIBS], [boost_cv_lib_$1_LIBS])dnl
BOOST_FIND_HEADER([$4])
AS_IF([test x"$8" = "xno"], [not_found_header='true'])
BOOST_FIND_HEADER([$4], [$not_found_header])
boost_save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib],
[_BOOST_FIND_LIBS($@)])
case $Boost_lib in #(
(yes) _AC_MSG_LOG_CONFTEST
AC_DEFINE(AS_TR_CPP([HAVE_BOOST_$1]), [1], [Defined if the Boost $1 library is available])dnl
AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS])dnl
AC_SUBST(AS_TR_CPP([BOOST_$1_LDPATH]), [$Boost_lib_LDPATH])dnl
AC_SUBST([BOOST_LDPATH], [$Boost_lib_LDPATH])dnl
AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS])dnl
;;
(no) _AC_MSG_LOG_CONFTEST
AC_MSG_ERROR([cannot find the flags to link with Boost $1])
AS_IF([test x"$8" != "xno"], [
AC_MSG_ERROR([cannot find flags to link with the Boost $1 library (libboost-$1)])
])
;;
esac
AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS])dnl
AC_SUBST(AS_TR_CPP([BOOST_$1_LDPATH]), [$Boost_lib_LDPATH])dnl
AC_SUBST([BOOST_LDPATH], [$Boost_lib_LDPATH])dnl
AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS])dnl
CPPFLAGS=$boost_save_CPPFLAGS
AS_VAR_POPDEF([Boost_lib])dnl
AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl
@@ -349,16 +361,20 @@ fi
# BOOST_FIND_LIB([LIB-NAME],
# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST],
# [CXX-PROLOGUE])
# [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE],
# [ERROR_ON_UNUSABLE])
# --------------------------------------------------------------
# Backward compatibility wrapper for BOOST_FIND_LIBS.
# ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their
# configure to fail
AC_DEFUN([BOOST_FIND_LIB],
[BOOST_FIND_LIBS([$1], $@)])
# _BOOST_FIND_LIBS([LIB-NAME], [CANDIDATE-LIB-NAMES],
# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST],
# [CXX-PROLOGUE])
# [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE],
# [ERROR_ON_UNUSABLE])
# --------------------------------------------------------------
# Real implementation of BOOST_FIND_LIBS: rely on these local macros:
# Boost_lib, Boost_lib_LDFLAGS, Boost_lib_LDPATH, Boost_lib_LIBS
@@ -370,6 +386,9 @@ AC_DEFUN([BOOST_FIND_LIB],
# usually installed. If we can't find the standard variants, we try
# to enforce -mt (for instance on MacOSX, libboost_thread.dylib
# doesn't exist but there's -obviously- libboost_thread-mt.dylib).
#
# ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their
# configure to fail
AC_DEFUN([_BOOST_FIND_LIBS],
[Boost_lib=no
case "$3" in #(
@@ -396,7 +415,8 @@ AC_DEFUN([_BOOST_FIND_LIBS],
AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?])
boost_save_ac_objext=$ac_objext
# Generate the test file.
AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <$4>
AC_LANG_CONFTEST([AC_LANG_PROGRAM([$7
#include <$4>
$6], [$5])])
dnl Optimization hacks: compiling C++ is slow, especially with Boost. What
dnl we're trying to do here is guess the right combination of link flags
@@ -416,7 +436,10 @@ dnl empty because the test file is generated only once above (before we
dnl start the for loops).
AC_COMPILE_IFELSE([],
[ac_objext=do_not_rm_me_plz],
[AC_MSG_ERROR([cannot compile a test that uses Boost $1])])
[AS_IF([test x"$8" != x"no"], [
AC_MSG_ERROR([cannot compile a test that uses Boost $1])
])
])
ac_objext=$boost_save_ac_objext
boost_failed_libs=
# Don't bother to ident the following nested for loops, only the 2
@@ -426,12 +449,15 @@ for boost_tag_ in -$boost_cv_lib_tag ''; do
for boost_ver_ in -$boost_cv_lib_version ''; do
for boost_mt_ in $boost_mt -mt ''; do
for boost_rtopt_ in $boost_rtopt '' -d; do
for boost_lib in \
boost_$boost_lib_$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \
boost_$boost_lib_$boost_tag_$boost_rtopt_$boost_ver_ \
boost_$boost_lib_$boost_tag_$boost_mt_$boost_ver_ \
boost_$boost_lib_$boost_tag_$boost_ver_
for boost_full_suffix in \
$boost_last_suffix \
x$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \
x$boost_tag_$boost_rtopt_$boost_ver_ \
x$boost_tag_$boost_mt_$boost_ver_ \
x$boost_tag_$boost_ver_
do
boost_real_suffix=`echo "$boost_full_suffix" | sed 's/^x//'`
boost_lib="boost_$boost_lib_$boost_real_suffix"
# Avoid testing twice the same lib
case $boost_failed_libs in #(
(*@$boost_lib@*) continue;;
@@ -480,7 +506,7 @@ dnl generated only once above (before we start the for loops).
*)
for boost_cv_rpath_link_ldflag in -Wl,-R, -Wl,-rpath,; do
LDFLAGS="$boost_save_LDFLAGS -L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath"
LIBS="$boost_save_LIBS $Boost_lib_LIBS"
LIBS="$Boost_lib_LIBS $boost_save_LIBS"
_BOOST_AC_LINK_IFELSE([],
[boost_rpath_link_ldflag_found=yes
break],
@@ -496,6 +522,7 @@ dnl generated only once above (before we start the for loops).
test x"$boost_ldpath" != x &&
Boost_lib_LDFLAGS="-L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath"
Boost_lib_LDPATH="$boost_ldpath"
boost_last_suffix="$boost_full_suffix"
break 7
else
boost_failed_libs="$boost_failed_libs@$boost_lib@"
@@ -534,6 +561,14 @@ m4_popdef([BOOST_Library])dnl
])
])
# BOOST_ANY()
# ------------
# Look for Boost.Any
BOOST_DEFUN([Any],
[BOOST_FIND_HEADER([boost/any.hpp])])
# BOOST_ARRAY()
# -------------
# Look for Boost.Array
@@ -548,6 +583,12 @@ BOOST_DEFUN([Asio],
[AC_REQUIRE([BOOST_SYSTEM])dnl
BOOST_FIND_HEADER([boost/asio.hpp])])
# BOOST_BIMAP()
# ------------
# Look for Boost.Bimap
BOOST_DEFUN([Bimap],
[BOOST_FIND_HEADER([boost/bimap.hpp])])
# BOOST_ASSIGN()
# -------------
@@ -556,6 +597,24 @@ BOOST_DEFUN([Assign],
[BOOST_FIND_HEADER([boost/assign.hpp])])
# BOOST_ATOMIC([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -------------------------------
# Look for Boost.Atomic. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Atomic],
[BOOST_FIND_LIB([atomic], [$1],
[boost/atomic.hpp],
[boost::atomic<int> a;],
[ ],
[#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif], [$2])
])# BOOST_ATOMIC
# BOOST_BIND()
# ------------
# Look for Boost.Bind.
@@ -563,7 +622,14 @@ BOOST_DEFUN([Bind],
[BOOST_FIND_HEADER([boost/bind.hpp])])
# BOOST_CHRONO()
# BOOST_CAST()
# ------------
# Look for Boost.Cast
BOOST_DEFUN([Cast],
[BOOST_FIND_HEADER([boost/cast.hpp])])
# BOOST_CHRONO([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# --------------
# Look for Boost.Chrono.
BOOST_DEFUN([Chrono],
@@ -571,7 +637,7 @@ BOOST_DEFUN([Chrono],
# added as of 1.35.0. If we have a version <1.35, we must not attempt to
# find Boost.System as it didn't exist by then.
if test $boost_major_version -ge 135; then
BOOST_SYSTEM([$1])
BOOST_SYSTEM([$1], [$2])
fi # end of the Boost.System check.
boost_filesystem_save_LIBS=$LIBS
boost_filesystem_save_LDFLAGS=$LDFLAGS
@@ -580,7 +646,7 @@ LIBS="$LIBS $BOOST_SYSTEM_LIBS"
LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS"
BOOST_FIND_LIB([chrono], [$1],
[boost/chrono.hpp],
[boost::chrono::thread_clock d;])
[boost::chrono::thread_clock d;], [], [], [$2])
if test $enable_static_boost = yes && test $boost_major_version -ge 135; then
BOOST_CHRONO_LIBS="$BOOST_CHRONO_LIBS $BOOST_SYSTEM_LIBS"
fi
@@ -589,7 +655,7 @@ LDFLAGS=$boost_filesystem_save_LDFLAGS
])# BOOST_CHRONO
# BOOST_CONTEXT([PREFERRED-RT-OPT])
# BOOST_CONTEXT([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -----------------------------------
# Look for Boost.Context. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
@@ -597,17 +663,76 @@ LDFLAGS=$boost_filesystem_save_LDFLAGS
# * This library was introduced in Boost 1.51.0
# * The signatures of make_fcontext() and jump_fcontext were changed in 1.56.0
# * A dependency on boost_thread appears in 1.57.0
# * The implementation details were moved to boost::context::detail in 1.61.0
# * 1.61 also introduces execution_context_v2, which is the "lowest common
# denominator" for boost::context presence since then.
# * boost::context::fiber was introduced in 1.69 and execution_context_v2 was
# removed in 1.72
BOOST_DEFUN([Context],
[boost_context_save_LIBS=$LIBS
boost_context_save_LDFLAGS=$LDFLAGS
if test $boost_major_version -ge 157; then
BOOST_THREAD([$1])
BOOST_THREAD([$1], [$2])
m4_pattern_allow([^BOOST_THREAD_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_THREAD_LIBS"
LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS"
fi
if test $boost_major_version -ge 169; then
BOOST_FIND_LIB([context], [$1],
[boost/context/all.hpp],[[
[boost/context/fiber.hpp], [[
namespace ctx=boost::context;
int a;
ctx::fiber source{[&a](ctx::fiber&& sink){
a=0;
int b=1;
for(;;){
sink=std::move(sink).resume();
int next=a+b;
a=b;
b=next;
}
return std::move(sink);
}};
for (int j=0;j<10;++j) {
source=std::move(source).resume();
}
return a == 34;
]], [], [], [$2])
elif test $boost_major_version -ge 161; then
BOOST_FIND_LIB([context], [$1],
[boost/context/execution_context_v2.hpp], [[
namespace ctx=boost::context;
int res=0;
int n=35;
ctx::execution_context<int> source(
[n, &res](ctx::execution_context<int> sink, int) mutable {
int a=0;
int b=1;
while(n-->0){
auto result=sink(a);
sink=std::move(std::get<0>(result));
auto next=a+b;
a=b;
b=next;
}
return sink;
});
for(int i=0;i<10;++i){
auto result=source(i);
source=std::move(std::get<0>(result));
res = std::get<1>(result);
}
return res == 34;
]], [], [], [$2])
else
BOOST_FIND_LIB([context], [$1],
[boost/context/fcontext.hpp],[[
// creates a stack
void * stack_pointer = new void*[4096];
@@ -662,7 +787,10 @@ static void f(intptr_t i) {
ctx::jump_fcontext(&fc, fcm, i * 2);
}
#endif
])
], [], [], [$2])
fi
LIBS=$boost_context_save_LIBS
LDFLAGS=$boost_context_save_LDFLAGS
])# BOOST_CONTEXT
@@ -677,7 +805,7 @@ BOOST_FIND_HEADER([boost/lexical_cast.hpp])
])# BOOST_CONVERSION
# BOOST_COROUTINE([PREFERRED-RT-OPT])
# BOOST_COROUTINE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -----------------------------------
# Look for Boost.Coroutine. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. This library was introduced in Boost
@@ -687,10 +815,10 @@ BOOST_DEFUN([Coroutine],
boost_coroutine_save_LIBS=$LIBS
boost_coroutine_save_LDFLAGS=$LDFLAGS
# Link-time dependency from coroutine to context
BOOST_CONTEXT([$1])
BOOST_CONTEXT([$1], [$2])
# Starting from Boost 1.55 a dependency on Boost.System is added
if test $boost_major_version -ge 155; then
BOOST_SYSTEM([$1])
BOOST_SYSTEM([$1], [$2])
fi
m4_pattern_allow([^BOOST_(CONTEXT|SYSTEM)_(LIBS|LDFLAGS)])
LIBS="$LIBS $BOOST_CONTEXT_LIBS $BOOST_SYSTEM_LIBS"
@@ -698,7 +826,8 @@ LDFLAGS="$LDFLAGS $BOOST_CONTEXT_LDFLAGS"
# in 1.53 coroutine was a header only library
if test $boost_major_version -eq 153; then
BOOST_FIND_HEADER([boost/coroutine/coroutine.hpp])
AS_IF([test x"$2" = "xno"], [not_found_header='true'])
BOOST_FIND_HEADER([boost/coroutine/coroutine.hpp], [$not_found_header])
else
BOOST_FIND_LIB([coroutine], [$1],
[boost/coroutine/coroutine.hpp],
@@ -709,7 +838,7 @@ else
#else
boost::coroutines::asymmetric_coroutine<int>::pull_type coro; coro.get();
#endif
])
], [], [], [$2])
fi
# Link-time dependency from coroutine to context, existed only in 1.53, in 1.54
# coroutine doesn't use context from its headers but from its library.
@@ -734,18 +863,25 @@ BOOST_DEFUN([CRC],
])# BOOST_CRC
# BOOST_DATE_TIME([PREFERRED-RT-OPT])
# BOOST_DATE_TIME([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -----------------------------------
# Look for Boost.Date_Time. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Date_Time],
[BOOST_FIND_LIB([date_time], [$1],
[boost/date_time/posix_time/posix_time.hpp],
[boost::posix_time::ptime t;])
[boost::posix_time::ptime t;], [], [], [$2])
])# BOOST_DATE_TIME
# BOOST_FILESYSTEM([PREFERRED-RT-OPT])
# BOOST_EXCEPTION()
# ------------
# Look for Boost.Exception
BOOST_DEFUN([Exception],
[BOOST_FIND_HEADER([boost/exception/all.hpp])])
# BOOST_FILESYSTEM([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# ------------------------------------
# Look for Boost.Filesystem. For the documentation of PREFERRED-RT-OPT, see
# the documentation of BOOST_FIND_LIB above.
@@ -756,7 +892,7 @@ BOOST_DEFUN([Filesystem],
# added as of 1.35.0. If we have a version <1.35, we must not attempt to
# find Boost.System as it didn't exist by then.
if test $boost_major_version -ge 135; then
BOOST_SYSTEM([$1])
BOOST_SYSTEM([$1], [$2])
fi # end of the Boost.System check.
boost_filesystem_save_LIBS=$LIBS
boost_filesystem_save_LDFLAGS=$LDFLAGS
@@ -764,7 +900,8 @@ m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_SYSTEM_LIBS"
LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS"
BOOST_FIND_LIB([filesystem], [$1],
[boost/filesystem/path.hpp], [boost::filesystem::path p;])
[boost/filesystem/path.hpp], [boost::filesystem::path p;],
[], [], [$2])
if test $enable_static_boost = yes && test $boost_major_version -ge 135; then
BOOST_FILESYSTEM_LIBS="$BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS"
fi
@@ -809,6 +946,13 @@ BOOST_DEFUN([Function],
[BOOST_FIND_HEADER([boost/function.hpp])])
# BOOST_FUSION()
# -----------------
# Look for Boost.Fusion
BOOST_DEFUN([Fusion],
[BOOST_FIND_HEADER([boost/fusion/sequence.hpp])])
# BOOST_GEOMETRY()
# ----------------
# Look for Boost.Geometry (new since 1.47.0).
@@ -817,7 +961,7 @@ BOOST_DEFUN([Geometry],
])# BOOST_GEOMETRY
# BOOST_GRAPH([PREFERRED-RT-OPT])
# BOOST_GRAPH([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -------------------------------
# Look for Boost.Graphs. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
@@ -826,29 +970,19 @@ BOOST_DEFUN([Graph],
boost_graph_save_LDFLAGS=$LDFLAGS
# Link-time dependency from graph to regex was added as of 1.40.0.
if test $boost_major_version -ge 140; then
BOOST_REGEX([$1])
BOOST_REGEX([$1], [$2])
m4_pattern_allow([^BOOST_REGEX_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_REGEX_LIBS"
LDFLAGS="$LDFLAGS $BOOST_REGEX_LDFLAGS"
fi
BOOST_FIND_LIB([graph], [$1],
[boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;])
[boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;],
[], [], [$2])
LIBS=$boost_graph_save_LIBS
LDFLAGS=$boost_graph_save_LDFLAGS
])# BOOST_GRAPH
# BOOST_IOSTREAMS([PREFERRED-RT-OPT])
# -----------------------------------
# Look for Boost.IOStreams. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([IOStreams],
[BOOST_FIND_LIB([iostreams], [$1],
[boost/iostreams/device/file_descriptor.hpp],
[boost::iostreams::file_descriptor fd; fd.close();])
])# BOOST_IOSTREAMS
# BOOST_HASH()
# ------------
# Look for Boost.Functional/Hash
@@ -856,6 +990,25 @@ BOOST_DEFUN([Hash],
[BOOST_FIND_HEADER([boost/functional/hash.hpp])])
# BOOST_IOSTREAMS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -----------------------------------
# Look for Boost.IOStreams. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([IOStreams],
[BOOST_FIND_LIB([iostreams], [$1],
[boost/iostreams/device/file_descriptor.hpp],
[boost::iostreams::file_descriptor fd; fd.close();],
[], [], [$2])
])# BOOST_IOSTREAMS
# BOOST_ITERATOR()
# ------------
# Look for Boost.Iterator
BOOST_DEFUN([Iterator],
[BOOST_FIND_HEADER([boost/iterator/iterator_adaptor.hpp])])
# BOOST_LAMBDA()
# --------------
# Look for Boost.Lambda
@@ -863,7 +1016,7 @@ BOOST_DEFUN([Lambda],
[BOOST_FIND_HEADER([boost/lambda/lambda.hpp])])
# BOOST_LOCALE()
# BOOST_LOCALE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# --------------
# Look for Boost.Locale
BOOST_DEFUN([Locale],
@@ -872,40 +1025,40 @@ boost_locale_save_LIBS=$LIBS
boost_locale_save_LDFLAGS=$LDFLAGS
# require SYSTEM for boost-1.50.0 and up
if test $boost_major_version -ge 150; then
BOOST_SYSTEM([$1])
BOOST_SYSTEM([$1], [$2])
m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_SYSTEM_LIBS"
LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS"
fi # end of the Boost.System check.
BOOST_FIND_LIB([locale], [$1],
[boost/locale.hpp],
[[boost::locale::generator gen; std::locale::global(gen(""));]])
[[boost::locale::generator gen; std::locale::global(gen(""));]], [], [], [$2])
LIBS=$boost_locale_save_LIBS
LDFLAGS=$boost_locale_save_LDFLAGS
])# BOOST_LOCALE
# BOOST_LOG([PREFERRED-RT-OPT])
# BOOST_LOG([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -----------------------------
# Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Log],
[boost_log_save_LIBS=$LIBS
boost_log_save_LDFLAGS=$LDFLAGS
BOOST_SYSTEM([$1])
BOOST_FILESYSTEM([$1])
BOOST_DATE_TIME([$1])
BOOST_SYSTEM([$1], [$2])
BOOST_FILESYSTEM([$1], [$2])
BOOST_DATE_TIME([$1], [$2])
m4_pattern_allow([^BOOST_(SYSTEM|FILESYSTEM|DATE_TIME)_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_DATE_TIME_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS"
LDFLAGS="$LDFLAGS $BOOST_DATE_TIME_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS $BOOST_SYSTEM_LDFLAGS"
BOOST_FIND_LIB([log], [$1],
[boost/log/core/core.hpp],
[boost::log::attribute a; a.get_value();])
[boost::log::attribute a; a.get_value();], [], [], [$2])
LIBS=$boost_log_save_LIBS
LDFLAGS=$boost_log_save_LDFLAGS
])# BOOST_LOG
# BOOST_LOG_SETUP([PREFERRED-RT-OPT])
# BOOST_LOG_SETUP([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -----------------------------------
# Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
@@ -918,7 +1071,7 @@ LIBS="$LIBS $BOOST_LOG_LIBS"
LDFLAGS="$LDFLAGS $BOOST_LOG_LDFLAGS"
BOOST_FIND_LIB([log_setup], [$1],
[boost/log/utility/setup/from_settings.hpp],
[boost::log::basic_settings<char> bs; bs.empty();])
[boost::log::basic_settings<char> bs; bs.empty();], [], [], [$2])
LIBS=$boost_log_setup_save_LIBS
LDFLAGS=$boost_log_setup_save_LDFLAGS
])# BOOST_LOG_SETUP
@@ -936,7 +1089,7 @@ BOOST_DEFUN([Math],
[BOOST_FIND_HEADER([boost/math/special_functions.hpp])])
# BOOST_MPI([PREFERRED-RT-OPT])
# BOOST_MPI([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -------------------------------
# Look for Boost MPI. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. Uses MPICXX variable if it is
@@ -953,12 +1106,20 @@ BOOST_FIND_LIB([mpi], [$1],
[boost/mpi.hpp],
[int argc = 0;
char **argv = 0;
boost::mpi::environment env(argc,argv);])
boost::mpi::environment env(argc,argv);],
[], [], [$2])
CXX=${boost_save_CXX}
CXXCPP=${boost_save_CXXCPP}
])# BOOST_MPI
# BOOST_MPL()
# ------------------
# Look for Boost.MPL
BOOST_DEFUN([MPL],
[BOOST_FIND_HEADER([boost/mpl/for_each.hpp])])
# BOOST_MULTIARRAY()
# ------------------
# Look for Boost.MultiArray
@@ -966,6 +1127,13 @@ BOOST_DEFUN([MultiArray],
[BOOST_FIND_HEADER([boost/multi_array.hpp])])
# BOOST_MULTIINDEXCCONTAINER()
# ------------------
# Look for Boost.MultiIndexContainer
BOOST_DEFUN([MultiIndexContainer],
[BOOST_FIND_HEADER([boost/multi_index_container.hpp])])
# BOOST_NUMERIC_UBLAS()
# --------------------------
# Look for Boost.NumericUblas (Basic Linear Algebra)
@@ -996,6 +1164,25 @@ BOOST_DEFUN([Preprocessor],
[BOOST_FIND_HEADER([boost/preprocessor/repeat.hpp])])
# BOOST_PROPERTY_TREE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -----------------------------------------
# Look for Boost.Property_Tree. For the documentation of PREFERRED-RT-OPT,
# see the documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Property_Tree],
[BOOST_FIND_LIB([property_tree], [$1],
[boost/property_tree/ptree.hpp],
[boost::property_tree::ptree pt; boost::property_tree::read_xml d("test", pt);],
[], [], [$2])
])# BOOST_PROPERTY_TREE
# BOOST_RANDOM()
# --------------------
# Look for Boost.Random
BOOST_DEFUN([Random],
[BOOST_FIND_HEADER([boost/random/random_number_generator.hpp])])
# BOOST_RANGE()
# --------------------
# Look for Boost.Range
@@ -1016,14 +1203,15 @@ BOOST_DEFUN([Uuid],
[BOOST_FIND_HEADER([boost/uuid/uuid.hpp])])
# BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT])
# BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -----------------------------------------
# Look for Boost.Program_options. For the documentation of PREFERRED-RT-OPT,
# see the documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Program_Options],
[BOOST_FIND_LIB([program_options], [$1],
[boost/program_options.hpp],
[boost::program_options::options_description d("test");])
[boost::program_options::options_description d("test");],
[], [], [$2])
])# BOOST_PROGRAM_OPTIONS
@@ -1039,7 +1227,7 @@ boost_python_save_$1=$$1
$1="$$1 $BOOST_PYTHON_$1"])
# BOOST_PYTHON([PREFERRED-RT-OPT])
# BOOST_PYTHON([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# --------------------------------
# Look for Boost.Python. For the documentation of PREFERRED-RT-OPT,
# see the documentation of BOOST_FIND_LIB above.
@@ -1050,7 +1238,7 @@ _BOOST_PYTHON_CONFIG([LIBS], [libs])
m4_pattern_allow([^BOOST_PYTHON_MODULE$])dnl
BOOST_FIND_LIBS([python], [python python3], [$1],
[boost/python.hpp],
[], [BOOST_PYTHON_MODULE(empty) {}])
[], [BOOST_PYTHON_MODULE(empty) {}], [], [$2])
CPPFLAGS=$boost_python_save_CPPFLAGS
LDFLAGS=$boost_python_save_LDFLAGS
LIBS=$boost_python_save_LIBS
@@ -1064,18 +1252,26 @@ BOOST_DEFUN([Ref],
[BOOST_FIND_HEADER([boost/ref.hpp])])
# BOOST_REGEX([PREFERRED-RT-OPT])
# BOOST_REGEX([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -------------------------------
# Look for Boost.Regex. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Regex],
[BOOST_FIND_LIB([regex], [$1],
[boost/regex.hpp],
[boost::regex exp("*"); boost::regex_match("foo", exp);])
[boost::regex exp("*"); boost::regex_match("foo", exp);],
[], [], [$2])
])# BOOST_REGEX
# BOOST_SERIALIZATION([PREFERRED-RT-OPT])
# BOOST_SCOPE_EXIT()
# ------------
# Look for Boost.ScopeExit.
BOOST_DEFUN([SCOPE_EXIT],
[BOOST_FIND_HEADER([boost/scope_exit.hpp])])
# BOOST_SERIALIZATION([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# ---------------------------------------
# Look for Boost.Serialization. For the documentation of PREFERRED-RT-OPT, see
# the documentation of BOOST_FIND_LIB above.
@@ -1083,18 +1279,20 @@ BOOST_DEFUN([Serialization],
[BOOST_FIND_LIB([serialization], [$1],
[boost/archive/text_oarchive.hpp],
[std::ostream* o = 0; // Cheap way to get an ostream...
boost::archive::text_oarchive t(*o);])
boost::archive::text_oarchive t(*o);],
[], [], [$2])
])# BOOST_SERIALIZATION
# BOOST_SIGNALS([PREFERRED-RT-OPT])
# BOOST_SIGNALS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# ---------------------------------
# Look for Boost.Signals. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Signals],
[BOOST_FIND_LIB([signals], [$1],
[boost/signal.hpp],
[boost::signal<void ()> s;])
[boost::signal<void ()> s;],
[], [], [$2])
])# BOOST_SIGNALS
@@ -1130,19 +1328,24 @@ BOOST_DEFUN([String_Algo],
])
# BOOST_SYSTEM([PREFERRED-RT-OPT])
# BOOST_SYSTEM([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# --------------------------------
# Look for Boost.System. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. This library was introduced in Boost
# 1.35.0.
# 1.35.0 and is header only since 1.70.
BOOST_DEFUN([System],
[BOOST_FIND_LIB([system], [$1],
[
if test $boost_major_version -ge 170; then
BOOST_FIND_HEADER([boost/system/error_code.hpp])
else
BOOST_FIND_LIB([system], [$1],
[boost/system/error_code.hpp],
[boost::system::error_code e; e.clear();])
[boost::system::error_code e; e.clear();], [], [], [$2])
fi
])# BOOST_SYSTEM
# BOOST_TEST([PREFERRED-RT-OPT])
# BOOST_TEST([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# ------------------------------
# Look for Boost.Test. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
@@ -1152,11 +1355,11 @@ BOOST_FIND_LIB([unit_test_framework], [$1],
[boost/test/unit_test.hpp], [BOOST_CHECK(2 == 2);],
[using boost::unit_test::test_suite;
test_suite* init_unit_test_suite(int argc, char ** argv)
{ return NULL; }])
{ return NULL; }], [], [$2])
])# BOOST_TEST
# BOOST_THREAD([PREFERRED-RT-OPT])
# BOOST_THREAD([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# ---------------------------------
# Look for Boost.Thread. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above.
@@ -1170,7 +1373,7 @@ boost_thread_save_LDFLAGS=$LDFLAGS
boost_thread_save_CPPFLAGS=$CPPFLAGS
# Link-time dependency from thread to system was added as of 1.49.0.
if test $boost_major_version -ge 149; then
BOOST_SYSTEM([$1])
BOOST_SYSTEM([$1], [$2])
fi # end of the Boost.System check.
m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag"
@@ -1189,7 +1392,7 @@ if test $boost_major_version -lt 148; then
fi
BOOST_FIND_LIBS([thread], [thread$boost_thread_lib_ext],
[$1],
[boost/thread.hpp], [boost::thread t; boost::mutex m;])
[boost/thread.hpp], [boost::thread t; boost::mutex m;], [], [], [$2])
case $host_os in
(*mingw*) boost_thread_w32_socket_link=-lws2_32;;
@@ -1265,7 +1468,7 @@ BOOST_FIND_HEADER([boost/ptr_container/ptr_map.hpp])
])# BOOST_POINTER_CONTAINER
# BOOST_WAVE([PREFERRED-RT-OPT])
# BOOST_WAVE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# ------------------------------
# NOTE: If you intend to use Wave/Spirit with thread support, make sure you
# call BOOST_THREAD first.
@@ -1283,7 +1486,7 @@ LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS \
$BOOST_DATE_TIME_LDFLAGS $BOOST_THREAD_LDFLAGS"
BOOST_FIND_LIB([wave], [$1],
[boost/wave.hpp],
[boost::wave::token_id id; get_token_name(id);])
[boost::wave::token_id id; get_token_name(id);], [], [], [$2])
LIBS=$boost_wave_save_LIBS
LDFLAGS=$boost_wave_save_LDFLAGS
])# BOOST_WAVE
@@ -1351,10 +1554,11 @@ AC_CACHE_CHECK([for the flags needed to use pthreads], [boost_cv_pthread_flag],
-pthreads -mthreads -lpthread --thread-safe -mt";;
esac
# Generate the test file.
AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <pthread.h>],
[pthread_t th; pthread_join(th, 0);
pthread_attr_init(0); pthread_cleanup_push(0, 0);
pthread_create(0,0,0,0); pthread_cleanup_pop(0);])])
AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <pthread.h>
void *f(void*){ return 0; }],
[pthread_t th; pthread_create(&th,0,f,0); pthread_join(th,0);
pthread_attr_t attr; pthread_attr_init(&attr); pthread_cleanup_push(0, 0);
pthread_cleanup_pop(0);])])
for boost_pthread_flag in '' $boost_pthread_flags; do
boost_pthread_ok=false
dnl Re-use the test file already generated.
@@ -1416,12 +1620,77 @@ if test x$boost_cv_inc_path != xno; then
# I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines
# the same defines as GCC's).
for i in \
"defined __clang__ && __clang_major__ == 14 && __clang_minor__ == 0 @ clang140" \
"defined __clang__ && __clang_major__ == 13 && __clang_minor__ == 0 @ clang130" \
"defined __clang__ && __clang_major__ == 12 && __clang_minor__ == 0 @ clang120" \
"defined __clang__ && __clang_major__ == 11 && __clang_minor__ == 1 @ clang111" \
"defined __clang__ && __clang_major__ == 11 && __clang_minor__ == 0 @ clang110" \
"defined __clang__ && __clang_major__ == 10 && __clang_minor__ == 0 @ clang100" \
"defined __clang__ && __clang_major__ == 9 && __clang_minor__ == 0 @ clang90" \
"defined __clang__ && __clang_major__ == 8 && __clang_minor__ == 0 @ clang80" \
"defined __clang__ && __clang_major__ == 7 && __clang_minor__ == 0 @ clang70" \
"defined __clang__ && __clang_major__ == 6 && __clang_minor__ == 0 @ clang60" \
"defined __clang__ && __clang_major__ == 5 && __clang_minor__ == 0 @ clang50" \
"defined __clang__ && __clang_major__ == 4 && __clang_minor__ == 0 @ clang40" \
"defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 9 @ clang39" \
"defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 8 @ clang38" \
"defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 7 @ clang37" \
_BOOST_mingw_test(11, 2) \
_BOOST_gcc_test(11, 2) \
_BOOST_mingw_test(11, 1) \
_BOOST_gcc_test(11, 1) \
_BOOST_mingw_test(10, 3) \
_BOOST_gcc_test(10, 3) \
_BOOST_mingw_test(10, 2) \
_BOOST_gcc_test(10, 2) \
_BOOST_mingw_test(10, 1) \
_BOOST_gcc_test(10, 1) \
_BOOST_mingw_test(9, 3) \
_BOOST_gcc_test(9, 3) \
_BOOST_mingw_test(9, 2) \
_BOOST_gcc_test(9, 2) \
_BOOST_mingw_test(9, 1) \
_BOOST_gcc_test(9, 1) \
_BOOST_mingw_test(9, 0) \
_BOOST_gcc_test(9, 0) \
_BOOST_mingw_test(8, 5) \
_BOOST_gcc_test(8, 5) \
_BOOST_mingw_test(8, 4) \
_BOOST_gcc_test(8, 4) \
_BOOST_mingw_test(8, 3) \
_BOOST_gcc_test(8, 3) \
_BOOST_mingw_test(8, 2) \
_BOOST_gcc_test(8, 2) \
_BOOST_mingw_test(8, 1) \
_BOOST_gcc_test(8, 1) \
_BOOST_mingw_test(8, 0) \
_BOOST_gcc_test(8, 0) \
_BOOST_mingw_test(7, 4) \
_BOOST_gcc_test(7, 4) \
_BOOST_mingw_test(7, 3) \
_BOOST_gcc_test(7, 3) \
_BOOST_mingw_test(7, 2) \
_BOOST_gcc_test(7, 2) \
_BOOST_mingw_test(7, 1) \
_BOOST_gcc_test(7, 1) \
_BOOST_mingw_test(7, 0) \
_BOOST_gcc_test(7, 0) \
_BOOST_mingw_test(6, 5) \
_BOOST_gcc_test(6, 5) \
_BOOST_mingw_test(6, 4) \
_BOOST_gcc_test(6, 4) \
_BOOST_mingw_test(6, 3) \
_BOOST_gcc_test(6, 3) \
_BOOST_mingw_test(6, 2) \
_BOOST_gcc_test(6, 2) \
_BOOST_mingw_test(6, 1) \
_BOOST_gcc_test(6, 1) \
_BOOST_mingw_test(6, 0) \
_BOOST_gcc_test(6, 0) \
_BOOST_mingw_test(5, 5) \
_BOOST_gcc_test(5, 5) \
_BOOST_mingw_test(5, 4) \
_BOOST_gcc_test(5, 4) \
_BOOST_mingw_test(5, 3) \
_BOOST_gcc_test(5, 3) \
_BOOST_mingw_test(5, 2) \

View File

@@ -20,22 +20,16 @@
#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/process/child.hpp>
#include <boost/process/io.hpp>
#include <boost/process/pipe.hpp>
#include <boost/process/search_path.hpp>
#include <boost/property_tree/exceptions.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ptree_fwd.hpp>
#include <algorithm>
#include <iostream>
#include <mpd/tag.h>
#include <sys/wait.h>
#include "actions.h"
#include "charset.h"
@@ -133,18 +127,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;
@@ -230,8 +212,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)
@@ -1769,33 +1749,46 @@ 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();
}
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<unsigned>(what[1]);
auto secs = fromString<unsigned>(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<unsigned>(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<unsigned>(what[1]);
boundsCheck(percent, 0u, 100u);
int secs = (percent * s.getDuration()) / 100.0;
Mpd.Seek(s.getPosition(), secs);
}
// 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<unsigned>(what[1]);
auto mins = fromString<unsigned>(what[2]);
auto secs = fromString<unsigned>(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]:[mm]:[ss], [m]:[ss], [s]s, [%]%, [%] accepted)");
}
bool SelectItem::canBeRun()
@@ -2008,6 +2001,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);
}
@@ -2763,13 +2759,11 @@ bool AddYoutubeDLItem::canBeRun()
void AddYoutubeDLItem::run()
{
using Global::wFooter;
namespace bp = boost::process;
namespace pt = boost::property_tree;
std::string url;
{
Statusbar::ScopedLock slock;
Statusbar::put() << "Add via youtube-dl: ";
Statusbar::put() << "Add via youtube-dl/yt-dlp: ";
url = wFooter->prompt();
}
@@ -2777,74 +2771,192 @@ void AddYoutubeDLItem::run()
if (url.empty())
return;
// search the youtube-dl executable in the PATH
auto ydl_path = bp::search_path("youtube-dl");
if (ydl_path.empty()) {
Statusbar::print("youtube-dl was not found in PATH");
// Try to find yt-dlp first, fall back to youtube-dl
std::string ydl_executable;
{
FILE *which_pipe = popen("which yt-dlp 2>/dev/null", "r");
if (which_pipe)
{
char path[256];
if (fgets(path, sizeof(path), which_pipe) != nullptr)
{
// Remove trailing newline
size_t len = strlen(path);
if (len > 0 && path[len-1] == '\n')
path[len-1] = '\0';
ydl_executable = path;
}
pclose(which_pipe);
}
}
// Fall back to youtube-dl if yt-dlp not found
if (ydl_executable.empty())
{
FILE *which_pipe = popen("which youtube-dl 2>/dev/null", "r");
if (which_pipe)
{
char path[256];
if (fgets(path, sizeof(path), which_pipe) != nullptr)
{
size_t len = strlen(path);
if (len > 0 && path[len-1] == '\n')
path[len-1] = '\0';
ydl_executable = path;
}
pclose(which_pipe);
}
}
if (ydl_executable.empty())
{
Statusbar::print("Neither yt-dlp nor youtube-dl was found in PATH");
return;
}
Statusbar::printf("Calling youtube-dl with '%1%' ...", url);
Statusbar::printf("Fetching from '%s'...", url.c_str());
// start youtube-dl in a child process
// -j: output as JSON, each playlist item on a separate line
// -f bestaudio/best: selects the best available audio-only stream, or
// alternatively the best audio+video stream
bp::ipstream output;
bp::child child_process(ydl_path, url, "-j", "-f", "bestaudio/best", "--playlist-end", "100, bp::std_out > output,
bp::std_err > bp::null);
// Build command with proper shell escaping
std::string escaped_url = url;
// Simple shell escape - replace single quotes with '\''
size_t pos = 0;
while ((pos = escaped_url.find("'", pos)) != std::string::npos)
{
escaped_url.replace(pos, 1, "'\\''");
pos += 4;
}
// extract the URL and metadata from a ptree object and add
auto add_song = [] (const pt::ptree& ptree) {
auto download_url = ptree.get<std::string>("url");
auto title = ptree.get_optional<std::string>("title");
auto artist = ptree.get_optional<std::string>("creator");
if (!artist.has_value()) {
artist = ptree.get_optional<std::string>("uploader");
}
auto album = ptree.get_optional<std::string>("album");
auto id = Mpd.AddSong(download_url);
if (id == -1) {
return 0;
}
if (title.has_value()) {
Mpd.AddTag(id, MPD_TAG_TITLE, *title);
}
if (artist.has_value()) {
Mpd.AddTag(id, MPD_TAG_ARTIST, *artist);
}
if (album.has_value()) {
Mpd.AddTag(id, MPD_TAG_ALBUM, *album);
}
return 1;
};
// Use --flat-playlist to get all items, then process each one
// First get list of video IDs/URLs from playlist
std::string command = ydl_executable + " --flat-playlist --get-id " +
"--playlist-end 100 " +
"'" + escaped_url + "' 2>/dev/null";
FILE *pipe = popen(command.c_str(), "r");
if (!pipe)
{
Statusbar::print("Failed to execute downloader");
return;
}
std::vector<std::string> video_ids;
char line[4096];
// Collect all video IDs
while (fgets(line, sizeof(line), pipe) != nullptr)
{
size_t len = strlen(line);
while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r'))
{
line[--len] = '\0';
}
if (len > 0)
video_ids.push_back(line);
}
pclose(pipe);
if (video_ids.empty())
{
Statusbar::print("No items found in playlist");
return;
}
Statusbar::printf("Found %u items, fetching URLs...",
static_cast<unsigned>(video_ids.size()));
std::string line;
pt::ptree ptree;
unsigned num_songs_added = 0;
while (std::getline(output, line)) {
try {
std::istringstream line_stream(line);
pt::read_json(line_stream, ptree);
num_songs_added += add_song(ptree);
} catch (pt::ptree_error &e) {
Statusbar::print("An error occurred while parsing the output of youtube-dl");
// Now process each video to get its title and stream URL
for (const auto &video_id : video_ids)
{
// Build URL for this specific video
std::string video_url;
if (video_id.find("http") == 0)
{
video_url = video_id;
}
else
{
// Assume it's a YouTube ID
video_url = "https://www.youtube.com/watch?v=" + video_id;
}
// Escape the video URL
std::string escaped_video_url = video_url;
pos = 0;
while ((pos = escaped_video_url.find("'", pos)) != std::string::npos)
{
escaped_video_url.replace(pos, 1, "'\\''");
pos += 4;
}
// Get title and URL for this specific video
std::string video_command = ydl_executable + " --get-title --get-url " +
"-f bestaudio/best " +
"'" + escaped_video_url + "' 2>/dev/null";
FILE *video_pipe = popen(video_command.c_str(), "r");
if (!video_pipe)
continue;
}
Statusbar::printf("Added %1% item(s) to playlist", num_songs_added);
std::string title;
std::string stream_url;
int line_num = 0;
while (fgets(line, sizeof(line), video_pipe) != nullptr)
{
size_t len = strlen(line);
while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r'))
{
line[--len] = '\0';
}
if (child_process.running()) {
child_process.terminate();
}
child_process.wait();
if (len == 0)
continue;
auto ec = child_process.exit_code();
if (ec == 0) {
Statusbar::printf("Added %1% item(s) to playlist", num_songs_added);
} else {
Statusbar::printf("Added %1% item(s) to playlist (youtube-dl exited with exit code %2%)", num_songs_added, ec);
if (line_num == 0)
title = line;
else if (line_num == 1)
stream_url = line;
++line_num;
}
pclose(video_pipe);
// Add to MPD if we got both title and URL
if (!stream_url.empty() &&
(stream_url.find("http://") == 0 || stream_url.find("https://") == 0))
{
int id = Mpd.AddSong(stream_url);
if (id != -1)
{
if (!title.empty())
Mpd.AddTag(id, MPD_TAG_TITLE, title);
++num_songs_added;
std::string display = title.empty() ? video_id : title;
if (display.length() > 50)
display = display.substr(0, 47) + "...";
Statusbar::printf("Added: %s (%u/%u)",
display.c_str(),
num_songs_added,
static_cast<unsigned>(video_ids.size()));
}
}
}
if (num_songs_added > 0)
{
Statusbar::printf("Successfully added %u item(s) to playlist", num_songs_added);
}
else
{
Statusbar::print("Failed to add any items");
}
}

View File

@@ -156,14 +156,10 @@ bool configure(int argc, char **argv)
if (vm.count("test-lyrics-fetchers"))
{
std::vector<std::tuple<std::string, std::string, std::string>> 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"),
};
@@ -175,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

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

View File

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

View File

@@ -24,6 +24,7 @@
#include <cstdlib>
#include <iostream>
#include <sys/select.h>
#include <termios.h>
#include <unistd.h>
#include "utility/readline.h"
@@ -198,6 +199,8 @@ int add_base()
int color_pair_counter;
std::vector<int> color_pair_map;
termios orig_termios;
}
namespace NC {
@@ -346,6 +349,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;
@@ -396,6 +403,7 @@ int colorCount()
void initScreen(bool enable_colors, bool enable_mouse)
{
tcgetattr(STDIN_FILENO, &orig_termios);
initscr();
if (has_colors() && enable_colors)
{
@@ -477,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,
@@ -494,14 +503,9 @@ 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)
|| m_width+m_start_x > size_t(COLS)
|| m_height+m_start_y > size_t(LINES))
throw std::logic_error("constructed window doesn't fit into the terminal");
if (m_border)
{
++m_start_x;
@@ -542,6 +546,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 +571,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 +597,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;
}
@@ -682,13 +689,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<T>::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;
}
@@ -777,6 +787,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;
@@ -1410,6 +1425,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;
}

View File

@@ -26,7 +26,6 @@
#include "config.h"
#include "curses.h"
#include "gcc.h"
#include <boost/optional.hpp>
#include <functional>
@@ -200,7 +199,8 @@ enum class Format {
Bold, NoBold,
Underline, NoUnderline,
Reverse, NoReverse,
AltCharset, NoAltCharset
AltCharset, NoAltCharset,
Italic, NoItalic
};
NC::Format reverseFormat(NC::Format fmt);
@@ -532,6 +532,11 @@ private:
///
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 +567,7 @@ private:
int m_underline_counter;
int m_reverse_counter;
int m_alt_charset_counter;
int m_italic_counter;
};
}

View File

@@ -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':
@@ -92,9 +94,9 @@ void setProperties(NC::Menu<T> &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();
}
}
}

View File

@@ -192,6 +192,8 @@ expressions<CharT> parseBracket(const string<CharT> &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<CharT> parseBracket(const string<CharT> &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));
}

View File

@@ -1,29 +0,0 @@
/***************************************************************************
* Copyright (C) 2008-2021 by Andrzej Rybczak *
* andrzej@rybczak.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#if defined(__GNUC__) && __GNUC__ >= 3
# define GNUC_NORETURN __attribute__((noreturn))
# define GNUC_UNUSED __attribute__((unused))
# define GNUC_PRINTF(a, b) __attribute__((format(printf, a, b)))
#else
# define GNUC_NORETURN
# define GNUC_UNUSED
# define GNUC_PRINTF(a, b)
#endif

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

@@ -25,7 +25,6 @@
#include <boost/tuple/tuple.hpp>
#include <string>
#include "enums.h"
#include "gcc.h"
#include "screens/screen.h"
#include "song.h"

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"
@@ -38,16 +44,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
{
std::string s;
is >> s;
if (s == "azlyrics")
fetcher = std::make_unique<AzLyricsFetcher>();
else if (s == "genius")
if (s == "genius")
fetcher = std::make_unique<GeniusFetcher>();
else if (s == "musixmatch")
fetcher = std::make_unique<MusixmatchFetcher>();
else if (s == "sing365")
fetcher = std::make_unique<Sing365Fetcher>();
else if (s == "metrolyrics")
fetcher = std::make_unique<MetrolyricsFetcher>();
else if (s == "justsomelyrics")
fetcher = std::make_unique<JustSomeLyricsFetcher>();
else if (s == "jahlyrics")
@@ -60,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;
@@ -68,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;
@@ -88,9 +91,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;
@@ -137,12 +142,14 @@ 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);
std::unique(lines.begin(), lines.end(), [](std::string &a, std::string &b) {
return a.empty() && b.empty();
});
auto last = std::unique(
lines.begin(),
lines.end(),
[](std::string &a, std::string &b) { return a.empty() && b.empty(); });
lines.erase(last, lines.end());
data = boost::algorithm::join(lines, "\n");
boost::trim(data);
}
@@ -150,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;
@@ -192,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)
@@ -202,18 +210,11 @@ 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)
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: ";
@@ -226,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; }
@@ -69,34 +71,6 @@ private:
const char *URL;
};
struct MusixmatchFetcher : public GoogleLyricsFetcher
{
virtual const char *name() const override { return "musixmatch.com"; }
protected:
virtual const char *regex() const override { return "<span class=\"lyrics__content__.*?>(.*?)</span>"; }
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 "<div class=\"lyrics-body\">(.*?)<!--WIDGET.*?<!-- Second Section -->(.*?)<!--WIDGET.*?<!-- Third Section -->(.*?)</div>"; }
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 "<div class=\"lyrics\">(.*?)</div>"; }
};
struct JustSomeLyricsFetcher : public GoogleLyricsFetcher
{
virtual const char *name() const override { return "justsomelyrics.com"; }
@@ -105,20 +79,12 @@ protected:
virtual const char *regex() const override { return "<div class=\"content.*?</div>(.*?)See also"; }
};
struct AzLyricsFetcher : public GoogleLyricsFetcher
{
virtual const char *name() const override { return "azlyrics.com"; }
protected:
virtual const char *regex() const override { return "<div class=\"lyricsh\">.*?</h2>.*<div>(.*?)</div>"; }
};
struct GeniusFetcher : public GoogleLyricsFetcher
{
virtual const char *name() const override { return "genius.com"; }
protected:
virtual const char *regex() const override { return "<div class=\"[Ll]yrics.*?>(.*?)</div>"; }
virtual const char *regex() const override { return "<div data-lyrics-container.*?>(.*?)</div>"; }
};
struct JahLyricsFetcher : public GoogleLyricsFetcher
@@ -142,7 +108,7 @@ struct TekstowoFetcher : public GoogleLyricsFetcher
virtual const char *name() const override { return "tekstowo.pl"; }
protected:
virtual const char *regex() const override { return "<div class=\"song-text\".*?>.*?</h2>(.*?)<a"; }
virtual const char *regex() const override { return "<div class=\"inner-text\">(.*?)</div>"; }
};
struct ZeneszovegFetcher : public GoogleLyricsFetcher
@@ -150,13 +116,13 @@ struct ZeneszovegFetcher : public GoogleLyricsFetcher
virtual const char *name() const override { return "zeneszoveg.hu"; }
protected:
virtual const char *regex() const override { return "<div class=\"lyrics-plain-text.*?\">(.*?)</div>"; }
virtual const char *regex() const override { return "<div class=\"lyrics-plain-text trans_original\">(.*?)</div>"; }
};
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; }
@@ -168,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

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

View File

@@ -353,8 +353,14 @@ private:
};
template <typename ObjectT>
struct Iterator: std::iterator<std::input_iterator_tag, ObjectT>
struct Iterator
{
using iterator_category = std::input_iterator_tag;
using value_type = ObjectT;
using difference_type = std::ptrdiff_t;
using pointer = ObjectT *;
using reference = ObjectT &;
// shared state of the iterator
struct State
{

View File

@@ -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());
@@ -220,7 +219,10 @@ int main(int argc, char **argv)
try
{
auto k = Bindings.get(input);
std::any_of(k.first, k.second, std::bind(&Binding::execute, ph::_1));
[[maybe_unused]] bool executed = std::any_of(
k.first,
k.second,
std::bind(&Binding::execute, ph::_1));
}
catch (ConversionError &e)
{

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

View File

@@ -98,7 +98,7 @@ bool MoveToAlbum(NC::Menu<AlbumEntry> &albums, const std::string &primary_tag, c
struct SortSongs {
typedef NC::Menu<MPD::Song>::Item SongItem;
static const std::array<MPD::Song::GetFunction, 3> GetFuns;
static const std::array<MPD::Song::GetFunction, 4> GetFuns;
LocaleStringComparison m_cmp;
@@ -117,26 +117,16 @@ public:
return ret < 0;
}
// Sort by track numbers.
try {
ret = boost::lexical_cast<int>(a.getTags(&MPD::Song::getTrackNumber))
- boost::lexical_cast<int>(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<char>(Config.song_library_format, &a)
< Format::stringify<char>(Config.song_library_format, &b);
}
};
const std::array<MPD::Song::GetFunction, 3> SortSongs::GetFuns = {{
const std::array<MPD::Song::GetFunction, 4> SortSongs::GetFuns = {{
&MPD::Song::getDate,
&MPD::Song::getAlbum,
&MPD::Song::getDisc
&MPD::Song::getDisc,
&MPD::Song::getTrackNumber,
}};
class SortAlbumEntries {
@@ -191,11 +181,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<PrimaryTag>(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::Border());
setHighlightFixes(Tags);
@@ -240,18 +235,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;
}
@@ -334,9 +336,9 @@ void MediaLibrary::update()
for (const auto &album : albums)
{
auto entry = AlbumEntry(
Album(std::move(std::get<0>(album.first)),
std::move(std::get<1>(album.first)),
std::move(std::get<2>(album.first)),
Album(std::get<0>(album.first),
std::get<1>(album.first),
std::get<2>(album.first),
album.second));
if (idx < Albums.size())
Albums[idx].value() = std::move(entry);
@@ -433,8 +435,8 @@ void MediaLibrary::update()
{
auto entry = AlbumEntry(
Album(primary_tag,
std::move(std::get<0>(album.first)),
std::move(std::get<1>(album.first)),
std::get<0>(album.first),
std::get<1>(album.first),
album.second));
if (idx < Albums.size())
{

View File

@@ -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<MPD::Playlist>(0, MainStartY, LeftColumnWidth, MainHeight, Config.titles_visibility ? "Playlists" : "", Config.main_color, NC::Border());
setHighlightFixes(Playlists);
Playlists.cyclicScrolling(Config.use_cyclic_scrolling);
@@ -108,8 +112,11 @@ 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;
@@ -162,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);

View File

@@ -797,6 +797,8 @@ void TagEditor::runAction()
bool success = 1;
Statusbar::print("Writing changes...");
for (auto it = EditedSongs.begin(); it != EditedSongs.end(); ++it)
{
if ((*it)->isModified())
{
Statusbar::printf("Writing tags in \"%1%\"...", (*it)->getName());
if (!Tags::write(**it))
@@ -807,6 +809,7 @@ void TagEditor::runAction()
break;
}
}
}
if (success)
{
Statusbar::print("Tags updated");

View File

@@ -83,7 +83,8 @@ Visualizer::Visualizer()
HZ_MIN(Config.visualizer_spectrum_hz_min),
HZ_MAX(Config.visualizer_spectrum_hz_max),
GAIN(Config.visualizer_spectrum_gain),
SMOOTH_CHARS(ToWString("▁▂▃▄▅▆▇█"))
SMOOTH_CHARS(ToWString("▁▂▃▄▅▆▇█")),
SMOOTH_CHARS_FLIPPED(ToWString("▔🮂🮃🮄🬎🮅🮆█")) // https://unicode.org/charts/PDF/U1FB00.pdf
#endif
{
InitDataSource();
@@ -95,7 +96,7 @@ Visualizer::Visualizer()
memset(m_fftw_input, 0, sizeof(double)*DFT_TOTAL_SIZE);
m_fftw_output = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex)*m_fftw_results));
m_fftw_plan = fftw_plan_dft_r2c_1d(DFT_TOTAL_SIZE, m_fftw_input, m_fftw_output, FFTW_ESTIMATE);
m_dft_logspace.reserve(500);
m_dft_freqspace.reserve(500);
m_bar_heights.reserve(100);
# endif // HAVE_FFTW3_H
}
@@ -107,7 +108,7 @@ void Visualizer::switchTo()
m_reset_output = true;
drawHeader();
# ifdef HAVE_FFTW3_H
GenLogspace();
GenFreqSpace();
m_bar_heights.reserve(w.getWidth());
# endif // HAVE_FFTW3_H
}
@@ -121,7 +122,7 @@ void Visualizer::resize()
hasToBeResized = 0;
InitVisualization();
# ifdef HAVE_FFTW3_H
GenLogspace();
GenFreqSpace();
m_bar_heights.reserve(w.getWidth());
# endif // HAVE_FFTW3_H
}
@@ -420,7 +421,7 @@ void Visualizer::DrawSoundEllipseStereo(const int16_t *buf_left, const int16_t *
auto c = toColor(sqrt(x*x + 4*y*y), radius, true);
w << NC::XY(left_half_width + x, top_half_height + y)
<< c
<< Config.visualizer_chars[1]
<< Config.visualizer_chars[0]
<< NC::FormattedColor::End<>(c);
}
}
@@ -449,7 +450,7 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
const size_t win_width = w.getWidth();
size_t cur_bin = 0;
while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_logspace[0])
while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_freqspace[0])
++cur_bin;
for (size_t x = 0; x < win_width; ++x)
{
@@ -458,10 +459,10 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
// accumulate bins
size_t count = 0;
// check right bound
while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_logspace[x])
while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_freqspace[x])
{
// check left bound if not first index
if (x == 0 || Bin2Hz(cur_bin) >= m_dft_logspace[x-1])
if (x == 0 || Bin2Hz(cur_bin) >= m_dft_freqspace[x-1])
{
bar_height += m_freq_magnitudes[cur_bin];
++count;
@@ -475,8 +476,19 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
// average bins
bar_height /= count;
// log scale bar heights
// apply scaling to bar heights
if (Config.visualizer_spectrum_log_scale_y) {
bar_height = (20 * log10(bar_height) + DYNAMIC_RANGE + GAIN) / DYNAMIC_RANGE;
} else {
// apply gain
bar_height *= pow(10, 1.8 + GAIN / 20);
// buff higher frequencies
bar_height *= log2(2 + x) * 80.0/win_width;
// moderately normalize the heights
bar_height = pow(bar_height, 0.65);
//bar_height = pow(10, 1 + GAIN / 20) * bar_height;
}
// Scale bar height between 0 and height
bar_height = bar_height > 0 ? bar_height * height : 0;
bar_height = bar_height > height ? height : bar_height;
@@ -498,7 +510,12 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
++h_idx;
} else {
// data point does not exist, need to interpolate
h = Interpolate(x, h_idx);
if (Config.visualizer_spectrum_log_scale_x) {
h = InterpolateCubic(x, h_idx);
} else {
h = std::min(InterpolateLinear(x, h_idx), height / 1.0);
//h = 0;
}
}
for (size_t j = 0; j < h; ++j)
@@ -518,8 +535,12 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
} else {
// fractional height
if (flipped) {
if (Config.visualizer_spectrum_smooth_look_legacy_chars) {
ch = SMOOTH_CHARS_FLIPPED[idx];
} else {
ch = SMOOTH_CHARS[size-idx-2];
color = NC::FormattedColor(color.color(), {NC::Format::Reverse});
}
} else {
ch = SMOOTH_CHARS[idx];
}
@@ -544,7 +565,7 @@ void Visualizer::DrawFrequencySpectrumStereo(const int16_t *buf_left, const int1
DrawFrequencySpectrum(buf_right, samples, height, w.getHeight() - height);
}
double Visualizer::Interpolate(size_t x, size_t h_idx)
double Visualizer::InterpolateCubic(size_t x, size_t h_idx)
{
const double x_next = m_bar_heights[h_idx].first;
const double h_next = m_bar_heights[h_idx].second;
@@ -589,6 +610,33 @@ double Visualizer::Interpolate(size_t x, size_t h_idx)
return h_next;
}
double Visualizer::InterpolateLinear(size_t x, size_t h_idx)
{
const double x_next = m_bar_heights[h_idx].first;
const double h_next = m_bar_heights[h_idx].second;
double dh = 0;
if (h_idx == 0) {
// no data points on the left, linear extrapolation
if (h_idx < m_bar_heights.size()) {
const double x_next2 = m_bar_heights[h_idx+1].first;
const double h_next2 = m_bar_heights[h_idx+1].second;
dh = (h_next2 - h_next) / (x_next2 - x_next);
}
return h_next - dh * (x_next - x);
} else if (h_idx < m_bar_heights.size()) {
// simple linear interpolation
const double x_prev = m_bar_heights[h_idx-1].first;
const double h_prev = m_bar_heights[h_idx-1].second;
const double m = (h_next - h_prev) / (x_next - x_prev);
return h_prev + m * (x - x_prev);
}
// no data points on the right: don't interpolate
return h_next;
}
void Visualizer::ApplyWindow(double *output, const int16_t *input, ssize_t samples)
{
// Use Blackman window for low sidelobes and fast sidelobe rolloff
@@ -617,10 +665,36 @@ void Visualizer::GenLogspace()
const size_t win_width = w.getWidth();
const size_t left_bins = (log10(HZ_MIN) - win_width*log10(HZ_MIN)) / (log10(HZ_MIN) - log10(HZ_MAX));
// Generate logspaced frequencies
m_dft_logspace.resize(win_width);
const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_logspace.size() - 1);
for (size_t i = left_bins; i < m_dft_logspace.size() + left_bins; ++i) {
m_dft_logspace[i - left_bins] = pow(10, i * log_scale);
m_dft_freqspace.resize(win_width);
const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_freqspace.size() - 1);
for (size_t i = left_bins; i < m_dft_freqspace.size() + left_bins; ++i) {
m_dft_freqspace[i - left_bins] = pow(10, i * log_scale);
}
}
// Generate vector of linearly-spaced frequencies from HZ_MIN to HZ_MAX
void Visualizer::GenLinspace()
{
// Calculate number of extra bins needed between 0 HZ and HZ_MIN
const size_t win_width = w.getWidth();
const size_t left_bins = (HZ_MIN - win_width * HZ_MIN) / (HZ_MIN - HZ_MAX);
// Generate linspaced frequencies
m_dft_freqspace.resize(win_width);
const double lin_scale = HZ_MAX / (left_bins + m_dft_freqspace.size() - 1);
for (size_t i = left_bins; i < m_dft_freqspace.size() + left_bins; ++i) {
m_dft_freqspace[i - left_bins] = i * lin_scale;
}
}
// Generate vector of spectrum frequencies from HZ_MIN to HZ_MAX
// Frequencies are (not) log-scaled depending on
// Config.visualizer_spectrum_log_scale_x
void Visualizer::GenFreqSpace()
{
if (Config.visualizer_spectrum_log_scale_x) {
GenLogspace();
} else {
GenLinspace();
}
}
#endif // HAVE_FFTW3_H

View File

@@ -76,8 +76,11 @@ private:
void DrawFrequencySpectrumStereo(const int16_t *, const int16_t *, ssize_t, size_t);
void ApplyWindow(double *, const int16_t *, ssize_t);
void GenLogspace();
void GenLinspace();
void GenFreqSpace();
double Bin2Hz(size_t);
double Interpolate(size_t, size_t);
double InterpolateCubic(size_t, size_t);
double InterpolateLinear(size_t, size_t);
# endif // HAVE_FFTW3_H
void InitDataSource();
@@ -113,7 +116,8 @@ private:
const double HZ_MAX;
const double GAIN;
const std::wstring SMOOTH_CHARS;
std::vector<double> m_dft_logspace;
const std::wstring SMOOTH_CHARS_FLIPPED;
std::vector<double> m_dft_freqspace;
std::vector<std::pair<size_t, double>> m_bar_heights;
std::vector<double> m_freq_magnitudes;

View File

@@ -212,28 +212,6 @@ void deprecated(const char *option, const char *version_removal,
bool Configuration::read(const std::vector<std::string> &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<void>("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);
@@ -270,11 +248,12 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
p.add("visualizer_fps", &visualizer_fps,
"60", [](std::string v) {
uint32_t result = verbose_lexical_cast<uint32_t>(v);
boundsCheck<uint32_t>(result, 30, 144);
boundsCheck<uint32_t>(result, 30, 1000);
return result;
});
p.add("visualizer_autoscale", &visualizer_autoscale, "no", yes_no);
p.add("visualizer_spectrum_smooth_look", &visualizer_spectrum_smooth_look, "yes", yes_no);
p.add("visualizer_spectrum_smooth_look_legacy_chars", &visualizer_spectrum_smooth_look_legacy_chars, "yes", yes_no);
p.add("visualizer_spectrum_dft_size", &visualizer_spectrum_dft_size,
"2", [](std::string v) {
auto result = verbose_lexical_cast<size_t>(v);
@@ -299,6 +278,8 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
lowerBoundCheck<double>(result, Config.visualizer_spectrum_hz_min+1);
return result;
});
p.add("visualizer_spectrum_log_scale_x", &visualizer_spectrum_log_scale_x, "yes", yes_no);
p.add("visualizer_spectrum_log_scale_y", &visualizer_spectrum_log_scale_y, "yes", yes_no);
p.add("visualizer_color", &visualizer_colors,
"blue, cyan, green, yellow, magenta, red", list_of<NC::FormattedColor>);
p.add("system_encoding", &system_encoding, "", [](std::string encoding) {
@@ -480,9 +461,27 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
p.add("titles_visibility", &titles_visibility, "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("lyrics_fetchers", &lyrics_fetchers,
"azlyrics, genius, musixmatch, sing365, metrolyrics, justsomelyrics, jahlyrics, plyrics, tekstowo, zeneszoveg, internet",
list_of<LyricsFetcher_>);
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;
try {
fetcher = boost::lexical_cast<LyricsFetcher_>(s);
} catch (boost::bad_lexical_cast &) {
std::clog << "Unknown lyrics fetcher: " << s << "\n";
}
return fetcher;
});
auto last = std::remove_if(
lyrics_fetchers.begin(), lyrics_fetchers.end(),
[](const auto &f) { return f.get() == nullptr; });
lyrics_fetchers.erase(last, lyrics_fetchers.end());
if (lyrics_fetchers.empty())
invalid_value(v);
});
p.add("follow_now_playing_lyrics", &now_playing_lyrics, "no", yes_no);
p.add("fetch_lyrics_for_current_song_in_background", &fetch_lyrics_in_background,
"no", yes_no);
@@ -534,6 +533,12 @@ bool Configuration::read(const std::vector<std::string> &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 +554,7 @@ bool Configuration::read(const std::vector<std::string> &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")

View File

@@ -86,13 +86,20 @@ struct Configuration
size_t visualizer_fps;
bool visualizer_autoscale;
bool visualizer_spectrum_smooth_look;
bool visualizer_spectrum_smooth_look_legacy_chars;
uint32_t visualizer_spectrum_dft_size;
double visualizer_spectrum_gain;
double visualizer_spectrum_hz_min;
double visualizer_spectrum_hz_max;
bool visualizer_spectrum_log_scale_x;
bool visualizer_spectrum_log_scale_y;
std::string pattern;
std::vector<size_t> playlist_editor_column_width_ratio;
std::vector<size_t> media_library_column_width_ratio_two;
std::vector<size_t> media_library_column_width_ratio_three;
std::vector<Column> columns;
DisplayMode playlist_display_mode;

View File

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

View File

@@ -24,7 +24,6 @@
#include <boost/format.hpp>
#include "curses/window.h"
#include "settings.h"
#include "gcc.h"
#include "interfaces.h"
namespace Progressbar {

View File

@@ -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");
}
@@ -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<TagLib::uint>(s.getDate()));
tag->setYear(boost::lexical_cast<unsigned>(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<TagLib::uint>(s.getTrack()));
tag->setTrack(boost::lexical_cast<unsigned>(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";
}
@@ -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));
}
@@ -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());
};
@@ -261,7 +262,7 @@ void read(mpd_song *s)
if (f.isNull())
return;
setAttribute(s, "Time", boost::lexical_cast<std::string>(f.audioProperties()->length()));
setAttribute(s, "Time", boost::lexical_cast<std::string>(f.audioProperties()->lengthInSeconds()));
if (auto mpeg_file = dynamic_cast<TagLib::MPEG::File *>(f.file()))
{
@@ -301,15 +302,9 @@ bool write(MPD::MutableSong &s)
if (f.isNull())
return false;
bool saved = false;
if (auto mpeg_file = dynamic_cast<TagLib::MPEG::File *>(f.file()))
{
writeID3v2Tags(s, mpeg_file->ID3v2Tag(true));
// write id3v2.4 tags only
if (!mpeg_file->save(TagLib::MPEG::File::ID3v2, true, 4, false))
return false;
// do not call generic save() as it will duplicate tags
saved = true;
}
else if (auto vorbis_file = dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file()))
{
@@ -326,7 +321,7 @@ bool write(MPD::MutableSong &s)
else
writeCommonTags(s, f.tag());
if (!saved && !f.save())
if (!f.save())
return false;
// TODO: move this somewhere else

View File

@@ -34,10 +34,23 @@ 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 ( 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;
if (m_ignore_the)
{

View File

@@ -26,7 +26,6 @@
#include <boost/type_traits/is_unsigned.hpp>
#include "config.h"
#include "gcc.h"
struct ConversionError
{
@@ -44,21 +43,21 @@ struct OutOfBounds : std::exception
const std::string &errorMessage() { return m_error_message; }
template <typename Type>
GNUC_NORETURN static void raise(const Type &value, const Type &lbound, const Type &ubound)
[[noreturn]] static void raise(const Type &value, const Type &lbound, const Type &ubound)
{
throw OutOfBounds((boost::format(
"value is out of bounds ([%1%, %2%] expected, %3% given)") % lbound % ubound % value).str());
}
template <typename Type>
GNUC_NORETURN static void raiseLower(const Type &value, const Type &lbound)
[[noreturn]] static void raiseLower(const Type &value, const Type &lbound)
{
throw OutOfBounds((boost::format(
"value is out of bounds ([%1%, ->) expected, %2% given)") % lbound % value).str());
}
template <typename Type>
GNUC_NORETURN static void raiseUpper(const Type &value, const Type &ubound)
[[noreturn]] static void raiseUpper(const Type &value, const Type &ubound)
{
throw OutOfBounds((boost::format(
"value is out of bounds ((<-, %1%] expected, %2% given)") % ubound % value).str());

View File

@@ -20,16 +20,29 @@
#include <algorithm>
#include <boost/algorithm/string/replace.hpp>
#include <cstdlib>
#include "utility/html.h"
std::string unescapeHtmlUtf8(const std::string &data)
{
int base;
size_t offset;
std::string result;
for (size_t i = 0, j; i < data.length(); ++i)
{
if (data[i] == '&' && data[i+1] == '#' && (j = data.find(';', i)) != std::string::npos)
{
int n = atoi(&data.c_str()[i+2]);
if (data[i+2] == 'x')
{
offset = 3;
base = 16;
}
else
{
offset = 2;
base = 10;
}
int n = strtol(&data.c_str()[i+offset], nullptr, base);
if (n >= 0x800)
{
result += (0xe0 | ((n >> 12) & 0x0f));

View File

@@ -45,6 +45,20 @@ bool yes_no(const std::string &v)
invalid_value(v);
}
std::vector<size_t> parse_ratio(const std::string &v, const std::vector<size_t>::size_type length)
{
std::vector<size_t> ret = list_of<size_t>(v, verbose_lexical_cast<size_t>, 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)

View File

@@ -56,17 +56,26 @@ DestT verbose_lexical_cast(const std::string &v)
}
template <typename ValueT, typename ConvertT>
std::vector<ValueT> list_of(const std::string &v, ConvertT convert)
std::vector<ValueT> list_of(const std::string &v, ConvertT convert, const typename std::vector<ValueT>::size_type length, const std::string &escape, const std::string &sep, const std::string &quote)
{
std::vector<ValueT> result;
boost::tokenizer<boost::escaped_list_separator<char>> elems(v);
boost::escaped_list_separator<char> esq(escape, sep, quote);
boost::tokenizer<boost::escaped_list_separator<char>> 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 <typename ValueT, typename ConvertT>
std::vector<ValueT> list_of(const std::string &v, ConvertT convert)
{
return list_of<ValueT>(v, convert, 0, "\\", ",", "\"");
}
template <typename ValueT>
std::vector<ValueT> list_of(const std::string &v)
{
@@ -75,6 +84,8 @@ std::vector<ValueT> list_of(const std::string &v)
bool yes_no(const std::string &v);
std::vector<size_t> parse_ratio(const std::string &v, const std::vector<size_t>::size_type length);
////////////////////////////////////////////////////////////////////////////////
class option_parser

View File

@@ -25,7 +25,6 @@
#include <locale>
#include <string>
#include <vector>
#include "gcc.h"
template <size_t N> size_t const_strlen(const char (&)[N]) {
return N-1;

View File

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