Compare commits

...

56 Commits

Author SHA1 Message Date
Max Kellermann
5761800197 release v0.19.9 2015-02-06 17:08:25 +01:00
Max Kellermann
0eebacc521 Merge tag 'v0.18.23' into v0.19.x 2015-02-06 17:07:47 +01:00
Max Kellermann
4a5528697d release v0.18.23 2015-02-06 17:04:14 +01:00
Max Kellermann
d38034bb5c fs/io/FileOutputStream: don't auto-delete file on WIN32
The file handle is never reset to INVALID_HANDLE_VALUE, and thus the
destructor will assume the operation shall be cancelled and will
delete the temporary file.

This was a major breakage for saving the database file and the state
file.
2015-02-06 14:36:55 +01:00
Max Kellermann
b3fe3e8b3d TagBuilder: allow adding duplicate tag types in Complement()
Build a table of pre-existing tag types before adding new items.  The
old way would check HasType() each time, which would return true after
the first instance of that tag type had been added, preventing
duplicate tag types to be merged.

This broke duplicate tag types loaded from the state file, because
this code path uses TagBuilder::Complement().
2015-02-06 12:25:34 +01:00
Max Kellermann
5489dec28d NEWS: fix v0.18.22 release date 2015-02-01 12:22:24 +01:00
Max Kellermann
8a6b4db19f Makefile.am: move org_musicpd_Bridge.h to BUILT_SOURCES 2015-01-31 00:05:30 +01:00
Max Kellermann
df43b6a05c Makefile.am: generate icon before compiling JNI classes
Apparently, Android's build.xml requires the icon to be available,
even when only running the Java compiler.
2015-01-31 00:05:30 +01:00
Max Kellermann
3adca3c2fa db/update/Walk: use std::unique_ptr instead of std::auto_ptr
std::auto_ptr is deprecated, and std::unique_ptr is much better
anyway.
2015-01-29 08:37:23 +01:00
PHO
39abd3ecb4 Avoid integer overflow in MonotonicClock{S,MS,US}
This is Darwin specific: the previous implementation was causing an integer
overflow when base.numer is very large. On PPC Darwin, the timebase info is 1000000000/33330116 and this is too large for integer arithmetic.
2015-01-29 08:34:37 +01:00
PHO
a4f4fc50b9 Avoid integer overflow in MonotonicClock{S,MS,US}
This is Darwin specific: the previous implementation was causing an integer
overflow when base.numer is very large. On PPC Darwin, the timebase info is 1000000000/33330116 and this is too large for integer arithmetic.
2015-01-29 08:33:48 +01:00
Max Kellermann
7bf638b0de decoder/DsdLib: use new[] to allocate the ID3 buffer
Don't abort the process if there's not enough memory.  This buffer is
not important and can be large.
2015-01-29 08:24:34 +01:00
Max Kellermann
56662a703c decoder/DsdLib: free ID3 buffer right after id3_tag_parse()
Merge two free() calls.
2015-01-29 08:24:34 +01:00
Max Kellermann
8b5f47d3a3 decoder/DsdLib: raise ID3 tag limit to 1 MB
A bug report was submitted with a 600 kB ID3 tag that could not be
read by MPD.
2015-01-29 08:20:14 +01:00
Max Kellermann
a289dcb9ee Merge branch 'v0.18.x' into v0.19.x 2015-01-26 20:48:19 +01:00
PHO
023b9c1e7e Test the existence of strndup(3) before using it.
This can eliminate the ad-hoc "#ifdef WIN32" and can also support other platforms lacking it as well (including Darwin 9).
2015-01-26 20:39:49 +01:00
Max Kellermann
4c61662644 test/read_mixer: add missing stdlib.h include 2015-01-26 20:39:49 +01:00
Max Kellermann
ad1b6ef0ac {playlist,input}/despotify: remove defunct plugin 2015-01-26 09:55:31 +01:00
Max Kellermann
ed5c6be2f1 util/list: disable gcc5 warning
This file has been removed in newer MPD versions, so don't care about
it now.
2015-01-23 16:50:31 +01:00
Max Kellermann
30cb082932 ClientProcess: cast enum to int before passing to printf()
Fixes gcc5 warning.
2015-01-23 16:50:31 +01:00
Max Kellermann
645554d12f configure.ac: prepare for 0.18.23 2015-01-23 16:47:13 +01:00
Max Kellermann
212b0faf0c android/build.py: use os.path.abspath() to build mpd_path
Fixes a bug that occurs when runing "build.py" from inside the
"android" directory.
2015-01-22 18:51:53 +01:00
Max Kellermann
276a0d9500 thread/Name: include stdio.h if HAVE_PRCTL
Caused a build failure with uClibc because snprintf() was not
available.
2015-01-21 20:40:56 +01:00
Max Kellermann
384b6c8288 doc/protocol: "playlistsearch" is case insensitive 2015-01-16 17:27:19 +01:00
Max Kellermann
a2af158fd3 configure.ac: prepare for 0.19.9 2015-01-16 17:26:50 +01:00
Max Kellermann
f33d2fb2e7 release v0.19.8 2015-01-14 23:12:24 +01:00
Max Kellermann
a9eec35aff Merge tag 'v0.18.22' into v0.19.x 2015-01-14 23:12:08 +01:00
Max Kellermann
8534f2d1e2 release v0.18.22 2015-01-14 23:04:49 +01:00
Max Kellermann
00740fb23b android/build.py: prepend "./" to "configure" if path is empty
Fixes in-tree build when the script is called as "android/build.py"
and not "./android/build.py".
2015-01-09 16:51:52 +01:00
Max Kellermann
37e9010887 input/async: reset the "open" flag after seeking successfully
Fixes a problem with the "curl" input plugin: IsEOF() always returns
true because the "open" flag was cleared by
CurlInputStream::RequestDone() when end-of-stream was reached.  This
flag stays false even when seeking to another position has succeeded.

This patch resets the "open" flag to true after seeking successfully.
2015-01-06 12:46:28 +01:00
Max Kellermann
4bd2c75056 thread/Name: disable pthread_setname_np() on NetBSD
NetBSD's pthread_setname_np() prototype is incompatible with the rest
of the world, and it requires to pass the string argument as a
non-const pointer.  Instead of working around this misdesign, I hereby
disable the feature on NetBSD.
2015-01-06 12:08:36 +01:00
Max Kellermann
b9ed850b98 thread/Name: enable FormatThreadName() with prctl()
Add macro HAVE_THREAD_NAME which is set when any method to set the
thread name is available.  Use that macro in FormatThreadName()
instead of just checking for HAVE_PTHREAD_SETNAME_NP.
2015-01-06 12:04:30 +01:00
Max Kellermann
11cea17496 thread/Name: indent preprocessor commands 2015-01-06 12:04:15 +01:00
Max Kellermann
163597ef69 db/simple: fix implicit nullptr/bool conversion
Return false on error, not nullptr.
2014-12-26 14:34:03 +01:00
Max Kellermann
95f84afd33 fs/Traits, ...: work around -Wtautological-pointer-compare
New in clang 3.6.
2014-12-26 14:34:03 +01:00
Max Kellermann
9f7fd1fbfb db/lazy, input/mms: add "override" keywords
Fixes -Winconsistent-missing-override (clang 3.6).
2014-12-26 14:29:29 +01:00
Max Kellermann
940cab8620 Merge branch 'v0.18.x' into v0.19.x 2014-12-26 14:28:52 +01:00
Max Kellermann
5b84c99d79 doc/user: remove autoconf/automake from Debian build dependencies 2014-12-26 14:00:50 +01:00
Max Kellermann
b295024574 doc/user: add more Debian build dependencies 2014-12-26 13:56:26 +01:00
Max Kellermann
34180f1745 doc/user: add libicu-dev to Debian build dependencies 2014-12-26 13:54:33 +01:00
Max Kellermann
665031467a db/proxy, output/shout: fix implicit nullptr/bool conversion
Return false on error, not nullptr.
2014-12-26 13:50:54 +01:00
Max Kellermann
df33171107 db/{simple,proxy}, ...: add "override" keywords
Fixes -Winconsistent-missing-override (clang 3.6).
2014-12-26 13:47:04 +01:00
Max Kellermann
53f4044890 util/{ASCII,UriUtil}, ...: work around -Wtautological-pointer-compare
New in clang 3.6.
2014-12-26 13:43:32 +01:00
Max Kellermann
a5049136ff DatabaseGlue: convert nullptr check to assertion 2014-12-26 13:43:32 +01:00
Max Kellermann
705b3c6b63 util/ASCII: fix indent 2014-12-26 13:37:38 +01:00
Max Kellermann
6b4ac66962 Compiler.h: add macro CLANG_CHECK_VERSION() 2014-12-26 13:31:03 +01:00
Max Kellermann
0964b06240 Compiler.h: add macro GCC_OLDER_THAN() 2014-12-26 13:30:44 +01:00
Max Kellermann
92eeca3ba7 util/Manual: reimplement GCC_CHECK_VERSION() using GCC_MAKE_VERSION() 2014-12-26 13:30:22 +01:00
Max Kellermann
2a86554ac4 Compiler.h: add macro GCC_MAKE_VERSION() 2014-12-26 13:30:11 +01:00
Max Kellermann
805caa30ce configure.ac: prepare for 0.18.22 2014-12-26 13:23:04 +01:00
Max Kellermann
a56949e9fa decoder/ffmpeg: support interleaved floating point 2014-12-23 20:51:08 +01:00
Max Kellermann
43da4c0eca input/mms: limit the mmsx_read() size 2014-12-23 20:34:45 +01:00
Max Kellermann
b9c7771830 decoder/DsdLib: add missing stdlib.h include 2014-12-23 10:08:46 +01:00
Jan Brittenson
35db88affe DSF ID3 tags hitting 4k size limit
Here's a change to dynamically allocate the DSD ID3 tag buffer.
Pretty much anything with cover art is going to exceed the existing,
static 4k limit...  Here's a change to dynamically allocate the buffer
and sanity check it at some upper limit.  I rather arbitrarily pulled
256k out of thin air just to keep a corrupt file from causing it to
trying to allocate a buffer larger than available memory.
2014-12-23 09:49:33 +01:00
Max Kellermann
e38faca455 configure.ac: prepare for 0.19.8 2014-12-23 09:48:31 +01:00
Max Kellermann
0255e8710c android: release v0.19.7 2014-12-23 09:42:52 +01:00
56 changed files with 271 additions and 939 deletions

