diff --git a/NEWS b/NEWS index aced502eb..d51ab7bce 100644 --- a/NEWS +++ b/NEWS @@ -9,9 +9,16 @@ ver 0.24 (not yet released) * tags - new tag "Mood" -ver 0.23.6 (not yet released) +ver 0.23.6 (2022/03/14) +* protocol + - support filename "cover.webp" for "albumart" command + - support "readcomments" and "readpicture" on CUE tracks * decoder + - ffmpeg: fix end-of-file check (update stuck at empty files) - opus: fix "readpicture" on Opus files +* output + - pipewire: fix crash bug if setting volume before playback starts + - wasapi: fix resume after pause ver 0.23.5 (2021/12/01) * protocol diff --git a/android/build.py b/android/build.py index dd5bb2f5b..07e7d1ac3 100755 --- a/android/build.py +++ b/android/build.py @@ -13,7 +13,7 @@ android_abi = sys.argv[3] configure_args = sys.argv[4:] if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')): - print("SDK not found in", ndk_path, file=sys.stderr) + print("SDK not found in", sdk_path, file=sys.stderr) sys.exit(1) if not os.path.isdir(ndk_path): diff --git a/python/build/libs.py b/python/build/libs.py index dd69d9780..412ca5b92 100644 --- a/python/build/libs.py +++ b/python/build/libs.py @@ -12,14 +12,14 @@ from build.boost import BoostProject from build.jack import JackProject libmpdclient = MesonProject( - 'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.19.tar.xz', - '158aad4c2278ab08e76a3f2b0166c99b39fae00ee17231bd225c5a36e977a189', + 'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.20.tar.xz', + '18793f68e939c3301e34d8fcadea1f7daa24143941263cecadb80126194e277d', 'lib/libmpdclient.a', ) libogg = CmakeProject( - 'http://downloads.xiph.org/releases/ogg/libogg-1.3.4.tar.xz', - 'c163bc12bc300c401b6aa35907ac682671ea376f13ae0969a220f7ddf71893fe', + 'http://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.xz', + 'c4d91be36fc8e54deae7575241e03f4211eb102afb3fc0775fbbc1b740016705', 'lib/libogg.a', [ '-DBUILD_SHARED_LIBS=OFF', @@ -43,8 +43,8 @@ opus = AutotoolsProject( ) flac = AutotoolsProject( - 'http://downloads.xiph.org/releases/flac/flac-1.3.3.tar.xz', - '213e82bd716c9de6db2f98bcadbc4c24c7e2efe8c75939a1a84e28539c4e1748', + 'http://downloads.xiph.org/releases/flac/flac-1.3.4.tar.xz', + '8ff0607e75a322dd7cd6ec48f4f225471404ae2730d0ea945127b1355155e737', 'lib/libFLAC.a', [ '--disable-shared', '--enable-static', @@ -151,8 +151,8 @@ gme = CmakeProject( ) ffmpeg = FfmpegProject( - 'http://ffmpeg.org/releases/ffmpeg-4.4.1.tar.xz', - 'eadbad9e9ab30b25f5520fbfde99fae4a92a1ae3c0257a8d68569a4651e30e02', + 'http://ffmpeg.org/releases/ffmpeg-5.0.tar.xz', + '51e919f7d205062c0fd4fae6243a84850391115104ccf1efc451733bc0ac7298', 'lib/libavcodec.a', [ '--disable-shared', '--enable-static', @@ -380,14 +380,14 @@ ffmpeg = FfmpegProject( ) openssl = OpenSSLProject( - 'https://www.openssl.org/source/openssl-3.0.0.tar.gz', - '59eedfcb46c25214c9bd37ed6078297b4df01d012267fe9e9eee31f61bc70536', + 'https://www.openssl.org/source/openssl-3.0.1.tar.gz', + 'c311ad853353bce796edad01a862c50a8a587f62e7e2100ef465ab53ec9b06d1', 'include/openssl/ossl_typ.h', ) curl = CmakeProject( - 'https://curl.se/download/curl-7.79.1.tar.xz', - '0606f74b1182ab732a17c11613cbbaf7084f2e6cca432642d0e3ad7c224c3689', + 'https://curl.se/download/curl-7.82.0.tar.xz', + '0aaa12d7bd04b0966254f2703ce80dd5c38dbbd76af0297d3d690cdce58a583c', 'lib/libcurl.a', [ '-DBUILD_CURL_EXE=OFF', @@ -415,14 +415,14 @@ curl = CmakeProject( '-DBUILD_TESTING=OFF', ], windows_configure_args=[ - '-DCMAKE_USE_SCHANNEL=ON', + '-DCURL_USE_SCHANNEL=ON', ], patches='src/lib/curl/patches', ) libnfs = AutotoolsProject( - 'https://github.com/sahlberg/libnfs/archive/libnfs-4.0.0.tar.gz', - '6ee77e9fe220e2d3e3b1f53cfea04fb319828cc7dbb97dd9df09e46e901d797d', + 'https://github.com/sahlberg/libnfs/archive/libnfs-5.0.1.tar.gz', + '7ef445410b42f36b9bad426608b53ccb9ccca4101e545c383f564c11db672ca8', 'lib/libnfs.a', [ '--disable-shared', '--enable-static', @@ -433,8 +433,7 @@ libnfs = AutotoolsProject( '--disable-utils', '--disable-examples', ], - base='libnfs-libnfs-4.0.0', - patches='src/lib/nfs/patches', + base='libnfs-libnfs-5.0.1', autoreconf=True, ) @@ -445,7 +444,7 @@ jack = JackProject( ) boost = BoostProject( - 'https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2', - 'fc9f85fc030e233142908241af7a846e60630aa7388de9a5fafb1f3a26840854', + 'https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.tar.bz2', + '8681f175d4bdb26c52222665793eef08490d7758529330f98d3b29dd0735bccc', 'include/boost/version.hpp', ) diff --git a/src/TagAny.cxx b/src/TagAny.cxx index 6f875846e..2fd573fae 100644 --- a/src/TagAny.cxx +++ b/src/TagAny.cxx @@ -21,12 +21,16 @@ #include "TagStream.hxx" #include "TagFile.hxx" #include "tag/Generic.hxx" +#include "song/LightSong.hxx" +#include "db/Interface.hxx" #include "storage/StorageInterface.hxx" #include "client/Client.hxx" #include "protocol/Ack.hxx" #include "fs/AllocatedPath.hxx" #include "input/InputStream.hxx" #include "util/Compiler.h" +#include "util/ScopeExit.hxx" +#include "util/StringCompare.hxx" #include "util/UriExtract.hxx" #include "LocateUri.hxx" @@ -51,10 +55,67 @@ TagScanFile(const Path path_fs, TagHandler &handler) ScanGenericTags(path_fs, handler); } +#ifdef ENABLE_DATABASE + +/** + * Collapse "../" prefixes in a URI relative to the specified base + * URI. + */ +static std::string +ResolveUri(std::string_view base, const char *relative) +{ + while (true) { + const char *rest = StringAfterPrefix(relative, "../"); + if (rest == nullptr) + break; + + if (base == ".") + throw ProtocolError(ACK_ERROR_NO_EXIST, "Bad real URI"); + + base = PathTraitsUTF8::GetParent(base); + relative = rest; + } + + return PathTraitsUTF8::Build(base, relative); +} + +/** + * Look up the specified song in the database and return its + * (resolved) "real" URI. + */ +static std::string +GetRealSongUri(Client &client, std::string_view uri) +{ + const auto &db = client.GetDatabaseOrThrow(); + + const auto *song = db.GetSong(uri); + if (song == nullptr) + throw ProtocolError(ACK_ERROR_NO_EXIST, "No such song"); + + AtScopeExit(&db, song) { db.ReturnSong(song); }; + + if (song->real_uri == nullptr) + return {}; + + return ResolveUri(PathTraitsUTF8::GetParent(uri), song->real_uri); +} + +#endif + static void TagScanDatabase(Client &client, const char *uri, TagHandler &handler) { #ifdef ENABLE_DATABASE + const auto real_uri = GetRealSongUri(client, uri); + + if (!real_uri.empty()) { + uri = real_uri.c_str(); + + // TODO: support absolute paths? + if (uri_has_scheme(uri)) + return TagScanStream(uri, handler); + } + const Storage *storage = client.GetStorage(); if (storage == nullptr) { #else diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index 006dedf28..c27eaeb79 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -160,8 +160,7 @@ find_stream_art(std::string_view directory, Mutex &mutex) static constexpr auto art_names = std::array { "cover.png", "cover.jpg", - "cover.tiff", - "cover.bmp", + "cover.webp", }; for(const auto name : art_names) { diff --git a/src/decoder/plugins/FfmpegIo.cxx b/src/decoder/plugins/FfmpegIo.cxx index f505dbdec..2e22d9599 100644 --- a/src/decoder/plugins/FfmpegIo.cxx +++ b/src/decoder/plugins/FfmpegIo.cxx @@ -21,6 +21,7 @@ #define __STDC_CONSTANT_MACROS #include "FfmpegIo.hxx" +#include "libavutil/mem.h" #include "../DecoderAPI.hxx" #include "input/InputStream.hxx" @@ -35,7 +36,11 @@ AvioStream::~AvioStream() inline int AvioStream::Read(void *dest, int size) { - return decoder_read(client, input, dest, size); + const auto nbytes = decoder_read(client, input, dest, size); + if (nbytes == 0) + return AVERROR_EOF; + + return nbytes; } inline int64_t diff --git a/src/lib/nfs/patches/no_sprintf_s b/src/lib/nfs/patches/no_sprintf_s deleted file mode 100644 index c2bce1466..000000000 --- a/src/lib/nfs/patches/no_sprintf_s +++ /dev/null @@ -1,14 +0,0 @@ -Index: libnfs-libnfs-4.0.0/include/win32/win32_compat.h -=================================================================== ---- libnfs-libnfs-4.0.0.orig/include/win32/win32_compat.h -+++ libnfs-libnfs-4.0.0/include/win32/win32_compat.h -@@ -133,7 +133,9 @@ struct pollfd { - - /* Wrapper macros to call misc. functions win32 is missing */ - #define poll(x, y, z) win32_poll(x, y, z) -+#ifndef __MINGW32__ - #define snprintf sprintf_s -+#endif - #define inet_pton(x,y,z) win32_inet_pton(x,y,z) - #define open(x, y, z) _open(x, y, z) - #ifndef lseek diff --git a/src/lib/nfs/patches/series b/src/lib/nfs/patches/series deleted file mode 100644 index d2e2667c9..000000000 --- a/src/lib/nfs/patches/series +++ /dev/null @@ -1 +0,0 @@ -no_sprintf_s diff --git a/src/lib/xiph/meson.build b/src/lib/xiph/meson.build index 8786ac7e6..c39cee8d4 100644 --- a/src/lib/xiph/meson.build +++ b/src/lib/xiph/meson.build @@ -1,4 +1,11 @@ libflac_dep = dependency('flac', version: '>= 1.2', required: get_option('flac')) + +if is_windows + # Our Windows build generates a static libFLAC build + libflac_dep = declare_dependency(compile_args: '-DFLAC__NO_DLL', + dependencies: libflac_dep) +endif + libopus_dep = dependency('opus', required: get_option('opus')) if get_option('tremor').enabled() diff --git a/src/output/plugins/PipeWireOutputPlugin.cxx b/src/output/plugins/PipeWireOutputPlugin.cxx index 02f66cddb..bf5155a5b 100644 --- a/src/output/plugins/PipeWireOutputPlugin.cxx +++ b/src/output/plugins/PipeWireOutputPlugin.cxx @@ -336,6 +336,8 @@ PipeWireOutput::Enable() throw MakeErrno("pw_thread_loop_new() failed"); pw_thread_loop_start(thread_loop); + + stream = nullptr; } void diff --git a/src/output/plugins/wasapi/WasapiOutputPlugin.cxx b/src/output/plugins/wasapi/WasapiOutputPlugin.cxx index bedd9f756..992dc2e67 100644 --- a/src/output/plugins/wasapi/WasapiOutputPlugin.cxx +++ b/src/output/plugins/wasapi/WasapiOutputPlugin.cxx @@ -471,6 +471,16 @@ try { } UINT32 write_in_frames = buffer_size_in_frames; + DWORD mode = 0; + AtScopeExit(&) { + render_client->ReleaseBuffer(write_in_frames, mode); + + if (!started) { + Start(client); + started = true; + } + }; + if (!is_exclusive) { UINT32 data_in_frames = GetCurrentPaddingFrames(client); @@ -481,7 +491,6 @@ try { } BYTE *data; - DWORD mode = 0; if (HRESULT result = render_client->GetBuffer(write_in_frames, &data); @@ -489,15 +498,6 @@ try { throw MakeHResultError(result, "Failed to get buffer"); } - AtScopeExit(&) { - render_client->ReleaseBuffer(write_in_frames, mode); - - if (!started) { - Start(client); - started = true; - } - }; - const UINT32 write_size = write_in_frames * frame_size; UINT32 new_data_size = 0; new_data_size = spsc_buffer.pop(data, write_size);