View File

@@ -116,9 +116,6 @@ For WavPack playback.
libadplug - http://adplug.sourceforge.net/ libadplug - http://adplug.sourceforge.net/
For AdLib playback. For AdLib playback.
despotify - https://github.com/SimonKagstrom/despotify
For Spotify playback.
Optional Miscellaneous Dependencies Optional Miscellaneous Dependencies
----------------------------------- -----------------------------------

View File

@@ -28,8 +28,6 @@ noinst_LIBRARIES = \
libmixer_plugins.a \ libmixer_plugins.a \
liboutput_plugins.a liboutput_plugins.a
libmpd_a_DEPENDENCIES =
libmpd_a_CPPFLAGS = $(AM_CPPFLAGS) \ libmpd_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(LIBMPDCLIENT_CFLAGS) \ $(LIBMPDCLIENT_CFLAGS) \
$(AVAHI_CFLAGS) \ $(AVAHI_CFLAGS) \
@@ -283,13 +281,13 @@ android/build/build.xml: android/AndroidManifest.xml
ln -s $(abs_srcdir)/android/res/values android/build/res ln -s $(abs_srcdir)/android/res/values android/build/res
$(ANDROID_SDK)/tools/android update project --path android/build --target android-17 $(ANDROID_SDK)/tools/android update project --path android/build --target android-17
android/build/bin/classes/org/musicpd/Bridge.class: android/src/Bridge.java android/build/build.xml android/build/bin/classes/org/musicpd/Bridge.class: android/src/Bridge.java android/build/build.xml android/build/res/drawable/icon.png
cd android/build && ant compile-jni-classes cd android/build && ant compile-jni-classes
android/build/include/org_musicpd_Bridge.h: android/build/bin/classes/org/musicpd/Bridge.class android/build/include/org_musicpd_Bridge.h: android/build/bin/classes/org/musicpd/Bridge.class
javah -classpath $(ANDROID_SDK)/platforms/android-17/android.jar:android/build/bin/classes -d $(@D) org.musicpd.Bridge javah -classpath $(ANDROID_SDK)/platforms/android-17/android.jar:android/build/bin/classes -d $(@D) org.musicpd.Bridge
libmpd_a_DEPENDENCIES += android/build/include/org_musicpd_Bridge.h BUILT_SOURCES = android/build/include/org_musicpd_Bridge.h
android/build/libs/armeabi-v7a/libmpd.so: libmpd.so android/build/build.xml android/build/libs/armeabi-v7a/libmpd.so: libmpd.so android/build/build.xml
mkdir -p $(@D) mkdir -p $(@D)
@@ -1125,7 +1123,6 @@ libinput_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(NFS_CFLAGS) \ $(NFS_CFLAGS) \
$(CDIO_PARANOIA_CFLAGS) \ $(CDIO_PARANOIA_CFLAGS) \
$(FFMPEG_CFLAGS) \ $(FFMPEG_CFLAGS) \
$(DESPOTIFY_CFLAGS) \
$(MMS_CFLAGS) $(MMS_CFLAGS)
INPUT_LIBS = \ INPUT_LIBS = \
@@ -1135,7 +1132,6 @@ INPUT_LIBS = \
$(NFS_LIBS) \ $(NFS_LIBS) \
$(CDIO_PARANOIA_LIBS) \ $(CDIO_PARANOIA_LIBS) \
$(FFMPEG_LIBS2) \ $(FFMPEG_LIBS2) \
$(DESPOTIFY_LIBS) \
$(MMS_LIBS) $(MMS_LIBS)
if HAVE_ALSA if HAVE_ALSA
@@ -1181,15 +1177,6 @@ libinput_a_SOURCES += \
src/input/plugins/MmsInputPlugin.cxx src/input/plugins/MmsInputPlugin.hxx src/input/plugins/MmsInputPlugin.cxx src/input/plugins/MmsInputPlugin.hxx
endif endif
if ENABLE_DESPOTIFY
libinput_a_SOURCES += \
src/lib/despotify/DespotifyUtils.cxx \
src/lib/despotify/DespotifyUtils.hxx \
src/input/plugins/DespotifyInputPlugin.cxx \
src/input/plugins/DespotifyInputPlugin.hxx
endif
liboutput_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ liboutput_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(AO_CFLAGS) \ $(AO_CFLAGS) \
$(ALSA_CFLAGS) \ $(ALSA_CFLAGS) \
@@ -1394,14 +1381,6 @@ PLAYLIST_LIBS = \
$(EXPAT_LIBS) \ $(EXPAT_LIBS) \
$(FLAC_LIBS) $(FLAC_LIBS)
if ENABLE_DESPOTIFY
libplaylist_plugins_a_SOURCES += \
src/lib/despotify/DespotifyUtils.cxx \
src/lib/despotify/DespotifyUtils.hxx \
src/playlist/plugins/DespotifyPlaylistPlugin.cxx \
src/playlist/plugins/DespotifyPlaylistPlugin.hxx
endif
if ENABLE_SOUNDCLOUD if ENABLE_SOUNDCLOUD
libplaylist_plugins_a_SOURCES += \ libplaylist_plugins_a_SOURCES += \
src/playlist/plugins/SoundCloudPlaylistPlugin.cxx \ src/playlist/plugins/SoundCloudPlaylistPlugin.cxx \
@@ -1638,12 +1617,6 @@ if HAVE_LIBUPNP
test_run_neighbor_explorer_SOURCES += src/lib/expat/ExpatParser.cxx test_run_neighbor_explorer_SOURCES += src/lib/expat/ExpatParser.cxx
endif endif
if ENABLE_DESPOTIFY
test_run_neighbor_explorer_SOURCES += \
src/lib/despotify/DespotifyUtils.cxx \
src/lib/despotify/DespotifyUtils.hxx
endif
endif endif
if ENABLE_ARCHIVE if ENABLE_ARCHIVE

30
NEWS
View File

@@ -1,3 +1,25 @@
ver 0.19.9 (2015/02/06)
* decoder
- dsdiff, dsf: raise ID3 tag limit to 1 MB
* playlist: fix loading duplicate tag types from state file
* despotify: remove defunct plugin
* fix clock integer overflow on OS X
* fix gcc 5.0 warnings
* fix build failure with uClibc
* fix build failure on non-POSIX operating systems
* fix dependency issue on parallel Android build
* fix database/state file saving on Windows
ver 0.19.8 (2015/01/14)
* input
- curl: fix bug after rewinding from end-of-file
- mms: reduce delay at the beginning of playback
* decoder
- dsdiff, dsf: allow ID3 tags larger than 4 kB
- ffmpeg: support interleaved floating point
* fix clang 3.6 warnings
* fix build failure on NetBSD
ver 0.19.7 (2014/12/17) ver 0.19.7 (2014/12/17)
* input * input
- nfs: fix crash while canceling a failing file open operation - nfs: fix crash while canceling a failing file open operation
@@ -161,6 +183,14 @@ ver 0.19 (2014/10/10)
* install systemd unit for socket activation * install systemd unit for socket activation
* Android port * Android port
ver 0.18.23 (2015/02/06)
* despotify: remove defunct plugin
* fix clock integer overflow on OS X
* fix gcc 5.0 warnings
ver 0.18.22 (2015/01/14)
* fix clang 3.6 warnings
ver 0.18.21 (2014/12/17) ver 0.18.21 (2014/12/17)
* playlist * playlist
- embcue: fix filename suffix detection - embcue: fix filename suffix detection

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd" package="org.musicpd"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="10" android:versionCode="13"
android:versionName="0.19.6"> android:versionName="0.19.9">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/>

View File

@@ -23,7 +23,7 @@ if not os.path.isdir(ndk_path):
sys.exit(1) sys.exit(1)
# the path to the MPD sources # the path to the MPD sources
mpd_path = os.path.dirname(os.path.dirname(sys.argv[0])) mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
# output directories # output directories
lib_path = os.path.abspath('lib') lib_path = os.path.abspath('lib')

View File

@@ -1,10 +1,10 @@
AC_PREREQ(2.60) AC_PREREQ(2.60)
AC_INIT(mpd, 0.19.7, musicpd-dev-team@lists.sourceforge.net) AC_INIT(mpd, 0.19.9, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0 VERSION_MAJOR=0
VERSION_MINOR=19 VERSION_MINOR=19
VERSION_REVISION=7 VERSION_REVISION=9
VERSION_EXTRA=0 VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx]) AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -206,6 +206,7 @@ if test x$host_is_linux = xyes; then
fi fi
AC_CHECK_FUNCS(getpwnam_r getpwuid_r) AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
AC_CHECK_FUNCS(strndup)
if test x$host_is_linux = xyes; then if test x$host_is_linux = xyes; then
MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD) MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD)
@@ -445,11 +446,6 @@ MPD_DEPENDS([enable_jack], [enable_glib],
AC_SYS_LARGEFILE AC_SYS_LARGEFILE
AC_ARG_ENABLE(despotify,
AS_HELP_STRING([--enable-despotify],
[enable support for despotify (default: disable)]),,
[enable_despotify=no])
AC_ARG_ENABLE(soundcloud, AC_ARG_ENABLE(soundcloud,
AS_HELP_STRING([--enable-soundcloud], AS_HELP_STRING([--enable-soundcloud],
[enable support for soundcloud.com]),, [enable support for soundcloud.com]),,
@@ -979,14 +975,6 @@ if test x$enable_nfs = xyes; then
fi fi
AM_CONDITIONAL(ENABLE_NFS, test x$enable_nfs = xyes) AM_CONDITIONAL(ENABLE_NFS, test x$enable_nfs = xyes)
dnl --------------------------------- Despotify ---------------------------------
MPD_AUTO_PKG(despotify, DESPOTIFY, [despotify],
[Despotify support], [despotify not found])
if test x$enable_despotify = xyes; then
AC_DEFINE(ENABLE_DESPOTIFY, 1, [Define when despotify is enabled])
fi
AM_CONDITIONAL(ENABLE_DESPOTIFY, test x$enable_despotify = xyes)
dnl --------------------------------- Soundcloud ------------------------------ dnl --------------------------------- Soundcloud ------------------------------
if test x$enable_soundcloud != xno; then if test x$enable_soundcloud != xno; then
PKG_CHECK_MODULES([YAJL], [yajl >= 2.0], PKG_CHECK_MODULES([YAJL], [yajl >= 2.0],
@@ -1903,7 +1891,6 @@ printf '\nStreaming support:\n\t'
results(cdio_paranoia, [CDIO_PARANOIA]) results(cdio_paranoia, [CDIO_PARANOIA])
results(curl,[CURL]) results(curl,[CURL])
results(smbclient,[SMBCLIENT]) results(smbclient,[SMBCLIENT])
results(despotify,[Despotify])
results(soundcloud,[Soundcloud]) results(soundcloud,[Soundcloud])
printf '\n\t' printf '\n\t'
results(mms,[MMS]) results(mms,[MMS])

View File

@@ -195,16 +195,6 @@ of database.
Limit the depth of the directories being watched, 0 means only watch Limit the depth of the directories being watched, 0 means only watch
the music directory itself. There is no limit by default. the music directory itself. There is no limit by default.
.TP .TP
.B despotify_user <name>
This specifies the user to use when logging in to Spotify using the despotify plugins.
.TP
.B despotify_password <name>
This specifies the password to use when logging in to Spotify using the despotify plugins.
.TP
.B despotify_high_bitrate <yes or no>
This specifies if the requested bitrate for Spotify should be high or not. Higher sounds
better but requires more processing and higher bandwidth. Default is yes.
.TP
.SH REQUIRED AUDIO OUTPUT PARAMETERS .SH REQUIRED AUDIO OUTPUT PARAMETERS
.TP .TP
.B type <type> .B type <type>

View File

@@ -1141,7 +1141,7 @@ OK
</term> </term>
<listitem> <listitem>
<para> <para>
Searches case-sensitively for partial matches in the Searches case-insensitively for partial matches in the
current playlist. current playlist.
</para> </para>
</listitem> </listitem>

View File

@@ -89,7 +89,7 @@ cd mpd-version</programlisting>
</para> </para>
<programlisting> <programlisting>
apt-get install g++ automake autoconf \ apt-get install g++ \
libmad0-dev libmpg123-dev libid3tag0-dev \ libmad0-dev libmpg123-dev libid3tag0-dev \
libflac-dev libvorbis-dev libopus-dev \ libflac-dev libvorbis-dev libopus-dev \
libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \ libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \
@@ -98,19 +98,21 @@ apt-get install g++ automake autoconf \
libsidplay2-dev libsidutils-dev libresid-builder-dev \ libsidplay2-dev libsidutils-dev libresid-builder-dev \
libavcodec-dev libavformat-dev \ libavcodec-dev libavformat-dev \
libmp3lame-dev \ libmp3lame-dev \
libsamplerate0-dev \ libsamplerate0-dev libsoxr-dev \
libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \ libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \
libzzip-dev \ libzzip-dev \
libcurl4-gnutls-dev libyajl-dev \ libcurl4-gnutls-dev libyajl-dev libexpat-dev \
libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \ libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \
libpulse-dev libroar-dev libshout3-dev \ libpulse-dev libroar-dev libshout3-dev \
libmpdclient-dev \ libmpdclient-dev \
libnfs-dev libsmbclient-dev \
libupnp-dev \
libavahi-client-dev \ libavahi-client-dev \
libsqlite3-dev \ libsqlite3-dev \
libsystemd-daemon-dev libwrap0-dev \ libsystemd-daemon-dev libwrap0-dev \
libcppunit-dev xmlto \ libcppunit-dev xmlto \
libboost-dev \ libboost-dev \
libglib2.0-dev libglib2.0-dev libicu-dev
</programlisting> </programlisting>
<para> <para>
@@ -313,9 +315,8 @@ systemctl start mpd.socket</programlisting>
</para> </para>
<programlisting>input { <programlisting>input {
plugin "despotify" plugin "curl"
user "foo" proxy "proxy.local"
password "bar"
} }
</programlisting> </programlisting>
@@ -1738,66 +1739,6 @@ buffer_size: 16384</programlisting>
</informaltable> </informaltable>
</section> </section>
<section>
<title><varname>despotify</varname></title>
<para>
Plays <ulink url="http://www.spotify.com">Spotify</ulink> tracks using the despotify
library. The despotify plugin uses a <filename>spt://</filename> URI and a Spotify
URL. So for example, you can add a song with:
</para>
<para>
<filename>mpc add spt://spotify:track:5qENVY0YEdZ7fiuOax70x1</filename>
</para>
<para>
You need a Spotify premium account to use this plugin, and you need
to setup username and password in the configuration file. The
configuration settings are global since the despotify playlist plugin
use the same settings.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>despotify_user</varname>
</entry>
<entry>
Sets up the Spotify username (required)
</entry>
</row>
<row>
<entry>
<varname>despotify_password</varname>
</entry>
<entry>
Sets up the Spotify password (required)
</entry>
</row>
<row>
<entry>
<varname>despotify_high_bitrate</varname>
</entry>
<entry>
Set up if high bitrate should be used for Spotify tunes.
High bitrate sounds better but slow systems can have problems
with playback (default yes).
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section> <section>
<title><varname>file</varname></title> <title><varname>file</varname></title>
@@ -3284,70 +3225,6 @@ buffer_size: 16384</programlisting>
playlist files. playlist files.
</para> </para>
</section> </section>
<section>
<title><varname>despotify</varname></title>
<para>
Adds <ulink url="http://www.spotify.com/">Spotify</ulink>
playlists. Spotify playlists use the <filename>spt://</filename> URI,
and a Spotify playlist URL. So for example, you can load a playlist
with
</para>
<para>
<filename>mpc load spt://spotify:user:simon.kagstrom:playlist:3SUwkOe5VbVHysZcidEZtH</filename>
</para>
<para>
See the despotify input plugin for configuration options (username
and password needs to be setup)
</para>
</section>
<section>
<title><varname>soundcloud</varname></title>
<para>
Adds <ulink url="https://www.soundcloud.com/">Soundcloud</ulink>
playlists. SoundCloud playlists use the <filename>soundcloud://</filename> URI,
and with a number of arguments, you may load different playlists with
</para>
<programlisting>
mpc load soundcloud://track/TRACK_ID
mpc load soundcloud://playlist/PLAYLIST_ID
mpc load soundcloud://user/USERNAME
mpc load soundcloud://search/SEARCH_QUERY
mpc load soundcloud://url/https://soundcloud.com/ARTIST/TRACK-NAME
</programlisting>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>apikey</varname>
<parameter>client_id</parameter>
</entry>
<entry>
User apikey/client_id can override the
<application>MPD</application> token provided by
SoundCloud.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
</section> </section>
</chapter> </chapter>
</book> </book>

View File

@@ -20,33 +20,45 @@
#ifndef COMPILER_H #ifndef COMPILER_H
#define COMPILER_H #define COMPILER_H
#define GCC_CHECK_VERSION(major, minor) \ #define GCC_MAKE_VERSION(major, minor, patchlevel) ((major) * 10000 + (minor) * 100 + patchlevel)
(defined(__GNUC__) && \
(__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))))
#ifdef __GNUC__ #ifdef __GNUC__
#define GCC_VERSION (__GNUC__ * 10000 \ #define GCC_VERSION GCC_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#else #else
#define GCC_VERSION 0 #define GCC_VERSION 0
#endif #endif
#define GCC_CHECK_VERSION(major, minor) \
(defined(__GNUC__) && GCC_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
/**
* Are we building with gcc (not clang or any other compiler) and a
* version older than the specified one?
*/
#define GCC_OLDER_THAN(major, minor) \
(defined(__GNUC__) && !defined(__clang__) && \
GCC_VERSION < GCC_MAKE_VERSION(major, minor, 0))
#ifdef __clang__ #ifdef __clang__
# define CLANG_VERSION (__clang_major__ * 10000 \ # define CLANG_VERSION GCC_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+ __clang_minor__ * 100 \
+ __clang_patchlevel__)
# if __clang_major__ < 3 # if __clang_major__ < 3
# error Sorry, your clang version is too old. You need at least version 3.1. # error Sorry, your clang version is too old. You need at least version 3.1.
# endif # endif
#elif defined(__GNUC__) #elif defined(__GNUC__)
# if !GCC_CHECK_VERSION(4,6) # if GCC_OLDER_THAN(4,6)
# error Sorry, your gcc version is too old. You need at least version 4.6. # error Sorry, your gcc version is too old. You need at least version 4.6.
# endif # endif
#else #else
# warning Untested compiler. Use at your own risk! # warning Untested compiler. Use at your own risk!
#endif #endif
/**
* Are we building with the specified version of clang or newer?
*/
#define CLANG_CHECK_VERSION(major, minor) \
(defined(__clang__) && \
CLANG_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
#if GCC_CHECK_VERSION(4,0) #if GCC_CHECK_VERSION(4,0)
/* GCC 4.x */ /* GCC 4.x */
@@ -141,7 +153,7 @@
#if defined(__cplusplus) #if defined(__cplusplus)
/* support for C++11 "override" was added in gcc 4.7 */ /* support for C++11 "override" was added in gcc 4.7 */
#if !defined(__clang__) && !GCC_CHECK_VERSION(4,7) #if GCC_OLDER_THAN(4,7)
#define override #define override
#define final #define final
#endif #endif

View File

@@ -76,7 +76,10 @@ idle_get_names(void)
unsigned unsigned
idle_parse_name(const char *name) idle_parse_name(const char *name)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(name != nullptr); assert(name != nullptr);
#endif
for (unsigned i = 0; idle_names[i] != nullptr; ++i) for (unsigned i = 0; idle_names[i] != nullptr; ++i)
if (StringEqualsCaseASCII(name, idle_names[i])) if (StringEqualsCaseASCII(name, idle_names[i]))

View File

@@ -77,7 +77,10 @@ SongFilter::Item::Item(unsigned _tag, time_t _time)
bool bool
SongFilter::Item::StringMatch(const char *s) const SongFilter::Item::StringMatch(const char *s) const
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(s != nullptr); assert(s != nullptr);
#endif
if (fold_case) { if (fold_case) {
const std::string folded = IcuCaseFold(s); const std::string folded = IcuCaseFold(s);

View File

@@ -77,7 +77,10 @@ SongLoader::LoadFile(const char *path_utf8, Error &error) const
DetachedSong * DetachedSong *
SongLoader::LoadSong(const char *uri_utf8, Error &error) const SongLoader::LoadSong(const char *uri_utf8, Error &error) const
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(uri_utf8 != nullptr); assert(uri_utf8 != nullptr);
#endif
if (memcmp(uri_utf8, "file:///", 8) == 0) if (memcmp(uri_utf8, "file:///", 8) == 0)
/* absolute path */ /* absolute path */

View File

@@ -41,7 +41,7 @@ client_process_command_list(Client &client, bool list_ok,
FormatDebug(client_domain, "process command \"%s\"", cmd); FormatDebug(client_domain, "process command \"%s\"", cmd);
ret = command_process(client, num++, cmd); ret = command_process(client, num++, cmd);
FormatDebug(client_domain, "command returned %i", ret); FormatDebug(client_domain, "command returned %i", int(ret));
if (ret != CommandResult::OK || client.IsExpired()) if (ret != CommandResult::OK || client.IsExpired())
break; break;
else if (list_ok) else if (list_ok)
@@ -90,7 +90,7 @@ client_process_line(Client &client, char *line)
std::move(cmd_list)); std::move(cmd_list));
FormatDebug(client_domain, FormatDebug(client_domain,
"[%u] process command " "[%u] process command "
"list returned %i", client.num, ret); "list returned %i", client.num, int(ret));
if (ret == CommandResult::CLOSE || if (ret == CommandResult::CLOSE ||
client.IsExpired()) client.IsExpired())
@@ -126,7 +126,7 @@ client_process_line(Client &client, char *line)
ret = command_process(client, 0, line); ret = command_process(client, 0, line);
FormatDebug(client_domain, FormatDebug(client_domain,
"[%u] command returned %i", "[%u] command returned %i",
client.num, ret); client.num, int(ret));
if (ret == CommandResult::CLOSE || if (ret == CommandResult::CLOSE ||
client.IsExpired()) client.IsExpired())

View File

@@ -43,7 +43,7 @@ public:
virtual const LightSong *GetSong(const char *uri_utf8, virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override; Error &error) const override;
virtual void ReturnSong(const LightSong *song) const; void ReturnSong(const LightSong *song) const override;
virtual bool Visit(const DatabaseSelection &selection, virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory, VisitDirectory visit_directory,

View File

@@ -103,7 +103,7 @@ public:
virtual void Close() override; virtual void Close() override;
virtual const LightSong *GetSong(const char *uri_utf8, virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override; Error &error) const override;
virtual void ReturnSong(const LightSong *song) const; void ReturnSong(const LightSong *song) const override;
virtual bool Visit(const DatabaseSelection &selection, virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory, VisitDirectory visit_directory,
@@ -731,7 +731,7 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
{ {
// TODO: eliminate the const_cast // TODO: eliminate the const_cast
if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error)) if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error))
return nullptr; return false;
if (!visit_directory && !visit_playlist && selection.recursive && if (!visit_directory && !visit_playlist && selection.recursive &&
(ServerSupportsSearchBase(connection) (ServerSupportsSearchBase(connection)
@@ -757,7 +757,7 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
{ {
// TODO: eliminate the const_cast // TODO: eliminate the const_cast
if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error)) if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error))
return nullptr; return false;
enum mpd_tag_type tag_type2 = Convert(tag_type); enum mpd_tag_type tag_type2 = Convert(tag_type);
if (tag_type2 == MPD_TAG_COUNT) { if (tag_type2 == MPD_TAG_COUNT) {
@@ -810,7 +810,7 @@ ProxyDatabase::GetStats(const DatabaseSelection &selection,
// TODO: eliminate the const_cast // TODO: eliminate the const_cast
if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error)) if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error))
return nullptr; return false;
struct mpd_stats *stats2 = struct mpd_stats *stats2 =
mpd_run_stats(connection); mpd_run_stats(connection);

View File

@@ -435,9 +435,12 @@ SimpleDatabase::Save(Error &error)
bool bool
SimpleDatabase::Mount(const char *uri, Database *db, Error &error) SimpleDatabase::Mount(const char *uri, Database *db, Error &error)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(uri != nullptr); assert(uri != nullptr);
assert(*uri != 0);
assert(db != nullptr); assert(db != nullptr);
#endif
assert(*uri != 0);
ScopeDatabaseLock protect; ScopeDatabaseLock protect;
@@ -445,13 +448,13 @@ SimpleDatabase::Mount(const char *uri, Database *db, Error &error)
if (r.uri == nullptr) { if (r.uri == nullptr) {
error.Format(db_domain, DB_CONFLICT, error.Format(db_domain, DB_CONFLICT,
"Already exists: %s", uri); "Already exists: %s", uri);
return nullptr; return false;
} }
if (strchr(r.uri, '/') != nullptr) { if (strchr(r.uri, '/') != nullptr) {
error.Format(db_domain, DB_NOT_FOUND, error.Format(db_domain, DB_NOT_FOUND,
"Parent not found: %s", uri); "Parent not found: %s", uri);
return nullptr; return false;
} }
Directory *mnt = r.directory->CreateChild(r.uri); Directory *mnt = r.directory->CreateChild(r.uri);
@@ -478,7 +481,7 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri,
if (cache_path.IsNull()) { if (cache_path.IsNull()) {
error.Format(db_domain, DB_NOT_FOUND, error.Format(db_domain, DB_NOT_FOUND,
"No 'cache_directory' configured"); "No 'cache_directory' configured");
return nullptr; return false;
} }
std::string name(storage_uri); std::string name(storage_uri);

View File

@@ -110,9 +110,9 @@ public:
virtual bool Open(Error &error) override; virtual bool Open(Error &error) override;
virtual void Close() override; virtual void Close() override;
virtual const LightSong *GetSong(const char *uri_utf8, const LightSong *GetSong(const char *uri_utf8,
Error &error) const override; Error &error) const override;
virtual void ReturnSong(const LightSong *song) const; void ReturnSong(const LightSong *song) const override;
virtual bool Visit(const DatabaseSelection &selection, virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory, VisitDirectory visit_directory,

View File

@@ -85,7 +85,7 @@ public:
virtual void Close() override; virtual void Close() override;
virtual const LightSong *GetSong(const char *uri_utf8, virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override; Error &error) const override;
virtual void ReturnSong(const LightSong *song) const; void ReturnSong(const LightSong *song) const override;
virtual bool Visit(const DatabaseSelection &selection, virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory, VisitDirectory visit_directory,
@@ -101,7 +101,9 @@ public:
virtual bool GetStats(const DatabaseSelection &selection, virtual bool GetStats(const DatabaseSelection &selection,
DatabaseStats &stats, DatabaseStats &stats,
Error &error) const override; Error &error) const override;
virtual time_t GetUpdateStamp() const {return 0;} time_t GetUpdateStamp() const override {
return 0;
}
protected: protected:
bool Configure(const config_param &param, Error &error); bool Configure(const config_param &param, Error &error);

View File

@@ -334,7 +334,7 @@ UpdateWalk::UpdateDirectory(Directory &directory, const FileInfo &info)
directory_set_stat(directory, info); directory_set_stat(directory, info);
Error error; Error error;
const std::auto_ptr<StorageDirectoryReader> reader(storage.OpenDirectory(directory.GetPath(), error)); const std::unique_ptr<StorageDirectoryReader> reader(storage.OpenDirectory(directory.GetPath(), error));
if (reader.get() == nullptr) { if (reader.get() == nullptr) {
LogError(error); LogError(error);
return false; return false;

View File

@@ -26,7 +26,10 @@
bool bool
DecoderPlugin::SupportsSuffix(const char *suffix) const DecoderPlugin::SupportsSuffix(const char *suffix) const
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(suffix != nullptr); assert(suffix != nullptr);
#endif
return suffixes != nullptr && string_array_contains(suffixes, suffix); return suffixes != nullptr && string_array_contains(suffixes, suffix);
@@ -35,7 +38,10 @@ DecoderPlugin::SupportsSuffix(const char *suffix) const
bool bool
DecoderPlugin::SupportsMimeType(const char *mime_type) const DecoderPlugin::SupportsMimeType(const char *mime_type) const
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(mime_type != nullptr); assert(mime_type != nullptr);
#endif
return mime_types != nullptr && return mime_types != nullptr &&
string_array_contains(mime_types, mime_type); string_array_contains(mime_types, mime_type);

View File

@@ -29,8 +29,10 @@
#include "input/InputStream.hxx" #include "input/InputStream.hxx"
#include "tag/TagId3.hxx" #include "tag/TagId3.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Alloc.hxx"
#include <string.h> #include <string.h>
#include <stdlib.h>
#ifdef HAVE_ID3TAG #ifdef HAVE_ID3TAG
#include <id3tag.h> #include <id3tag.h>
@@ -123,22 +125,26 @@ dsdlib_tag_id3(InputStream &is,
const id3_length_t count = size - offset; const id3_length_t count = size - offset;
/* Check and limit id3 tag size to prevent a stack overflow */ if (count < 10 || count > 1024 * 1024)
id3_byte_t dsdid3[4096];
if (count == 0 || count > sizeof(dsdid3))
return; return;
if (!decoder_read_full(nullptr, is, dsdid3, count)) id3_byte_t *const id3_buf = new id3_byte_t[count];
if (id3_buf == nullptr)
return; return;
struct id3_tag *id3_tag = id3_tag_parse(dsdid3, count); if (!decoder_read_full(nullptr, is, id3_buf, count)) {
delete[] id3_buf;
return;
}
struct id3_tag *id3_tag = id3_tag_parse(id3_buf, count);
delete[] id3_buf;
if (id3_tag == nullptr) if (id3_tag == nullptr)
return; return;
scan_id3_tag(id3_tag, handler, handler_ctx); scan_id3_tag(id3_tag, handler, handler_ctx);
id3_tag_delete(id3_tag); id3_tag_delete(id3_tag);
return; return;
} }
#endif #endif

View File

@@ -374,6 +374,7 @@ ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_S32P:
return SampleFormat::S32; return SampleFormat::S32;
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP: case AV_SAMPLE_FMT_FLTP:
return SampleFormat::FLOAT; return SampleFormat::FLOAT;

View File

@@ -53,10 +53,11 @@ public:
children.emplace_back(name, filter); children.emplace_back(name, filter);
} }
virtual AudioFormat Open(AudioFormat &af, Error &error) override; /* virtual methods from class Filter */
virtual void Close(); AudioFormat Open(AudioFormat &af, Error &error) override;
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src, void Close() override;
Error &error); ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
private: private:
/** /**

View File

@@ -34,9 +34,10 @@ class NormalizeFilter final : public Filter {
PcmBuffer buffer; PcmBuffer buffer;
public: public:
virtual AudioFormat Open(AudioFormat &af, Error &error) override; /* virtual methods from class Filter */
virtual void Close(); AudioFormat Open(AudioFormat &af, Error &error) override;
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src, void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override; Error &error) override;
}; };

View File

@@ -112,9 +112,10 @@ public:
*/ */
void Update(); void Update();
virtual AudioFormat Open(AudioFormat &af, Error &error) override; /* virtual methods from class Filter */
virtual void Close(); AudioFormat Open(AudioFormat &af, Error &error) override;
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src, void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override; Error &error) override;
}; };

View File

@@ -120,9 +120,10 @@ public:
*/ */
bool Configure(const config_param &param, Error &error); bool Configure(const config_param &param, Error &error);
virtual AudioFormat Open(AudioFormat &af, Error &error) override; /* virtual methods from class Filter */
virtual void Close(); AudioFormat Open(AudioFormat &af, Error &error) override;
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src, void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override; Error &error) override;
}; };

View File

@@ -43,9 +43,10 @@ public:
pv.SetVolume(_volume); pv.SetVolume(_volume);
} }
virtual AudioFormat Open(AudioFormat &af, Error &error) override; /* virtual methods from class Filter */
virtual void Close(); AudioFormat Open(AudioFormat &af, Error &error) override;
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src, void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override; Error &error) override;
}; };

View File

@@ -103,7 +103,10 @@ static inline void FixSeparators(std::string &s)
std::string std::string
PathToUTF8(const char *path_fs) PathToUTF8(const char *path_fs)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(path_fs != nullptr); assert(path_fs != nullptr);
#endif
#ifdef HAVE_GLIB #ifdef HAVE_GLIB
if (fs_charset.empty()) { if (fs_charset.empty()) {
@@ -144,7 +147,10 @@ PathToUTF8(const char *path_fs)
char * char *
PathFromUTF8(const char *path_utf8) PathFromUTF8(const char *path_utf8)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(path_utf8 != nullptr); assert(path_utf8 != nullptr);
#endif
if (fs_charset.empty()) if (fs_charset.empty())
return g_strdup(path_utf8); return g_strdup(path_utf8);

View File

@@ -52,7 +52,10 @@ template<typename Traits>
typename Traits::const_pointer typename Traits::const_pointer
GetBasePathImpl(typename Traits::const_pointer p) GetBasePathImpl(typename Traits::const_pointer p)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
#endif
typename Traits::const_pointer sep = Traits::FindLastSeparator(p); typename Traits::const_pointer sep = Traits::FindLastSeparator(p);
return sep != nullptr return sep != nullptr
@@ -64,7 +67,10 @@ template<typename Traits>
typename Traits::string typename Traits::string
GetParentPathImpl(typename Traits::const_pointer p) GetParentPathImpl(typename Traits::const_pointer p)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
#endif
typename Traits::const_pointer sep = Traits::FindLastSeparator(p); typename Traits::const_pointer sep = Traits::FindLastSeparator(p);
if (sep == nullptr) if (sep == nullptr)

View File

@@ -57,7 +57,11 @@ struct PathTraitsFS {
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static const_pointer FindLastSeparator(const_pointer p) { static const_pointer FindLastSeparator(const_pointer p) {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
#endif
#ifdef WIN32 #ifdef WIN32
const_pointer pos = p + GetLength(p); const_pointer pos = p + GetLength(p);
while (p != pos && !IsSeparator(*pos)) while (p != pos && !IsSeparator(*pos))
@@ -77,7 +81,11 @@ struct PathTraitsFS {
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static bool IsAbsolute(const_pointer p) { static bool IsAbsolute(const_pointer p) {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
#endif
#ifdef WIN32 #ifdef WIN32
if (IsDrive(p) && IsSeparator(p[2])) if (IsDrive(p) && IsSeparator(p[2]))
return true; return true;
@@ -147,7 +155,11 @@ struct PathTraitsUTF8 {
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static const_pointer FindLastSeparator(const_pointer p) { static const_pointer FindLastSeparator(const_pointer p) {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
#endif
return strrchr(p, SEPARATOR); return strrchr(p, SEPARATOR);
} }
@@ -160,7 +172,11 @@ struct PathTraitsUTF8 {
gcc_pure gcc_nonnull_all gcc_pure gcc_nonnull_all
static bool IsAbsolute(const_pointer p) { static bool IsAbsolute(const_pointer p) {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr); assert(p != nullptr);
#endif
#ifdef WIN32 #ifdef WIN32
if (IsDrive(p) && IsSeparator(p[2])) if (IsDrive(p) && IsSeparator(p[2]))
return true; return true;

View File

@@ -62,6 +62,7 @@ FileOutputStream::Commit(gcc_unused Error &error)
assert(IsDefined()); assert(IsDefined());
CloseHandle(handle); CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
return true; return true;
} }
@@ -71,6 +72,7 @@ FileOutputStream::Cancel()
assert(IsDefined()); assert(IsDefined());
CloseHandle(handle); CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
RemoveFile(path); RemoveFile(path);
} }

View File

@@ -160,6 +160,11 @@ AsyncInputStream::SeekDone()
assert(io_thread_inside()); assert(io_thread_inside());
assert(IsSeekPending()); assert(IsSeekPending());
/* we may have reached end-of-file previously, and the
connection may have been closed already; however after
seeking successfully, the connection must be alive again */
open = true;
seek_state = SeekState::NONE; seek_state = SeekState::NONE;
cond.broadcast(); cond.broadcast();
} }

View File

@@ -122,7 +122,10 @@ InputStream::IsAvailable()
size_t size_t
InputStream::LockRead(void *ptr, size_t _size, Error &error) InputStream::LockRead(void *ptr, size_t _size, Error &error)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(ptr != nullptr); assert(ptr != nullptr);
#endif
assert(_size > 0); assert(_size > 0);
const ScopeLock protect(mutex); const ScopeLock protect(mutex);

View File

@@ -54,10 +54,6 @@
#include "plugins/CdioParanoiaInputPlugin.hxx" #include "plugins/CdioParanoiaInputPlugin.hxx"
#endif #endif
#ifdef ENABLE_DESPOTIFY
#include "plugins/DespotifyInputPlugin.hxx"
#endif
const InputPlugin *const input_plugins[] = { const InputPlugin *const input_plugins[] = {
&input_plugin_file, &input_plugin_file,
#ifdef HAVE_ALSA #ifdef HAVE_ALSA
@@ -83,9 +79,6 @@ const InputPlugin *const input_plugins[] = {
#endif #endif
#ifdef ENABLE_CDIO_PARANOIA #ifdef ENABLE_CDIO_PARANOIA
&input_plugin_cdio_paranoia, &input_plugin_cdio_paranoia,
#endif
#ifdef ENABLE_DESPOTIFY
&input_plugin_despotify,
#endif #endif
nullptr nullptr
}; };

View File

@@ -1,227 +0,0 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "DespotifyInputPlugin.hxx"
#include "lib/despotify/DespotifyUtils.hxx"
#include "../InputStream.hxx"
#include "../InputPlugin.hxx"
#include "tag/Tag.hxx"
#include "util/StringUtil.hxx"
#include "Log.hxx"
extern "C" {
#include <despotify.h>
}
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
class DespotifyInputStream final : public InputStream {
struct despotify_session *session;
struct ds_track *track;
Tag tag;
struct ds_pcm_data pcm;
size_t len_available;
bool eof;
DespotifyInputStream(const char *_uri,
Mutex &_mutex, Cond &_cond,
despotify_session *_session,
ds_track *_track)
:InputStream(_uri, _mutex, _cond),
session(_session), track(_track),
tag(mpd_despotify_tag_from_track(*track)),
len_available(0), eof(false) {
memset(&pcm, 0, sizeof(pcm));
/* Despotify outputs pcm data */
SetMimeType("audio/x-mpd-cdda-pcm");
SetReady();
}
public:
~DespotifyInputStream();
static InputStream *Open(const char *url, Mutex &mutex, Cond &cond,
Error &error);
void Callback(int sig);
/* virtual methods from InputStream */
bool IsEOF() override {
return eof;
}
Tag *ReadTag() override {
if (tag.IsEmpty())
return nullptr;
Tag *result = new Tag(std::move(tag));
tag.Clear();
return result;
}
size_t Read(void *ptr, size_t size, Error &error) override;
private:
void FillBuffer();
};
inline void
DespotifyInputStream::FillBuffer()
{
/* Wait until there is data */
while (1) {
int rc = despotify_get_pcm(session, &pcm);
if (rc == 0 && pcm.len) {
len_available = pcm.len;
break;
}
if (eof == true)
break;
if (rc < 0) {
LogDebug(despotify_domain, "despotify_get_pcm error");
eof = true;
break;
}
/* Wait a while until next iteration */
usleep(50 * 1000);
}
}
inline void
DespotifyInputStream::Callback(int sig)
{
switch (sig) {
case DESPOTIFY_NEW_TRACK:
break;
case DESPOTIFY_TIME_TELL:
break;
case DESPOTIFY_TRACK_PLAY_ERROR:
LogWarning(despotify_domain, "Track play error");
eof = true;
len_available = 0;
break;
case DESPOTIFY_END_OF_PLAYLIST:
eof = true;
LogDebug(despotify_domain, "End of playlist");
break;
}
}
static void callback(gcc_unused struct despotify_session* ds,
int sig, gcc_unused void* data, void* callback_data)
{
DespotifyInputStream *ctx = (DespotifyInputStream *)callback_data;
ctx->Callback(sig);
}
DespotifyInputStream::~DespotifyInputStream()
{
mpd_despotify_unregister_callback(callback);
despotify_free_track(track);
}
inline InputStream *
DespotifyInputStream::Open(const char *url,
Mutex &mutex, Cond &cond,
gcc_unused Error &error)
{
if (!StringStartsWith(url, "spt://"))
return nullptr;
despotify_session *session = mpd_despotify_get_session();
if (session == nullptr)
return nullptr;
ds_link *ds_link = despotify_link_from_uri(url + 6);
if (!ds_link) {
FormatDebug(despotify_domain, "Can't find %s", url);
return nullptr;
}
if (ds_link->type != LINK_TYPE_TRACK) {
despotify_free_link(ds_link);
return nullptr;
}
ds_track *track = despotify_link_get_track(session, ds_link);
despotify_free_link(ds_link);
if (!track)
return nullptr;
DespotifyInputStream *ctx =
new DespotifyInputStream(url, mutex, cond,
session, track);
if (!mpd_despotify_register_callback(callback, ctx)) {
delete ctx;
return nullptr;
}
if (despotify_play(ctx->session, ctx->track, false) == false) {
mpd_despotify_unregister_callback(callback);
delete ctx;
return nullptr;
}
return ctx;
}
static InputStream *
input_despotify_open(const char *url, Mutex &mutex, Cond &cond, Error &error)
{
return DespotifyInputStream::Open(url, mutex, cond, error);
}
size_t
DespotifyInputStream::Read(void *ptr, size_t read_size,
gcc_unused Error &error)
{
if (len_available == 0)
FillBuffer();
size_t to_cpy = std::min(read_size, len_available);
memcpy(ptr, pcm.buf, to_cpy);
len_available -= to_cpy;
offset += to_cpy;
return to_cpy;
}
const InputPlugin input_plugin_despotify = {
"despotify",
nullptr,
nullptr,
input_despotify_open,
};

View File

@@ -1,25 +0,0 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef INPUT_DESPOTIFY_HXX
#define INPUT_DESPOTIFY_HXX
extern const struct InputPlugin input_plugin_despotify;
#endif

View File

@@ -43,7 +43,7 @@ protected:
virtual size_t ThreadRead(void *ptr, size_t size, virtual size_t ThreadRead(void *ptr, size_t size,
Error &error) override; Error &error) override;
virtual void Close() { void Close() override {
mmsx_close(mms); mmsx_close(mms);
} }
}; };
@@ -92,6 +92,13 @@ input_mms_open(const char *url,
size_t size_t
MmsInputStream::ThreadRead(void *ptr, size_t read_size, Error &error) MmsInputStream::ThreadRead(void *ptr, size_t read_size, Error &error)
{ {
/* unfortunately, mmsx_read() blocks until the whole buffer
has been filled; to avoid big latencies, limit the size of
each chunk we read to a reasonable size */
constexpr size_t MAX_CHUNK = 16384;
if (read_size > MAX_CHUNK)
read_size = MAX_CHUNK;
int nbytes = mmsx_read(nullptr, mms, (char *)ptr, read_size); int nbytes = mmsx_read(nullptr, mms, (char *)ptr, read_size);
if (nbytes <= 0) { if (nbytes <= 0) {
if (nbytes < 0) if (nbytes < 0)

View File

@@ -1,153 +0,0 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "DespotifyUtils.hxx"
#include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx"
#include "config/ConfigGlobal.hxx"
#include "config/ConfigOption.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
extern "C" {
#include <despotify.h>
}
#include <stdio.h>
const Domain despotify_domain("despotify");
static struct despotify_session *g_session;
static void (*registered_callbacks[8])(struct despotify_session *,
int, void *, void *);
static void *registered_callback_data[8];
static void
callback(struct despotify_session* ds, int sig,
void *data, gcc_unused void *callback_data)
{
size_t i;
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
void (*cb)(struct despotify_session *, int, void *, void *) = registered_callbacks[i];
void *cb_data = registered_callback_data[i];
if (cb)
cb(ds, sig, data, cb_data);
}
}
bool mpd_despotify_register_callback(void (*cb)(struct despotify_session *, int, void *, void *),
void *cb_data)
{
size_t i;
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
if (!registered_callbacks[i]) {
registered_callbacks[i] = cb;
registered_callback_data[i] = cb_data;
return true;
}
}
return false;
}
void mpd_despotify_unregister_callback(void (*cb)(struct despotify_session *, int, void *, void *))
{
size_t i;
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
if (registered_callbacks[i] == cb) {
registered_callbacks[i] = nullptr;
}
}
}
Tag
mpd_despotify_tag_from_track(const ds_track &track)
{
char tracknum[20];
char comment[80];
char date[20];
if (!track.has_meta_data)
return Tag();
TagBuilder tag;
snprintf(tracknum, sizeof(tracknum), "%d", track.tracknumber);
snprintf(date, sizeof(date), "%d", track.year);
snprintf(comment, sizeof(comment), "Bitrate %d Kbps, %sgeo restricted",
track.file_bitrate / 1000,
track.geo_restricted ? "" : "not ");
tag.AddItem(TAG_TITLE, track.title);
tag.AddItem(TAG_ARTIST, track.artist->name);
tag.AddItem(TAG_TRACK, tracknum);
tag.AddItem(TAG_ALBUM, track.album);
tag.AddItem(TAG_DATE, date);
tag.AddItem(TAG_COMMENT, comment);
tag.SetDuration(SignedSongTime::FromMS(track.length));
return tag.Commit();
}
struct despotify_session *mpd_despotify_get_session(void)
{
const char *user;
const char *passwd;
bool high_bitrate;
if (g_session)
return g_session;
user = config_get_string(CONF_DESPOTIFY_USER, nullptr);
passwd = config_get_string(CONF_DESPOTIFY_PASSWORD, nullptr);
high_bitrate = config_get_bool(CONF_DESPOTIFY_HIGH_BITRATE, true);
if (user == nullptr || passwd == nullptr) {
LogDebug(despotify_domain,
"disabling despotify because account is not configured");
return nullptr;
}
if (!despotify_init()) {
LogWarning(despotify_domain, "Can't initialize despotify");
return nullptr;
}
g_session = despotify_init_client(callback, nullptr,
high_bitrate, true);
if (!g_session) {
LogWarning(despotify_domain,
"Can't initialize despotify client");
return nullptr;
}
if (!despotify_authenticate(g_session, user, passwd)) {
LogWarning(despotify_domain,
"Can't authenticate despotify session");
despotify_exit(g_session);
return nullptr;
}
return g_session;
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_DESPOTIFY_H
#define MPD_DESPOTIFY_H
struct Tag;
struct despotify_session;
struct ds_track;
extern const class Domain despotify_domain;
/**
* Return the current despotify session.
*
* If the session isn't initialized, this function will initialize
* it and connect to Spotify.
*
* @return a pointer to the despotify session, or nullptr if it can't
* be initialized (e.g., if the configuration isn't supplied)
*/
struct despotify_session *mpd_despotify_get_session(void);
/**
* Create a MPD tags structure from a spotify track
*
* @param track the track to convert
*
* @return filled in #Tag structure
*/
Tag
mpd_despotify_tag_from_track(const ds_track &track);
/**
* Register a despotify callback.
*
* Despotify calls this e.g., when a track ends.
*
* @param cb the callback
* @param cb_data the data to pass to the callback
*
* @return true if the callback could be registered
*/
bool mpd_despotify_register_callback(void (*cb)(struct despotify_session *, int, void *, void *),
void *cb_data);
/**
* Unregister a despotify callback.
*
* @param cb the callback to unregister.
*/
void mpd_despotify_unregister_callback(void (*cb)(struct despotify_session *, int, void *, void *));
#endif

View File

@@ -121,8 +121,11 @@ gcc_pure
int int
IcuCollate(const char *a, const char *b) IcuCollate(const char *a, const char *b)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(a != nullptr); assert(a != nullptr);
assert(b != nullptr); assert(b != nullptr);
#endif
#ifdef HAVE_ICU #ifdef HAVE_ICU
assert(collator != nullptr); assert(collator != nullptr);
@@ -159,7 +162,10 @@ IcuCaseFold(const char *src)
{ {
#ifdef HAVE_ICU #ifdef HAVE_ICU
assert(collator != nullptr); assert(collator != nullptr);
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(src != nullptr); assert(src != nullptr);
#endif
const auto u = UCharFromUTF8(src); const auto u = UCharFromUTF8(src);
if (u.IsNull()) if (u.IsNull())

View File

@@ -58,9 +58,6 @@ static const char *remoteUrlPrefixes[] = {
#ifdef ENABLE_CDIO_PARANOIA #ifdef ENABLE_CDIO_PARANOIA
"cdda://", "cdda://",
#endif #endif
#ifdef ENABLE_DESPOTIFY
"spt://",
#endif
#ifdef HAVE_ALSA #ifdef HAVE_ALSA
"alsa://", "alsa://",
#endif #endif

View File

@@ -109,7 +109,7 @@ ShoutOutput::Configure(const config_param &param, Error &error)
if (!audio_format.IsFullyDefined()) { if (!audio_format.IsFullyDefined()) {
error.Set(config_domain, error.Set(config_domain,
"Need full audio format specification"); "Need full audio format specification");
return nullptr; return false;
} }
const char *host = require_block_string(param, "host"); const char *host = require_block_string(param, "host");

View File

@@ -44,7 +44,7 @@ PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format,
"PCM conversion from %s to %s is not implemented", "PCM conversion from %s to %s is not implemented",
sample_format_to_string(_src_format), sample_format_to_string(_src_format),
sample_format_to_string(_dest_format)); sample_format_to_string(_dest_format));
return nullptr; return false;
case SampleFormat::S16: case SampleFormat::S16:
case SampleFormat::S24_P32: case SampleFormat::S24_P32:

View File

@@ -23,7 +23,6 @@
#include "plugins/ExtM3uPlaylistPlugin.hxx" #include "plugins/ExtM3uPlaylistPlugin.hxx"
#include "plugins/M3uPlaylistPlugin.hxx" #include "plugins/M3uPlaylistPlugin.hxx"
#include "plugins/XspfPlaylistPlugin.hxx" #include "plugins/XspfPlaylistPlugin.hxx"
#include "plugins/DespotifyPlaylistPlugin.hxx"
#include "plugins/SoundCloudPlaylistPlugin.hxx" #include "plugins/SoundCloudPlaylistPlugin.hxx"
#include "plugins/PlsPlaylistPlugin.hxx" #include "plugins/PlsPlaylistPlugin.hxx"
#include "plugins/AsxPlaylistPlugin.hxx" #include "plugins/AsxPlaylistPlugin.hxx"
@@ -54,9 +53,6 @@ const struct playlist_plugin *const playlist_plugins[] = {
&asx_playlist_plugin, &asx_playlist_plugin,
&rss_playlist_plugin, &rss_playlist_plugin,
#endif #endif
#ifdef ENABLE_DESPOTIFY
&despotify_playlist_plugin,
#endif
#ifdef ENABLE_SOUNDCLOUD #ifdef ENABLE_SOUNDCLOUD
&soundcloud_playlist_plugin, &soundcloud_playlist_plugin,
#endif #endif

View File

@@ -1,142 +0,0 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "DespotifyPlaylistPlugin.hxx"
#include "lib/despotify/DespotifyUtils.hxx"
#include "../PlaylistPlugin.hxx"
#include "../MemorySongEnumerator.hxx"
#include "tag/Tag.hxx"
#include "DetachedSong.hxx"
#include "Log.hxx"
extern "C" {
#include <despotify.h>
}
#include <string.h>
#include <stdlib.h>
#include <string.h>
static void
add_song(std::forward_list<DetachedSong> &songs, ds_track &track)
{
const char *dsp_scheme = despotify_playlist_plugin.schemes[0];
char uri[128];
char *ds_uri;
/* Create a spt://... URI for MPD */
snprintf(uri, sizeof(uri), "%s://", dsp_scheme);
ds_uri = uri + strlen(dsp_scheme) + 3;
if (despotify_track_to_uri(&track, ds_uri) != ds_uri) {
/* Should never really fail, but let's be sure */
FormatDebug(despotify_domain,
"Can't add track %s", track.title);
return;
}
songs.emplace_front(uri, mpd_despotify_tag_from_track(track));
}
static bool
parse_track(struct despotify_session *session,
std::forward_list<DetachedSong> &songs,
struct ds_link *link)
{
struct ds_track *track = despotify_link_get_track(session, link);
if (track == nullptr)
return false;
add_song(songs, *track);
return true;
}
static bool
parse_playlist(struct despotify_session *session,
std::forward_list<DetachedSong> &songs,
struct ds_link *link)
{
ds_playlist *playlist = despotify_link_get_playlist(session, link);
if (playlist == nullptr)
return false;
for (ds_track *track = playlist->tracks; track != nullptr;
track = track->next)
add_song(songs, *track);
return true;
}
static SongEnumerator *
despotify_playlist_open_uri(const char *url,
gcc_unused Mutex &mutex, gcc_unused Cond &cond)
{
despotify_session *session = mpd_despotify_get_session();
if (session == nullptr)
return nullptr;
/* Get link without spt:// */
ds_link *link =
despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3);
if (link == nullptr) {
FormatDebug(despotify_domain, "Can't find %s\n", url);
return nullptr;
}
std::forward_list<DetachedSong> songs;
bool parse_result;
switch (link->type) {
case LINK_TYPE_TRACK:
parse_result = parse_track(session, songs, link);
break;
case LINK_TYPE_PLAYLIST:
parse_result = parse_playlist(session, songs, link);
break;
default:
parse_result = false;
break;
}
despotify_free_link(link);
if (!parse_result)
return nullptr;
songs.reverse();
return new MemorySongEnumerator(std::move(songs));
}
static const char *const despotify_schemes[] = {
"spt",
nullptr
};
const struct playlist_plugin despotify_playlist_plugin = {
"despotify",
nullptr,
nullptr,
despotify_playlist_open_uri,
nullptr,
despotify_schemes,
nullptr,
nullptr,
};

View File

@@ -1,25 +0,0 @@
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX
#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin despotify_playlist_plugin;
#endif

View File

@@ -288,7 +288,7 @@ NfsStorage::GetInfo(const char *uri_utf8, gcc_unused bool follow,
return false; return false;
if (!WaitConnected(error)) if (!WaitConnected(error))
return nullptr; return false;
NfsGetInfoOperation operation(*connection, path.c_str(), info); NfsGetInfoOperation operation(*connection, path.c_str(), info);
return operation.Run(error); return operation.Run(error);

View File

@@ -40,8 +40,8 @@ MonotonicClockS(void)
if (base.denom == 0) if (base.denom == 0)
(void)mach_timebase_info(&base); (void)mach_timebase_info(&base);
return (unsigned)((mach_absolute_time() * base.numer / 1000) return (unsigned)(((double)mach_absolute_time() * base.numer / 1000)
/ (1000000 * base.denom)); / base.denom / 1000000);
#elif defined(CLOCK_MONOTONIC) #elif defined(CLOCK_MONOTONIC)
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -62,8 +62,8 @@ MonotonicClockMS(void)
if (base.denom == 0) if (base.denom == 0)
(void)mach_timebase_info(&base); (void)mach_timebase_info(&base);
return (unsigned)((mach_absolute_time() * base.numer) return (unsigned)(((double)mach_absolute_time() * base.numer)
/ (1000000 * base.denom)); / base.denom / 1000000);
#elif defined(CLOCK_MONOTONIC) #elif defined(CLOCK_MONOTONIC)
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -104,8 +104,8 @@ MonotonicClockUS(void)
if (base.denom == 0) if (base.denom == 0)
(void)mach_timebase_info(&base); (void)mach_timebase_info(&base);
return ((uint64_t)mach_absolute_time() * (uint64_t)base.numer) return (uint64_t)(((double)mach_absolute_time() * base.numer)
/ (1000 * (uint64_t)base.denom); / base.denom / 1000);
#elif defined(CLOCK_MONOTONIC) #elif defined(CLOCK_MONOTONIC)
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);

View File

@@ -25,6 +25,8 @@
#include "Tag.hxx" #include "Tag.hxx"
#include "util/WritableBuffer.hxx" #include "util/WritableBuffer.hxx"
#include <array>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@@ -168,12 +170,19 @@ TagBuilder::Complement(const Tag &other)
has_playlist |= other.has_playlist; has_playlist |= other.has_playlist;
/* build a table of tag types that were already present in
this object, which will not be copied from #other */
std::array<bool, TAG_NUM_OF_ITEM_TYPES> present;
present.fill(false);
for (const TagItem *i : items)
present[i->type] = true;
items.reserve(items.size() + other.num_items); items.reserve(items.size() + other.num_items);
tag_pool_lock.lock(); tag_pool_lock.lock();
for (unsigned i = 0, n = other.num_items; i != n; ++i) { for (unsigned i = 0, n = other.num_items; i != n; ++i) {
TagItem *item = other.items[i]; TagItem *item = other.items[i];
if (!HasType(item->type)) if (!present[item->type])
items.push_back(tag_pool_dup_item(item)); items.push_back(tag_pool_dup_item(item));
} }
tag_pool_lock.unlock(); tag_pool_lock.unlock();
@@ -182,7 +191,10 @@ TagBuilder::Complement(const Tag &other)
inline void inline void
TagBuilder::AddItemInternal(TagType type, const char *value, size_t length) TagBuilder::AddItemInternal(TagType type, const char *value, size_t length)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(value != nullptr); assert(value != nullptr);
#endif
assert(length > 0); assert(length > 0);
auto f = FixTagString(value, length); auto f = FixTagString(value, length);
@@ -203,7 +215,10 @@ TagBuilder::AddItemInternal(TagType type, const char *value, size_t length)
void void
TagBuilder::AddItem(TagType type, const char *value, size_t length) TagBuilder::AddItem(TagType type, const char *value, size_t length)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(value != nullptr); assert(value != nullptr);
#endif
if (length == 0 || ignore_tag_items[type]) if (length == 0 || ignore_tag_items[type])
return; return;
@@ -214,7 +229,10 @@ TagBuilder::AddItem(TagType type, const char *value, size_t length)
void void
TagBuilder::AddItem(TagType type, const char *value) TagBuilder::AddItem(TagType type, const char *value)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(value != nullptr); assert(value != nullptr);
#endif
AddItem(type, value, strlen(value)); AddItem(type, value, strlen(value));
} }

View File

@@ -20,17 +20,28 @@
#ifndef MPD_THREAD_NAME_HXX #ifndef MPD_THREAD_NAME_HXX
#define MPD_THREAD_NAME_HXX #define MPD_THREAD_NAME_HXX
#ifdef HAVE_PTHREAD_SETNAME_NP #if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__NetBSD__)
# define HAVE_THREAD_NAME
# include <pthread.h> # include <pthread.h>
#include <stdio.h>
#elif defined(HAVE_PRCTL) #elif defined(HAVE_PRCTL)
# include <sys/prctl.h> # include <sys/prctl.h>
# ifdef PR_SET_NAME
# define HAVE_THREAD_NAME
# endif
#endif
#ifdef HAVE_THREAD_NAME
# include <stdio.h>
#endif #endif
static inline void static inline void
SetThreadName(const char *name) SetThreadName(const char *name)
{ {
#ifdef HAVE_PTHREAD_SETNAME_NP #if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__NetBSD__)
/* not using pthread_setname_np() on NetBSD because it
requires a non-const pointer argument, which we don't have
here */
#ifdef __APPLE__ #ifdef __APPLE__
pthread_setname_np(name); pthread_setname_np(name);
#else #else
@@ -47,7 +58,7 @@ template<typename... Args>
static inline void static inline void
FormatThreadName(const char *fmt, gcc_unused Args&&... args) FormatThreadName(const char *fmt, gcc_unused Args&&... args)
{ {
#ifdef HAVE_PTHREAD_SETNAME_NP #ifdef HAVE_THREAD_NAME
char buffer[16]; char buffer[16];
snprintf(buffer, sizeof(buffer), fmt, args...); snprintf(buffer, sizeof(buffer), fmt, args...);
SetThreadName(buffer); SetThreadName(buffer);

View File

@@ -43,8 +43,11 @@ gcc_pure gcc_nonnull_all
static inline bool static inline bool
StringEqualsCaseASCII(const char *a, const char *b) StringEqualsCaseASCII(const char *a, const char *b)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(a != nullptr); assert(a != nullptr);
assert(b != nullptr); assert(b != nullptr);
#endif
/* note: strcasecmp() depends on the locale, but for ASCII-only /* note: strcasecmp() depends on the locale, but for ASCII-only
strings, it's safe to use */ strings, it's safe to use */
@@ -55,8 +58,11 @@ gcc_pure gcc_nonnull_all
static inline bool static inline bool
StringEqualsCaseASCII(const char *a, const char *b, size_t n) StringEqualsCaseASCII(const char *a, const char *b, size_t n)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(a != nullptr); assert(a != nullptr);
assert(b != nullptr); assert(b != nullptr);
#endif
/* note: strcasecmp() depends on the locale, but for ASCII-only /* note: strcasecmp() depends on the locale, but for ASCII-only
strings, it's safe to use */ strings, it's safe to use */

View File

@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "config.h"
#include "Alloc.hxx" #include "Alloc.hxx"
#include <stdlib.h> #include <stdlib.h>
@@ -62,14 +63,14 @@ xstrdup(const char *s)
char * char *
xstrndup(const char *s, size_t n) xstrndup(const char *s, size_t n)
{ {
#ifdef WIN32 #ifdef HAVE_STRNDUP
char *p = (char *)xalloc(n + 1);
memcpy(p, s, n);
p[n] = 0;
#else
char *p = strndup(s, n); char *p = strndup(s, n);
if (gcc_unlikely(p == nullptr)) if (gcc_unlikely(p == nullptr))
oom(); oom();
#else
char *p = (char *)xalloc(n + 1);
memcpy(p, s, n);
p[n] = 0;
#endif #endif
return p; return p;

View File

@@ -35,7 +35,7 @@
#include <new> #include <new>
#include <utility> #include <utility>
#if !defined(__clang__) && __GNUC__ && !GCC_CHECK_VERSION(4,8) #if GCC_OLDER_THAN(4,8)
#include <type_traits> #include <type_traits>
#endif #endif
@@ -54,7 +54,7 @@
*/ */
template<class T> template<class T>
class Manual { class Manual {
#if !defined(__clang__) && __GNUC__ && !GCC_CHECK_VERSION(4,8) #if GCC_OLDER_THAN(4,8)
/* no alignas() on gcc < 4.8: apply worst-case fallback */ /* no alignas() on gcc < 4.8: apply worst-case fallback */
__attribute__((aligned(8))) __attribute__((aligned(8)))
#else #else

View File

@@ -140,8 +140,11 @@ uri_remove_auth(const char *uri)
bool bool
uri_is_child(const char *parent, const char *child) uri_is_child(const char *parent, const char *child)
{ {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(parent != nullptr); assert(parent != nullptr);
assert(child != nullptr); assert(child != nullptr);
#endif
const size_t parent_length = strlen(parent); const size_t parent_length = strlen(parent);
return memcmp(parent, child, parent_length) == 0 && return memcmp(parent, child, parent_length) == 0 &&

View File

@@ -35,6 +35,7 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h>
const struct filter_plugin * const struct filter_plugin *
filter_plugin_by_name(gcc_unused const char *name) filter_plugin_by_name(gcc_unused const char *name)