From 258830e913a95d385d883322a0d60bfb99067f13 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sun, 21 May 2023 20:29:23 +0200
Subject: [PATCH 01/48] increment version number to 0.23.13

---
 NEWS        | 2 ++
 meson.build | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index b6322e829..dd8479049 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,5 @@
+ver 0.23.13 (not yet released)
+
 ver 0.23.12 (2023/01/17)
 * input
   - curl: require CURL 7.55.0 or later
diff --git a/meson.build b/meson.build
index ae8a7a14b..e9925ee39 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,7 @@
 project(
   'mpd',
   ['c', 'cpp'],
-  version: '0.23.12',
+  version: '0.23.13',
   meson_version: '>= 0.56.0',
   default_options: [
     'c_std=c11',

From 65b9b3195c211835ee509459e4d0e337bc93d496 Mon Sep 17 00:00:00 2001
From: Max Kellermann <mk@cm4all.com>
Date: Tue, 3 Jan 2023 16:08:12 +0100
Subject: [PATCH 02/48] lib/dbus/AppendIter: add missing include

---
 src/lib/dbus/AppendIter.hxx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/lib/dbus/AppendIter.hxx b/src/lib/dbus/AppendIter.hxx
index b3dc28747..d785274e0 100644
--- a/src/lib/dbus/AppendIter.hxx
+++ b/src/lib/dbus/AppendIter.hxx
@@ -36,6 +36,7 @@
 #include "Iter.hxx"
 #include "Values.hxx"
 
+#include <cstdint>
 #include <stdexcept>
 
 namespace ODBus {

From 1170fb1e1e0b99c9b8122fd71edb7b042500ce47 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 23 Jan 2023 14:06:51 +0100
Subject: [PATCH 03/48] output/osx: change type to std::size_t to fix
 -Wc++11-narrowing

---
 src/output/plugins/OSXOutputPlugin.cxx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx
index a02f3e9a4..c275d17e6 100644
--- a/src/output/plugins/OSXOutputPlugin.cxx
+++ b/src/output/plugins/OSXOutputPlugin.cxx
@@ -627,7 +627,7 @@ osx_render(void *vdata,
 {
 	OSXOutput *od = (OSXOutput *) vdata;
 
-	int count = in_number_frames * od->asbd.mBytesPerFrame;
+	std::size_t count = in_number_frames * od->asbd.mBytesPerFrame;
 	buffer_list->mBuffers[0].mDataByteSize =
 		od->ring_buffer->pop((uint8_t *)buffer_list->mBuffers[0].mData,
 				     count);

From d2797effa3bc660d008fad05587990bf07724939 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 23 Jan 2023 15:50:48 +0100
Subject: [PATCH 04/48] command/database: add missing include for UINT_MAX

---
 src/command/DatabaseCommands.cxx | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx
index e1b311022..160b7e4e6 100644
--- a/src/command/DatabaseCommands.cxx
+++ b/src/command/DatabaseCommands.cxx
@@ -41,6 +41,8 @@
 #include <memory>
 #include <vector>
 
+#include <limits.h> // for UINT_MAX
+
 CommandResult
 handle_listfiles_db(Client &client, Response &r, const char *uri)
 {

From 541468f0cabd5481e1e1574c4b8a990e93eb9ba1 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sat, 28 Jan 2023 07:41:47 +0100
Subject: [PATCH 05/48] input/async: check for errors in Seek()

Fixes a busy loop in BufferingInputStream::RunThreadLocked() because
the method never learns that seeking is ignored, even though the HTTP
stream is already broken and can never be read; nobody cared to check
for errors.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1727
---
 NEWS                           |  2 ++
 src/input/AsyncInputStream.cxx | 12 ++++++++++--
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index dd8479049..ac1429960 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,6 @@
 ver 0.23.13 (not yet released)
+* input
+  - curl: fix busy loop after connection failed
 
 ver 0.23.12 (2023/01/17)
 * input
diff --git a/src/input/AsyncInputStream.cxx b/src/input/AsyncInputStream.cxx
index 38ae566ee..4b21f3391 100644
--- a/src/input/AsyncInputStream.cxx
+++ b/src/input/AsyncInputStream.cxx
@@ -101,9 +101,17 @@ AsyncInputStream::Seek(std::unique_lock<Mutex> &lock,
 	assert(IsReady());
 	assert(seek_state == SeekState::NONE);
 
-	if (new_offset == offset)
-		/* no-op */
+	if (new_offset == offset) {
+		/* no-op, but if the stream is not open anymore (maybe
+		   because it has failed), nothing can be read, so we
+		   should check for errors here instead of pretending
+		   everything's fine */
+
+		if (!open)
+			Check();
+
 		return;
+	}
 
 	if (!IsSeekable())
 		throw std::runtime_error("Not seekable");

From c877a32d979a3971b238da2a2b5e634cbd37aa6d Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 07:53:34 +0100
Subject: [PATCH 06/48] python/build/libs.py: update OpenSSL to 3.0.8

---
 python/build/libs.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/python/build/libs.py b/python/build/libs.py
index c9cebf372..fef626476 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -387,8 +387,8 @@ ffmpeg = FfmpegProject(
 )
 
 openssl = OpenSSLProject(
-    'https://www.openssl.org/source/openssl-3.0.7.tar.gz',
-    '83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e',
+    'https://www.openssl.org/source/openssl-3.0.8.tar.gz',
+    '6c13d2bf38fdf31eac3ce2a347073673f5d63263398f1f69d0df4a41253e4b3e',
     'include/openssl/ossl_typ.h',
 )
 

From 8d2a1846588a43fc855b017ada9d03d01166c0fa Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 07:54:01 +0100
Subject: [PATCH 07/48] python/build/libs.py: update CURL to 7.88.1

---
 python/build/libs.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/python/build/libs.py b/python/build/libs.py
index fef626476..c60284c55 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -393,8 +393,8 @@ openssl = OpenSSLProject(
 )
 
 curl = CmakeProject(
-    'https://curl.se/download/curl-7.87.0.tar.xz',
-    'ee5f1a1955b0ed413435ef79db28b834ea5f0fb7c8cfb1ce47175cc3bee08fff',
+    'https://curl.se/download/curl-7.88.1.tar.xz',
+    '1dae31b2a7c1fe269de99c0c31bb488346aab3459b5ffca909d6938249ae415f',
     'lib/libcurl.a',
     [
         '-DBUILD_CURL_EXE=OFF',

From 949d72e36871835e191f4174ebdde351e27e4559 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Na=C3=AFm=20Favier?= <n@monade.li>
Date: Mon, 6 Mar 2023 10:03:14 +0100
Subject: [PATCH 08/48] output/PipeWire: lock thread loop in SendTag

---
 NEWS                                        | 2 ++
 src/output/plugins/PipeWireOutputPlugin.cxx | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/NEWS b/NEWS
index ac1429960..23e59ede8 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 ver 0.23.13 (not yet released)
 * input
   - curl: fix busy loop after connection failed
+* output
+  - pipewire: fix corruption bug due to missing lock
 
 ver 0.23.12 (2023/01/17)
 * input
diff --git a/src/output/plugins/PipeWireOutputPlugin.cxx b/src/output/plugins/PipeWireOutputPlugin.cxx
index aa5b0eb66..a97052f14 100644
--- a/src/output/plugins/PipeWireOutputPlugin.cxx
+++ b/src/output/plugins/PipeWireOutputPlugin.cxx
@@ -973,6 +973,8 @@ PipeWireOutput::SendTag(const Tag &tag)
 
 	struct spa_dict dict = SPA_DICT_INIT(items, n_items);
 
+	const PipeWire::ThreadLoopLock lock(thread_loop);
+
 	auto rc = pw_stream_update_properties(stream, &dict);
 	if (rc < 0)
 		LogWarning(pipewire_output_domain, "Error updating properties");

From e7c963f2ce6a2c79064af5a4a36960c4bf00c019 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 08:32:14 +0100
Subject: [PATCH 09/48] python/build/libs.py: disable more unused FFmpeg
 features

---
 python/build/libs.py | 215 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 214 insertions(+), 1 deletion(-)

diff --git a/python/build/libs.py b/python/build/libs.py
index c60284c55..0cc45b56e 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -176,13 +176,16 @@ ffmpeg = FfmpegProject(
         '--disable-pixelutils',
         '--disable-network',
         '--disable-encoders',
+        '--disable-hwaccels',
         '--disable-muxers',
         '--disable-protocols',
         '--disable-devices',
         '--disable-filters',
         '--disable-v4l2_m2m',
 
+        '--disable-sdl2',
         '--disable-vulkan',
+        '--disable-xlib',
 
         '--disable-parser=bmp',
         '--disable-parser=cavsvideo',
@@ -196,17 +199,22 @@ ffmpeg = FfmpegProject(
         '--disable-parser=h263',
         '--disable-parser=h264',
         '--disable-parser=hevc',
+        '--disable-parser=jpeg2000',
         '--disable-parser=mjpeg',
         '--disable-parser=mlp',
         '--disable-parser=mpeg4video',
         '--disable-parser=mpegvideo',
         '--disable-parser=opus',
+        '--disable-parser=qoi',
+        '--disable-parser=rv30',
+        '--disable-parser=rv40',
         '--disable-parser=vc1',
         '--disable-parser=vp3',
         '--disable-parser=vp8',
         '--disable-parser=vp9',
         '--disable-parser=png',
         '--disable-parser=pnm',
+        '--disable-parser=webp',
         '--disable-parser=xma',
 
         '--disable-demuxer=aqtitle',
@@ -222,6 +230,42 @@ ffmpeg = FfmpegProject(
         '--disable-demuxer=h264',
         '--disable-demuxer=ico',
         '--disable-demuxer=image2',
+        '--disable-demuxer=image2pipe',
+        '--disable-demuxer=image_bmp_pipe',
+        '--disable-demuxer=image_cri_pipe',
+        '--disable-demuxer=image_dds_pipe',
+        '--disable-demuxer=image_dpx_pipe',
+        '--disable-demuxer=image_exr_pipe',
+        '--disable-demuxer=image_gem_pipe',
+        '--disable-demuxer=image_gif_pipe',
+        '--disable-demuxer=image_j2k_pipe',
+        '--disable-demuxer=image_jpeg_pipe',
+        '--disable-demuxer=image_jpegls_pipe',
+        '--disable-demuxer=image_jpegxl_pipe',
+        '--disable-demuxer=image_pam_pipe',
+        '--disable-demuxer=image_pbm_pipe',
+        '--disable-demuxer=image_pcx_pipe',
+        '--disable-demuxer=image_pfm_pipe',
+        '--disable-demuxer=image_pgm_pipe',
+        '--disable-demuxer=image_pgmyuv_pipe',
+        '--disable-demuxer=image_pgx_pipe',
+        '--disable-demuxer=image_phm_pipe',
+        '--disable-demuxer=image_photocd_pipe',
+        '--disable-demuxer=image_pictor_pipe',
+        '--disable-demuxer=image_png_pipe',
+        '--disable-demuxer=image_ppm_pipe',
+        '--disable-demuxer=image_psd_pipe',
+        '--disable-demuxer=image_qdraw_pipe',
+        '--disable-demuxer=image_qoi_pipe',
+        '--disable-demuxer=image_sgi_pipe',
+        '--disable-demuxer=image_sunrast_pipe',
+        '--disable-demuxer=image_svg_pipe',
+        '--disable-demuxer=image_tiff_pipe',
+        '--disable-demuxer=image_vbn_pipe',
+        '--disable-demuxer=image_webp_pipe',
+        '--disable-demuxer=image_xbm_pipe',
+        '--disable-demuxer=image_xpm_pipe',
+        '--disable-demuxer=image_xwd_pipe',
         '--disable-demuxer=jacosub',
         '--disable-demuxer=lrc',
         '--disable-demuxer=microdvd',
@@ -244,6 +288,7 @@ ffmpeg = FfmpegProject(
         '--disable-demuxer=tedcaptions',
         '--disable-demuxer=vobsub',
         '--disable-demuxer=vplayer',
+        '--disable-demuxer=webm_dash_manifest',
         '--disable-demuxer=webvtt',
         '--disable-demuxer=yuv4mpegpipe',
 
@@ -273,78 +318,179 @@ ffmpeg = FfmpegProject(
         '--disable-decoder=qdmc',
 
         # disable lots of image and video codecs
+        '--disable-decoder=acelp_kelvin',
+        '--disable-decoder=agm',
+        '--disable-decoder=aic',
+        '--disable-decoder=alias_pix',
+        '--disable-decoder=ansi',
+        '--disable-decoder=apng',
+        '--disable-decoder=arbc',
+        '--disable-decoder=argo',
         '--disable-decoder=ass',
         '--disable-decoder=asv1',
         '--disable-decoder=asv2',
-        '--disable-decoder=apng',
+        '--disable-decoder=aura',
+        '--disable-decoder=aura2',
         '--disable-decoder=avrn',
         '--disable-decoder=avrp',
+        '--disable-decoder=avui',
+        '--disable-decoder=ayuv',
         '--disable-decoder=bethsoftvid',
+        '--disable-decoder=bfi',
         '--disable-decoder=bink',
+        '--disable-decoder=bintext',
+        '--disable-decoder=bitpacked',
         '--disable-decoder=bmp',
         '--disable-decoder=bmv_video',
+        '--disable-decoder=brender_pix',
+        '--disable-decoder=c93',
         '--disable-decoder=cavs',
         '--disable-decoder=ccaption',
         '--disable-decoder=cdgraphics',
+        '--disable-decoder=cdtoons',
+        '--disable-decoder=cdxl',
+        '--disable-decoder=cfhd',
+        '--disable-decoder=cinepak',
         '--disable-decoder=clearvideo',
+        '--disable-decoder=cljr',
+        '--disable-decoder=cllc',
+        '--disable-decoder=cpia',
+        '--disable-decoder=cscd',
+        '--disable-decoder=cyuv',
+        '--disable-decoder=dds',
         '--disable-decoder=dirac',
+        '--disable-decoder=dnxhd',
+        '--disable-decoder=dpx',
         '--disable-decoder=dsicinvideo',
         '--disable-decoder=dvbsub',
         '--disable-decoder=dvdsub',
         '--disable-decoder=dvvideo',
+        '--disable-decoder=dxa',
+        '--disable-decoder=dxtory',
+        '--disable-decoder=dxv',
+        '--disable-decoder=eacmv',
+        '--disable-decoder=eamad',
+        '--disable-decoder=eatgq',
+        '--disable-decoder=eatgv',
+        '--disable-decoder=eatqi',
+        '--disable-decoder=eightbps',
+        '--disable-decoder=escape124',
+        '--disable-decoder=escape130',
         '--disable-decoder=exr',
         '--disable-decoder=ffv1',
         '--disable-decoder=ffvhuff',
         '--disable-decoder=ffwavesynth',
+        '--disable-decoder=fic',
+        '--disable-decoder=fits',
+        '--disable-decoder=flashsv',
+        '--disable-decoder=flashsv2',
         '--disable-decoder=flic',
         '--disable-decoder=flv',
+        '--disable-decoder=fmvc',
         '--disable-decoder=fraps',
+        '--disable-decoder=fourxm',
+        '--disable-decoder=frwu',
+        '--disable-decoder=g2m',
+        '--disable-decoder=gdv',
+        '--disable-decoder=gem',
         '--disable-decoder=gif',
         '--disable-decoder=h261',
         '--disable-decoder=h263',
         '--disable-decoder=h263i',
         '--disable-decoder=h263p',
         '--disable-decoder=h264',
+        '--disable-decoder=hap',
         '--disable-decoder=hevc',
         '--disable-decoder=hnm4_video',
         '--disable-decoder=hq_hqa',
         '--disable-decoder=hqx',
+        '--disable-decoder=huffyuv',
+        '--disable-decoder=hymt',
         '--disable-decoder=idcin',
+        '--disable-decoder=idf',
         '--disable-decoder=iff_ilbm',
+        '--disable-decoder=imm4',
         '--disable-decoder=indeo2',
         '--disable-decoder=indeo3',
         '--disable-decoder=indeo4',
         '--disable-decoder=indeo5',
         '--disable-decoder=interplay_video',
+        '--disable-decoder=ipu',
         '--disable-decoder=jacosub',
         '--disable-decoder=jpeg2000',
         '--disable-decoder=jpegls',
+        '--disable-decoder=jv',
+        '--disable-decoder=kgv1',
+        '--disable-decoder=kmvc',
+        '--disable-decoder=lagarith',
+        '--disable-decoder=loco',
+        '--disable-decoder=lscr',
+        '--disable-decoder=m101',
+        '--disable-decoder=magicyuv',
+        '--disable-decoder=mdec',
         '--disable-decoder=microdvd',
         '--disable-decoder=mimic',
         '--disable-decoder=mjpeg',
         '--disable-decoder=mmvideo',
         '--disable-decoder=mpl2',
+        '--disable-decoder=mobiclip',
         '--disable-decoder=motionpixels',
+        '--disable-decoder=movtext',
         '--disable-decoder=mpeg1video',
         '--disable-decoder=mpeg2video',
         '--disable-decoder=mpeg4',
         '--disable-decoder=mpegvideo',
+        '--disable-decoder=msa1',
         '--disable-decoder=mscc',
         '--disable-decoder=msmpeg4_crystalhd',
         '--disable-decoder=msmpeg4v1',
         '--disable-decoder=msmpeg4v2',
         '--disable-decoder=msmpeg4v3',
+        '--disable-decoder=msp2',
+        '--disable-decoder=msrle',
+        '--disable-decoder=mss1',
         '--disable-decoder=msvideo1',
         '--disable-decoder=mszh',
+        '--disable-decoder=mts2',
+        '--disable-decoder=mv30',
         '--disable-decoder=mvc1',
         '--disable-decoder=mvc2',
+        '--disable-decoder=mvdv',
+        '--disable-decoder=mvha',
+        '--disable-decoder=mwsc',
+        '--disable-decoder=notchlc',
+        '--disable-decoder=nuv',
         '--disable-decoder=on2avc',
         '--disable-decoder=paf_video',
+        '--disable-decoder=pam',
+        '--disable-decoder=pbm',
+        '--disable-decoder=pcx',
+        '--disable-decoder=pgm',
+        '--disable-decoder=pgmyuv',
+        '--disable-decoder=pgssub',
+        '--disable-decoder=pgx',
+        '--disable-decoder=phm',
+        '--disable-decoder=photocd',
         '--disable-decoder=png',
+        '--disable-decoder=pictor',
+        '--disable-decoder=pixlet',
+        '--disable-decoder=pjs',
+        '--disable-decoder=ppm',
+        '--disable-decoder=prores',
+        '--disable-decoder=prosumer',
+        '--disable-decoder=psd',
+        '--disable-decoder=ptx',
         '--disable-decoder=qdraw',
+        '--disable-decoder=qoi',
         '--disable-decoder=qpeg',
+        '--disable-decoder=qtrle',
         '--disable-decoder=rawvideo',
+        '--disable-decoder=r10k',
+        '--disable-decoder=r210',
+        '--disable-decoder=rasc',
         '--disable-decoder=realtext',
+        '--disable-decoder=rl2',
+        '--disable-decoder=rpza',
         '--disable-decoder=roq',
         '--disable-decoder=roq_dpcm',
         '--disable-decoder=rscc',
@@ -353,36 +499,103 @@ ffmpeg = FfmpegProject(
         '--disable-decoder=rv30',
         '--disable-decoder=rv40',
         '--disable-decoder=sami',
+        '--disable-decoder=sanm',
+        '--disable-decoder=scpr',
+        '--disable-decoder=screenpresso',
+        '--disable-decoder=sga',
+        '--disable-decoder=sgi',
+        '--disable-decoder=sgirle',
         '--disable-decoder=sheervideo',
+        '--disable-decoder=simbiosis_imx',
+        '--disable-decoder=smc',
         '--disable-decoder=snow',
+        '--disable-decoder=speedhq',
+        '--disable-decoder=srgc',
         '--disable-decoder=srt',
+        '--disable-decoder=ssa',
         '--disable-decoder=stl',
         '--disable-decoder=subrip',
         '--disable-decoder=subviewer',
         '--disable-decoder=subviewer1',
+        '--disable-decoder=sunrast',
         '--disable-decoder=svq1',
         '--disable-decoder=svq3',
+        '--disable-decoder=targa',
+        '--disable-decoder=targa_y216',
+        '--disable-decoder=text',
         '--disable-decoder=tiff',
         '--disable-decoder=tiertexseqvideo',
+        '--disable-decoder=tmv',
         '--disable-decoder=truemotion1',
         '--disable-decoder=truemotion2',
         '--disable-decoder=truemotion2rt',
+        '--disable-decoder=tscc',
+        '--disable-decoder=tscc2',
         '--disable-decoder=twinvq',
+        '--disable-decoder=txd',
+        '--disable-decoder=ulti',
         '--disable-decoder=utvideo',
+        '--disable-decoder=v210',
+        '--disable-decoder=v210x',
+        '--disable-decoder=v308',
+        '--disable-decoder=v408',
+        '--disable-decoder=v410',
+        '--disable-decoder=vb',
+        '--disable-decoder=vble',
+        '--disable-decoder=vbn',
         '--disable-decoder=vc1',
+        '--disable-decoder=vcr1',
         '--disable-decoder=vmdvideo',
+        '--disable-decoder=vmnc',
         '--disable-decoder=vp3',
         '--disable-decoder=vp5',
         '--disable-decoder=vp6',
         '--disable-decoder=vp7',
         '--disable-decoder=vp8',
         '--disable-decoder=vp9',
+        '--disable-decoder=vplayer',
         '--disable-decoder=vqa',
         '--disable-decoder=webvtt',
+        '--disable-decoder=wcmv',
         '--disable-decoder=wmv1',
         '--disable-decoder=wmv2',
         '--disable-decoder=wmv3',
+        '--disable-decoder=wnv1',
+        '--disable-decoder=wrapped_avframe',
+        '--disable-decoder=xan_wc3',
+        '--disable-decoder=xan_wc4',
+        '--disable-decoder=xbin',
+        '--disable-decoder=xbm',
+        '--disable-decoder=xface',
+        '--disable-decoder=xl',
+        '--disable-decoder=xpm',
+        '--disable-decoder=xsub',
+        '--disable-decoder=xwd',
+        '--disable-decoder=y41p',
+        '--disable-decoder=ylc',
+        '--disable-decoder=yop',
         '--disable-decoder=yuv4',
+        '--disable-decoder=zero12v',
+        '--disable-decoder=zerocodec',
+        '--disable-decoder=zlib',
+        '--disable-decoder=zmbv',
+
+        '--disable-bsf=av1_frame_merge',
+        '--disable-bsf=av1_frame_split',
+        '--disable-bsf=av1_metadata',
+        '--disable-bsf=h264_metadata',
+        '--disable-bsf=h264_mp4toannexb',
+        '--disable-bsf=h264_redundant_pps',
+        '--disable-bsf=hevc_metadata',
+        '--disable-bsf=hevc_mp4toannexb',
+        '--disable-bsf=mjpeg2jpeg',
+        '--disable-bsf=opus_metadata',
+        '--disable-bsf=pgs_frame_merge',
+        '--disable-bsf=text2movsub',
+        '--disable-bsf=vp9_metadata',
+        '--disable-bsf=vp9_raw_reorder',
+        '--disable-bsf=vp9_superframe',
+        '--disable-bsf=vp9_superframe_split',
     ],
 )
 

From 35eaed7206a52c5a3743ef684b061fa711594ecc Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 07:52:59 +0100
Subject: [PATCH 10/48] python/build/libs.py: update FFmpeg to 6.0

---
 python/build/libs.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/python/build/libs.py b/python/build/libs.py
index 0cc45b56e..8399fd98e 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -157,8 +157,8 @@ gme = CmakeProject(
 )
 
 ffmpeg = FfmpegProject(
-    'http://ffmpeg.org/releases/ffmpeg-5.1.2.tar.xz',
-    '619e706d662c8420859832ddc259cd4d4096a48a2ce1eefd052db9e440eef3dc',
+    'http://ffmpeg.org/releases/ffmpeg-6.0.tar.xz',
+    '57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082',
     'lib/libavcodec.a',
     [
         '--disable-shared', '--enable-static',
@@ -583,6 +583,7 @@ ffmpeg = FfmpegProject(
         '--disable-bsf=av1_frame_merge',
         '--disable-bsf=av1_frame_split',
         '--disable-bsf=av1_metadata',
+        '--disable-bsf=dts2pts',
         '--disable-bsf=h264_metadata',
         '--disable-bsf=h264_mp4toannexb',
         '--disable-bsf=h264_redundant_pps',

From 6e700dab69039caefcb6dff87db60480e9cbaee3 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 12:57:42 +0100
Subject: [PATCH 11/48] CommandLine: hard-code `daemon=false` if ENABLE_DAEMON
 is not set

---
 src/CommandLine.cxx | 4 ++++
 src/CommandLine.hxx | 7 +++++++
 2 files changed, 11 insertions(+)

diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx
index 11827010a..b606a7568 100644
--- a/src/CommandLine.cxx
+++ b/src/CommandLine.cxx
@@ -352,12 +352,16 @@ ParseCommandLine(int argc, char **argv, CommandLineOptions &options,
 			break;
 
 		case OPTION_NO_DAEMON:
+#ifdef ENABLE_DAEMON
 			options.daemon = false;
+#endif
 			break;
 
 #ifdef __linux__
 		case OPTION_SYSTEMD:
+#ifdef ENABLE_DAEMON
 			options.daemon = false;
+#endif
 			options.systemd = true;
 			break;
 #endif
diff --git a/src/CommandLine.hxx b/src/CommandLine.hxx
index 3f4f2c78e..fe9d6c511 100644
--- a/src/CommandLine.hxx
+++ b/src/CommandLine.hxx
@@ -20,11 +20,18 @@
 #ifndef MPD_COMMAND_LINE_HXX
 #define MPD_COMMAND_LINE_HXX
 
+#include "config.h" // for ENABLE_DAEMON
+
 struct ConfigData;
 
 struct CommandLineOptions {
 	bool kill = false;
+
+#ifdef ENABLE_DAEMON
 	bool daemon = true;
+#else
+	static constexpr bool daemon = false;
+#endif
 
 #ifdef __linux__
 	bool systemd = false;

From a0f6932ebe21ff6908db250019bdb46f7b0dfedc Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 12:53:19 +0100
Subject: [PATCH 12/48] unix/SignalHandlers: shut down if parent process dies
 in --no-daemon mode

By default, if the parent of a process dies, the process gets SIGHUP
and is supposed to shut down.  This however doesn't work for MPD,
because MPD redefines SIGHUP with a different meaning (like most
daemons do).

To work around this, we configure the kernel to send SIGTERM instead
of SIGHUP.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1706
---
 NEWS                        |  2 ++
 src/Main.cxx                |  5 ++++-
 src/unix/SignalHandlers.cxx | 14 +++++++++++++-
 src/unix/SignalHandlers.hxx |  6 +++---
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index 23e59ede8..5cb84ea24 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,8 @@ ver 0.23.13 (not yet released)
   - curl: fix busy loop after connection failed
 * output
   - pipewire: fix corruption bug due to missing lock
+* Linux
+  - shut down if parent process dies in --no-daemon mode
 
 ver 0.23.12 (2023/01/17)
 * input
diff --git a/src/Main.cxx b/src/Main.cxx
index bcbda0bb5..6b9bdf641 100644
--- a/src/Main.cxx
+++ b/src/Main.cxx
@@ -482,7 +482,10 @@ MainConfigured(const CommandLineOptions &options,
 #ifndef ANDROID
 	setup_log_output();
 
-	const ScopeSignalHandlersInit signal_handlers_init(instance);
+	const ScopeSignalHandlersInit signal_handlers_init{
+		instance,
+		options.daemon,
+	};
 #endif
 
 	instance.io_thread.Start();
diff --git a/src/unix/SignalHandlers.cxx b/src/unix/SignalHandlers.cxx
index 886dc78d5..e58dda7de 100644
--- a/src/unix/SignalHandlers.cxx
+++ b/src/unix/SignalHandlers.cxx
@@ -30,6 +30,10 @@
 
 #include <csignal>
 
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
 static constexpr Domain signal_handlers_domain("signal_handlers");
 
 static void
@@ -60,7 +64,7 @@ handle_reload_event(void *ctx) noexcept
 #endif
 
 void
-SignalHandlersInit(Instance &instance)
+SignalHandlersInit(Instance &instance, bool daemon)
 {
 	auto &loop = instance.event_loop;
 
@@ -79,6 +83,14 @@ SignalHandlersInit(Instance &instance)
 
 	SignalMonitorRegister(SIGHUP, {&instance, handle_reload_event});
 #endif
+
+	if (!daemon) {
+#ifdef __linux__
+		/* if MPD was not daemonized, shut it down when the
+		   parent process dies */
+		prctl(PR_SET_PDEATHSIG, SIGTERM);
+#endif
+	}
 }
 
 void
diff --git a/src/unix/SignalHandlers.hxx b/src/unix/SignalHandlers.hxx
index dbcec12d6..1eb9a305d 100644
--- a/src/unix/SignalHandlers.hxx
+++ b/src/unix/SignalHandlers.hxx
@@ -23,15 +23,15 @@
 struct Instance;
 
 void
-SignalHandlersInit(Instance &instance);
+SignalHandlersInit(Instance &instance, bool daemon);
 
 void
 SignalHandlersFinish() noexcept;
 
 class ScopeSignalHandlersInit {
 public:
-	ScopeSignalHandlersInit(Instance &instance) {
-		SignalHandlersInit(instance);
+	ScopeSignalHandlersInit(Instance &instance, bool daemon) {
+		SignalHandlersInit(instance, daemon);
 	}
 
 	~ScopeSignalHandlersInit() noexcept {

From 52e4a4c90404518ffbe9b5dd3469f2008269149f Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 3 May 2021 20:42:50 +0200
Subject: [PATCH 13/48] build/python/build/project.py: lazy tarball extraction

---
 python/build/project.py | 4 ++--
 python/build/tar.py     | 4 +++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/python/build/project.py b/python/build/project.py
index a1ea28bc6..183d87675 100644
--- a/python/build/project.py
+++ b/python/build/project.py
@@ -55,8 +55,8 @@ class Project:
             parent_path = toolchain.src_path
         else:
             parent_path = toolchain.build_path
-        path = untar(self.download(toolchain), parent_path, self.base)
-
+        path = untar(self.download(toolchain), parent_path, self.base,
+                     lazy=out_of_tree and self.patches is None)
         if self.patches is not None:
             push_all(toolchain, path, self.patches)
 
diff --git a/python/build/tar.py b/python/build/tar.py
index 7cb7db67c..e73ffdefa 100644
--- a/python/build/tar.py
+++ b/python/build/tar.py
@@ -1,7 +1,9 @@
 import os, shutil, subprocess
 
-def untar(tarball_path, parent_path, base):
+def untar(tarball_path, parent_path, base, lazy=False):
     path = os.path.join(parent_path, base)
+    if lazy and os.path.isdir(path):
+        return path
     try:
         shutil.rmtree(path)
     except FileNotFoundError:

From 4ab8a677dc0f6791c4a92d2cb2a38f4b133a391a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20H=C3=A4dicke?= <felixhaedicke@web.de>
Date: Mon, 30 Nov 2015 22:55:46 +0100
Subject: [PATCH 14/48] build/python: do not use absolute path for tar

---
 python/build/tar.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/python/build/tar.py b/python/build/tar.py
index e73ffdefa..8247d2c22 100644
--- a/python/build/tar.py
+++ b/python/build/tar.py
@@ -10,7 +10,7 @@ def untar(tarball_path, parent_path, base, lazy=False):
         pass
     os.makedirs(parent_path, exist_ok=True)
     try:
-        subprocess.check_call(['/bin/tar', 'xfC', tarball_path, parent_path])
+        subprocess.check_call(['tar', 'xfC', tarball_path, parent_path])
     except FileNotFoundError:
         import tarfile
         tar = tarfile.open(tarball_path)

From 302432e157e0d58d5a6af4824a202c6c56c397af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20H=C3=A4dicke?= <felixhaedicke@web.de>
Date: Sat, 30 Sep 2017 00:10:24 +0200
Subject: [PATCH 15/48] python/makeproject: set appropriate build jobs count
 depending on the number of CPUs

---
 python/build/makeproject.py | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/python/build/makeproject.py b/python/build/makeproject.py
index 667a10243..9ec95e27f 100644
--- a/python/build/makeproject.py
+++ b/python/build/makeproject.py
@@ -1,4 +1,4 @@
-import subprocess
+import subprocess, multiprocessing
 
 from build.project import Project
 
@@ -10,7 +10,12 @@ class MakeProject(Project):
         self.install_target = install_target
 
     def get_simultaneous_jobs(self):
-        return 12
+        try:
+            # use twice as many simultaneous jobs as we have CPU cores
+            return multiprocessing.cpu_count() * 2
+        except NotImplementedError:
+            # default to 12, if multiprocessing.cpu_count() is not implemented
+            return 12
 
     def get_make_args(self, toolchain):
         return ['--quiet', '-j' + str(self.get_simultaneous_jobs())]

From 70d0fbd715119c52c127f8a088623b292a2bdbad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20H=C3=A4dicke?= <felixhaedicke@web.de>
Date: Tue, 10 Oct 2017 00:08:14 +0200
Subject: [PATCH 16/48] python/makeproject: do not use hard-code absolute path
 to make

---
 python/build/makeproject.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/python/build/makeproject.py b/python/build/makeproject.py
index 9ec95e27f..9d498cfbd 100644
--- a/python/build/makeproject.py
+++ b/python/build/makeproject.py
@@ -24,7 +24,7 @@ class MakeProject(Project):
         return ['--quiet', self.install_target]
 
     def make(self, toolchain, wd, args):
-        subprocess.check_call(['/usr/bin/make'] + args,
+        subprocess.check_call(['make'] + args,
                               cwd=wd, env=toolchain.env)
 
     def build_make(self, toolchain, wd, install=True):

From d32ed194e85d567d298c994e90cc5a17516cb487 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Fri, 13 Aug 2021 18:52:00 +0200
Subject: [PATCH 17/48] python/build/autotools.py: dump config.log on configure
 error

For better error logs on CI.
---
 python/build/autotools.py | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/python/build/autotools.py b/python/build/autotools.py
index b6421dbaf..6cd484f3a 100644
--- a/python/build/autotools.py
+++ b/python/build/autotools.py
@@ -52,7 +52,18 @@ class AutotoolsProject(MakeProject):
             '--enable-silent-rules',
         ] + self.configure_args
 
-        subprocess.check_call(configure, cwd=build, env=toolchain.env)
+        try:
+            subprocess.check_call(configure, cwd=build, env=toolchain.env)
+        except subprocess.CalledProcessError:
+            # dump config.log after a failed configure run
+            try:
+                with open(os.path.join(build, 'config.log')) as f:
+                    sys.stdout.write(f.read())
+            except:
+                pass
+            # re-raise the exception
+            raise
+
         return build
 
     def _build(self, toolchain):

From 10aec174d535214e3e340666abca71cfd3617fd3 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 8 Aug 2022 10:05:30 +0200
Subject: [PATCH 18/48] python/{cmake,autotools}: build in verbose mode

Make sure all the gory details are visible in CI logs.
---
 python/build/autotools.py | 3 ++-
 python/build/cmake.py     | 3 ++-
 python/build/meson.py     | 2 +-
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/python/build/autotools.py b/python/build/autotools.py
index 6cd484f3a..cf9125439 100644
--- a/python/build/autotools.py
+++ b/python/build/autotools.py
@@ -49,10 +49,11 @@ class AutotoolsProject(MakeProject):
             'STRIP=' + toolchain.strip,
             '--host=' + toolchain.arch,
             '--prefix=' + toolchain.install_prefix,
-            '--enable-silent-rules',
+            '--disable-silent-rules',
         ] + self.configure_args
 
         try:
+            print(configure)
             subprocess.check_call(configure, cwd=build, env=toolchain.env)
         except subprocess.CalledProcessError:
             # dump config.log after a failed configure run
diff --git a/python/build/cmake.py b/python/build/cmake.py
index 158bf6d76..3f9fd2946 100644
--- a/python/build/cmake.py
+++ b/python/build/cmake.py
@@ -61,6 +61,7 @@ def configure(toolchain, src, build, args=()):
         '-GNinja',
     ] + cross_args + args
 
+    print(configure)
     subprocess.check_call(configure, env=toolchain.env, cwd=build)
 
 class CmakeProject(Project):
@@ -82,5 +83,5 @@ class CmakeProject(Project):
 
     def _build(self, toolchain):
         build = self.configure(toolchain)
-        subprocess.check_call(['ninja', 'install'],
+        subprocess.check_call(['ninja', '-v', 'install'],
                               cwd=build, env=toolchain.env)
diff --git a/python/build/meson.py b/python/build/meson.py
index d86c48e6a..4c1a5babf 100644
--- a/python/build/meson.py
+++ b/python/build/meson.py
@@ -115,5 +115,5 @@ class MesonProject(Project):
 
     def _build(self, toolchain):
         build = self.configure(toolchain)
-        subprocess.check_call(['ninja', 'install'],
+        subprocess.check_call(['ninja', '-v', 'install'],
                               cwd=build, env=toolchain.env)

From b09a54b2c264a589af2e892e23cd352161e03f5c Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 13:25:43 +0100
Subject: [PATCH 19/48] python/build/autotools.py: use toolchain.arflags

---
 android/build.py          | 1 +
 python/build/autotools.py | 1 +
 win32/build.py            | 1 +
 3 files changed, 3 insertions(+)

diff --git a/android/build.py b/android/build.py
index aa4f8b193..509cf85a4 100755
--- a/android/build.py
+++ b/android/build.py
@@ -100,6 +100,7 @@ class AndroidNdkToolchain:
         common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
 
         self.ar = os.path.join(llvm_bin, 'llvm-ar')
+        self.arflags = 'rcs'
         self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib')
         self.nm = os.path.join(llvm_bin, 'llvm-nm')
         self.strip = os.path.join(llvm_bin, 'llvm-strip')
diff --git a/python/build/autotools.py b/python/build/autotools.py
index cf9125439..46fbd273b 100644
--- a/python/build/autotools.py
+++ b/python/build/autotools.py
@@ -45,6 +45,7 @@ class AutotoolsProject(MakeProject):
             'LDFLAGS=' + toolchain.ldflags + ' ' + self.ldflags,
             'LIBS=' + toolchain.libs + ' ' + self.libs,
             'AR=' + toolchain.ar,
+            'ARFLAGS=' + toolchain.arflags,
             'RANLIB=' + toolchain.ranlib,
             'STRIP=' + toolchain.strip,
             '--host=' + toolchain.arch,
diff --git a/win32/build.py b/win32/build.py
index f29d75302..494969792 100755
--- a/win32/build.py
+++ b/win32/build.py
@@ -48,6 +48,7 @@ class CrossGccToolchain:
         self.cc = os.path.join(toolchain_bin, arch + '-gcc')
         self.cxx = os.path.join(toolchain_bin, arch + '-g++')
         self.ar = os.path.join(toolchain_bin, arch + '-ar')
+        self.arflags = 'rcs'
         self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
         self.nm = os.path.join(toolchain_bin, arch + '-nm')
         self.strip = os.path.join(toolchain_bin, arch + '-strip')

From 90eaa87a4d801d6ae75095a55c6a8cecfc2812a1 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 13:14:24 +0100
Subject: [PATCH 20/48] python/build/zlib.py: use autotools to be more portable

Right now, zlib is only built for Windows, but we may eventually
changed that, so don't hard-code `win32/Makefile.gcc`.
---
 python/build/zlib.py | 38 ++++++++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 14 deletions(-)

diff --git a/python/build/zlib.py b/python/build/zlib.py
index 0747a6d0d..c5aeb8c30 100644
--- a/python/build/zlib.py
+++ b/python/build/zlib.py
@@ -1,22 +1,32 @@
-import os.path, subprocess
+import subprocess
 
-from build.project import Project
+from build.makeproject import MakeProject
 
-class ZlibProject(Project):
+class ZlibProject(MakeProject):
     def __init__(self, url, md5, installed,
                  **kwargs):
-        Project.__init__(self, url, md5, installed, **kwargs)
+        MakeProject.__init__(self, url, md5, installed, **kwargs)
+
+    def get_make_args(self, toolchain):
+        return MakeProject.get_make_args(self, toolchain) + [
+            'CC=' + toolchain.cc + ' ' + toolchain.cppflags + ' ' + toolchain.cflags,
+            'CPP=' + toolchain.cc + ' -E ' + toolchain.cppflags,
+            'AR=' + toolchain.ar,
+            'ARFLAGS=' + toolchain.arflags,
+            'RANLIB=' + toolchain.ranlib,
+            'LDSHARED=' + toolchain.cc + ' -shared',
+            'libz.a'
+        ]
+
+    def get_make_install_args(self, toolchain):
+        return [
+            'RANLIB=' + toolchain.ranlib,
+            self.install_target
+        ]
 
     def _build(self, toolchain):
         src = self.unpack(toolchain, out_of_tree=False)
 
-        subprocess.check_call(['/usr/bin/make', '--quiet',
-            '-f', 'win32/Makefile.gcc',
-            'PREFIX=' + toolchain.arch + '-',
-            '-j12',
-            'install',
-            'INCLUDE_PATH='+ os.path.join(toolchain.install_prefix, 'include'),
-            'LIBRARY_PATH=' + os.path.join(toolchain.install_prefix, 'lib'),
-            'BINARY_PATH=' + os.path.join(toolchain.install_prefix, 'bin'),
-            ],
-            cwd=src, env=toolchain.env)
+        subprocess.check_call(['./configure', '--prefix=' + toolchain.install_prefix, '--static'],
+                              cwd=src, env=toolchain.env)
+        self.build_make(toolchain, src)

From 47341107eadc35adf95a34acc866051889a99b5e Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 3 May 2021 14:53:56 +0200
Subject: [PATCH 21/48] build/python/build/project.py: raise exception on regex
 mismatch

---
 python/build/project.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/python/build/project.py b/python/build/project.py
index 183d87675..2b8c14723 100644
--- a/python/build/project.py
+++ b/python/build/project.py
@@ -14,13 +14,14 @@ class Project:
         if base is None:
             basename = os.path.basename(url)
             m = re.match(r'^(.+)\.(tar(\.(gz|bz2|xz|lzma))?|zip)$', basename)
-            if not m: raise
+            if not m: raise RuntimeError('Could not identify tarball name: ' + basename)
             self.base = m.group(1)
         else:
             self.base = base
 
         if name is None or version is None:
             m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-(?:alpha|beta)\d+)?)(\+.*)?$', self.base)
+            if not m: raise RuntimeError('Could not identify tarball name: ' + self.base)
             if name is None: name = m.group(1)
             if version is None: version = m.group(2)
 

From d5d3982d3c477780916e18af944e3e56210822db Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Tue, 4 May 2021 14:57:46 +0200
Subject: [PATCH 22/48] build/python/build/project.py: add "lazy" parameter to
 make_build_path()

---
 python/build/project.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/python/build/project.py b/python/build/project.py
index 2b8c14723..bf787cf43 100644
--- a/python/build/project.py
+++ b/python/build/project.py
@@ -72,8 +72,10 @@ class Project:
 
         return path
 
-    def make_build_path(self, toolchain):
+    def make_build_path(self, toolchain, lazy=False):
         path = os.path.join(toolchain.build_path, self.base)
+        if lazy and os.path.isdir(path):
+            return path
         try:
             shutil.rmtree(path)
         except FileNotFoundError:

From f8cfeb39e92af57c47ab7ee288e84184f1f417cb Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 22 Aug 2022 10:31:14 +0200
Subject: [PATCH 23/48] build/python/cmake: add "env" parameter

---
 python/build/cmake.py | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/python/build/cmake.py b/python/build/cmake.py
index 3f9fd2946..9017efd56 100644
--- a/python/build/cmake.py
+++ b/python/build/cmake.py
@@ -31,7 +31,7 @@ set(CMAKE_CXX_FLAGS "{toolchain.cxxflags} {toolchain.cppflags}")
     __write_cmake_compiler(f, 'C', toolchain.cc)
     __write_cmake_compiler(f, 'CXX', toolchain.cxx)
 
-def configure(toolchain, src, build, args=()):
+def configure(toolchain, src, build, args=(), env=None):
     cross_args = []
 
     if toolchain.is_windows:
@@ -61,16 +61,23 @@ def configure(toolchain, src, build, args=()):
         '-GNinja',
     ] + cross_args + args
 
+    if env is None:
+        env = toolchain.env
+    else:
+        env = {**toolchain.env, **env}
+
     print(configure)
-    subprocess.check_call(configure, env=toolchain.env, cwd=build)
+    subprocess.check_call(configure, env=env, cwd=build)
 
 class CmakeProject(Project):
     def __init__(self, url, md5, installed, configure_args=[],
                  windows_configure_args=[],
+                 env=None,
                  **kwargs):
         Project.__init__(self, url, md5, installed, **kwargs)
         self.configure_args = configure_args
         self.windows_configure_args = windows_configure_args
+        self.env = env
 
     def configure(self, toolchain):
         src = self.unpack(toolchain)
@@ -78,7 +85,7 @@ class CmakeProject(Project):
         configure_args = self.configure_args
         if toolchain.is_windows:
             configure_args = configure_args + self.windows_configure_args
-        configure(toolchain, src, build, configure_args)
+        configure(toolchain, src, build, configure_args, self.env)
         return build
 
     def _build(self, toolchain):

From d105985d78e5246692156fc9e2850e37b643ff90 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 8 Aug 2022 10:51:23 +0200
Subject: [PATCH 24/48] build/python/cmake: set CMAKE_OSX_SYSROOT on macOS

---
 python/build/cmake.py | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/python/build/cmake.py b/python/build/cmake.py
index 9017efd56..4f1af6a1f 100644
--- a/python/build/cmake.py
+++ b/python/build/cmake.py
@@ -1,4 +1,5 @@
 import os
+import re
 import subprocess
 
 from build.project import Project
@@ -31,6 +32,26 @@ set(CMAKE_CXX_FLAGS "{toolchain.cxxflags} {toolchain.cppflags}")
     __write_cmake_compiler(f, 'C', toolchain.cc)
     __write_cmake_compiler(f, 'CXX', toolchain.cxx)
 
+    if cmake_system_name == 'Darwin':
+        # On macOS, cmake forcibly adds an "-isysroot" flag even if
+        # one is already present in the flags variable; this breaks
+        # cross-compiling for iOS, and can be worked around by setting
+        # the CMAKE_OSX_SYSROOT variable
+        # (https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html).
+        m = re.search(r'-isysroot +(\S+)', toolchain.cflags)
+        if m:
+            sysroot = m.group(1)
+
+            print(f'set(CMAKE_OSX_SYSROOT {sysroot})', file=f)
+
+            # search libraries and headers only in the sysroot, not on
+            # the build host
+            f.write(f"""
+set(CMAKE_FIND_ROOT_PATH "{toolchain.install_prefix};{sysroot}")
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+""")
+
 def configure(toolchain, src, build, args=(), env=None):
     cross_args = []
 

From 273a93cfcfafef6fd02d25fa859a66f9478dc47e Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 22 Aug 2022 10:28:10 +0200
Subject: [PATCH 25/48] build/python/cmake: set CMAKE_C_FLAGS_INIT, not
 CMAKE_C_FLAGS

According to
https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_INIT.html
the _INIT variables should be set in the toolchain file.
---
 python/build/cmake.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/python/build/cmake.py b/python/build/cmake.py
index 4f1af6a1f..b41d14698 100644
--- a/python/build/cmake.py
+++ b/python/build/cmake.py
@@ -26,8 +26,8 @@ set(CMAKE_SYSTEM_PROCESSOR {toolchain.actual_arch.split('-', 1)[0]})
 set(CMAKE_C_COMPILER_TARGET {toolchain.actual_arch})
 set(CMAKE_CXX_COMPILER_TARGET {toolchain.actual_arch})
 
-set(CMAKE_C_FLAGS "{toolchain.cflags} {toolchain.cppflags}")
-set(CMAKE_CXX_FLAGS "{toolchain.cxxflags} {toolchain.cppflags}")
+set(CMAKE_C_FLAGS_INIT "{toolchain.cflags} {toolchain.cppflags}")
+set(CMAKE_CXX_FLAGS_INIT "{toolchain.cxxflags} {toolchain.cppflags}")
 """)
     __write_cmake_compiler(f, 'C', toolchain.cc)
     __write_cmake_compiler(f, 'CXX', toolchain.cxx)

From 23a5b8fd3c7fbe1cf0f3184aaa86cf8052a33a0c Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 14:17:19 +0100
Subject: [PATCH 26/48] python/build/meson.py: remove unused import

---
 python/build/meson.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/python/build/meson.py b/python/build/meson.py
index 4c1a5babf..084e36610 100644
--- a/python/build/meson.py
+++ b/python/build/meson.py
@@ -1,4 +1,5 @@
-import os.path, subprocess, sys
+import os
+import subprocess
 import platform
 
 from build.project import Project

From fb695bc55f24f73a2518b3c14dcdcee998f802dc Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 16:32:49 +0100
Subject: [PATCH 27/48] command/{file,storage}: remove stray "#pragma GCC
 diagnostic pop"

---
 src/command/FileCommands.cxx    | 4 ----
 src/command/StorageCommands.cxx | 4 ----
 2 files changed, 8 deletions(-)

diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx
index c27eaeb79..4a14e9a2a 100644
--- a/src/command/FileCommands.cxx
+++ b/src/command/FileCommands.cxx
@@ -100,10 +100,6 @@ handle_listfiles_local(Response &r, Path path_fs)
 	return CommandResult::OK;
 }
 
-#if defined(_WIN32) && GCC_CHECK_VERSION(4,6)
-#pragma GCC diagnostic pop
-#endif
-
 gcc_pure
 static bool
 IsValidName(const StringView s) noexcept
diff --git a/src/command/StorageCommands.cxx b/src/command/StorageCommands.cxx
index b547a2da6..461c0e342 100644
--- a/src/command/StorageCommands.cxx
+++ b/src/command/StorageCommands.cxx
@@ -83,10 +83,6 @@ handle_listfiles_storage(Response &r, StorageDirectoryReader &reader)
 	}
 }
 
-#if defined(_WIN32) && GCC_CHECK_VERSION(4,6)
-#pragma GCC diagnostic pop
-#endif
-
 CommandResult
 handle_listfiles_storage(Response &r, Storage &storage, const char *uri)
 {

From edae00e71929466bd9e84af40ca8ac545fed7540 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 19:20:12 +0100
Subject: [PATCH 28/48] fs/Charset: remove useless log message

---
 src/fs/Charset.cxx | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx
index 11b04c724..5a207fc0d 100644
--- a/src/fs/Charset.cxx
+++ b/src/fs/Charset.cxx
@@ -20,7 +20,6 @@
 #include "Charset.hxx"
 #include "Features.hxx"
 #include "Domain.hxx"
-#include "Log.hxx"
 #include "lib/icu/Converter.hxx"
 #include "util/AllocatedString.hxx"
 #include "config.h"
@@ -47,9 +46,6 @@ SetFSCharset(const char *charset)
 
 	fs_converter = IcuConverter::Create(charset);
 	assert(fs_converter != nullptr);
-
-	FmtDebug(path_domain,
-		 "SetFSCharset: fs charset is {}", fs_charset);
 }
 
 #endif

From 9475ef2202302135adcf18f4f51c524e0b07e53c Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 6 Mar 2023 19:24:53 +0100
Subject: [PATCH 29/48] fs/Charset: assign fs_charset

This got lost 8 years ago in commit 87c88fcb27692a

D'oh!
---
 src/fs/Charset.cxx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx
index 5a207fc0d..e9ddc5659 100644
--- a/src/fs/Charset.cxx
+++ b/src/fs/Charset.cxx
@@ -44,6 +44,7 @@ SetFSCharset(const char *charset)
 	assert(charset != nullptr);
 	assert(fs_converter == nullptr);
 
+	fs_charset = charset;
 	fs_converter = IcuConverter::Create(charset);
 	assert(fs_converter != nullptr);
 }

From 0206a46d39b73b9cdc12009213f6d3823b664375 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Thu, 9 Mar 2023 18:09:54 +0100
Subject: [PATCH 30/48] decoder/gme: require GME 0.6 or later

This allows dropping a few compile-time version checks and we can use
pkg-config to detect the library.
---
 NEWS                                     | 2 ++
 src/decoder/plugins/GmeDecoderPlugin.cxx | 7 +------
 src/decoder/plugins/meson.build          | 2 +-
 3 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/NEWS b/NEWS
index 5cb84ea24..0cc0a8d93 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 ver 0.23.13 (not yet released)
 * input
   - curl: fix busy loop after connection failed
+* decoder
+  - gme: require GME 0.6 or later
 * output
   - pipewire: fix corruption bug due to missing lock
 * Linux
diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx
index 66d35a5fa..f854c5b25 100644
--- a/src/decoder/plugins/GmeDecoderPlugin.cxx
+++ b/src/decoder/plugins/GmeDecoderPlugin.cxx
@@ -56,20 +56,17 @@ struct GmeContainerPath {
 	unsigned track;
 };
 
-#if GME_VERSION >= 0x000600
 static int gme_accuracy;
-#endif
 static unsigned gme_default_fade;
 
 static bool
 gme_plugin_init([[maybe_unused]] const ConfigBlock &block)
 {
-#if GME_VERSION >= 0x000600
 	auto accuracy = block.GetBlockParam("accuracy");
 	gme_accuracy = accuracy != nullptr
 		? (int)accuracy->GetBoolValue()
 		: -1;
-#endif
+
 	auto fade = block.GetBlockParam("default_fade");
 	gme_default_fade = fade != nullptr
 		? fade->GetUnsignedValue() * 1000
@@ -163,10 +160,8 @@ gme_file_decode(DecoderClient &client, Path path_fs)
 	FmtDebug(gme_domain, "emulator type '{}'",
 		 gme_type_system(gme_type(emu)));
 
-#if GME_VERSION >= 0x000600
 	if (gme_accuracy >= 0)
 		gme_enable_accuracy(emu, gme_accuracy);
-#endif
 
 	gme_info_t *ti;
 	const char *gme_err = gme_track_info(emu, &ti, container.track);
diff --git a/src/decoder/plugins/meson.build b/src/decoder/plugins/meson.build
index 2322eeaaa..cd6740494 100644
--- a/src/decoder/plugins/meson.build
+++ b/src/decoder/plugins/meson.build
@@ -81,7 +81,7 @@ if libfaad_dep.found()
   decoder_plugins_sources += 'FaadDecoderPlugin.cxx'
 endif
 
-libgme_dep = c_compiler.find_library('gme', required: get_option('gme'))
+libgme_dep = dependency('libgme', version: '>= 0.6', required: get_option('gme'))
 decoder_features.set('ENABLE_GME', libgme_dep.found())
 if libgme_dep.found()
   decoder_plugins_sources += 'GmeDecoderPlugin.cxx'

From a5281856c99a23c05618f008294b711b42adf073 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Thu, 9 Mar 2023 18:33:21 +0100
Subject: [PATCH 31/48] python/build/libs.py: update WildMidi to 0.4.5

---
 python/build/libs.py | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/python/build/libs.py b/python/build/libs.py
index 8399fd98e..064a57080 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -131,17 +131,14 @@ libopenmpt = AutotoolsProject(
 )
 
 wildmidi = CmakeProject(
-    'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.4',
-    '6f267c8d331e9859906837e2c197093fddec31829d2ebf7b958cf6b7ae935430',
+    'https://github.com/Mindwerks/wildmidi/releases/download/wildmidi-0.4.5/wildmidi-0.4.5.tar.gz',
+    'd5e7bef00a7aa47534a53d43b1265f8d3d27f6a28e7f563c1cdf02ff4fa35b99',
     'lib/libWildMidi.a',
     [
         '-DBUILD_SHARED_LIBS=OFF',
         '-DWANT_PLAYER=OFF',
         '-DWANT_STATIC=ON',
     ],
-    base='wildmidi-wildmidi-0.4.4',
-    name='wildmidi',
-    version='0.4.4',
 )
 
 gme = CmakeProject(

From 0c4d824d642c20ac8c919e96d2cd11c54d45b4fb Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Fri, 10 Mar 2023 13:34:59 +0100
Subject: [PATCH 32/48] subprojects/sqlite3.wrap: update to 3.41.0-1

---
 subprojects/sqlite3.wrap | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/subprojects/sqlite3.wrap b/subprojects/sqlite3.wrap
index d6af58006..ceb528205 100644
--- a/subprojects/sqlite3.wrap
+++ b/subprojects/sqlite3.wrap
@@ -1,12 +1,13 @@
 [wrap-file]
-directory = sqlite-amalgamation-3390300
-source_url = https://sqlite.org/2022/sqlite-amalgamation-3390300.zip
-source_filename = sqlite-amalgamation-3390300.zip
-source_hash = a89db3030d229d860ae56a8bac50ac9761434047ae886e47e7c8f9f428fa98ad
-patch_filename = sqlite3_3.39.3-1_patch.zip
-patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.39.3-1/get_patch
-patch_hash = f5c41ff7b3da1108ed221b9a820b41188550cafb8a6c3d247bb40bd598775050
-wrapdb_version = 3.39.3-1
+directory = sqlite-amalgamation-3410000
+source_url = https://www.sqlite.org/2023/sqlite-amalgamation-3410000.zip
+source_filename = sqlite-amalgamation-3410000.zip
+source_hash = 146ce189b67fdbefbf2d72cdc81e198d07ff643614cc9102e9bf063255e8e7e1
+patch_filename = sqlite3_3.41.0-1_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.41.0-1/get_patch
+patch_hash = 784f9257df751765725e2c83356debabf12ea9f0b3c7750aa866e5077872de17
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.41.0-1/sqlite-amalgamation-3410000.zip
+wrapdb_version = 3.41.0-1
 
 [provide]
 sqlite3 = sqlite3_dep

From a59c9c602b325bb1fe024b66a75896c977d167fe Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Tue, 21 Mar 2023 09:37:36 +0100
Subject: [PATCH 33/48] python/build/libs.py: update CURL to 8.0.1

---
 python/build/libs.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/python/build/libs.py b/python/build/libs.py
index 064a57080..e5218d6cd 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -604,8 +604,8 @@ openssl = OpenSSLProject(
 )
 
 curl = CmakeProject(
-    'https://curl.se/download/curl-7.88.1.tar.xz',
-    '1dae31b2a7c1fe269de99c0c31bb488346aab3459b5ffca909d6938249ae415f',
+    'https://curl.se/download/curl-8.0.1.tar.xz',
+    '0a381cd82f4d00a9a334438b8ca239afea5bfefcfa9a1025f2bf118e79e0b5f0',
     'lib/libcurl.a',
     [
         '-DBUILD_CURL_EXE=OFF',

From a6a1182c4c4f63ec021aebde3c2f7c921d309fa2 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Tue, 21 Mar 2023 09:39:20 +0100
Subject: [PATCH 34/48] python/build/libs.py: update OpenSSL to 3.1.0

---
 python/build/libs.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/python/build/libs.py b/python/build/libs.py
index e5218d6cd..1f89df53e 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -598,8 +598,8 @@ ffmpeg = FfmpegProject(
 )
 
 openssl = OpenSSLProject(
-    'https://www.openssl.org/source/openssl-3.0.8.tar.gz',
-    '6c13d2bf38fdf31eac3ce2a347073673f5d63263398f1f69d0df4a41253e4b3e',
+    'https://www.openssl.org/source/openssl-3.1.0.tar.gz',
+    'aaa925ad9828745c4cad9d9efeb273deca820f2cdcf2c3ac7d7c1212b7c497b4',
     'include/openssl/ossl_typ.h',
 )
 

From 4ec6d0555abbf11a94e4e9145713e932bda34728 Mon Sep 17 00:00:00 2001
From: kaliko <kaliko@azylum.org>
Date: Tue, 11 Apr 2023 21:06:53 +0200
Subject: [PATCH 35/48] check systemd unit dir from systemd.pc in meson

---
 NEWS                       | 1 +
 systemd/system/meson.build | 6 ++++++
 systemd/user/meson.build   | 6 ++++++
 3 files changed, 13 insertions(+)

diff --git a/NEWS b/NEWS
index 0cc0a8d93..2b14f80f2 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ ver 0.23.13 (not yet released)
   - pipewire: fix corruption bug due to missing lock
 * Linux
   - shut down if parent process dies in --no-daemon mode
+  - determine systemd unit directories via pkg-config
 
 ver 0.23.12 (2023/01/17)
 * input
diff --git a/systemd/system/meson.build b/systemd/system/meson.build
index 96e6a219b..b4b4d523c 100644
--- a/systemd/system/meson.build
+++ b/systemd/system/meson.build
@@ -1,4 +1,10 @@
 systemd_system_unit_dir = get_option('systemd_system_unit_dir')
+if systemd_system_unit_dir == ''
+  systemd = dependency('systemd', required: false)
+  if systemd.found()
+      systemd_system_unit_dir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
+  endif
+endif
 if systemd_system_unit_dir == ''
   systemd_system_unit_dir = join_paths(get_option('prefix'), 'lib', 'systemd', 'system')
 endif
diff --git a/systemd/user/meson.build b/systemd/user/meson.build
index fdce339e4..8411f0856 100644
--- a/systemd/user/meson.build
+++ b/systemd/user/meson.build
@@ -1,4 +1,10 @@
 systemd_user_unit_dir = get_option('systemd_user_unit_dir')
+if systemd_user_unit_dir == ''
+  systemd = dependency('systemd', required: false)
+  if systemd.found()
+    systemd_user_unit_dir = systemd.get_pkgconfig_variable('systemduserunitdir')
+  endif
+endif
 if systemd_user_unit_dir == ''
   systemd_user_unit_dir = join_paths(get_option('prefix'), 'lib', 'systemd', 'user')
 endif

From d4f3dd49b4aff022eb90f9718819038c878a373d Mon Sep 17 00:00:00 2001
From: datasone <datasone@datasone.moe>
Date: Wed, 12 Apr 2023 00:04:37 +0800
Subject: [PATCH 36/48] db/SimpleDatabasePlugin: store `in_playlist` value of
 songs into database

Fixes hide_playlist_targets not working after server restart

Currently, `hide_playlists_targets` works by skipping songs with
`in_playlist` value set to true in
[`Directory::Walk`](https://github.com/MusicPlayerDaemon/MPD/blob/a57bcd02382947abbd961594cdf00302c0c7866a/src/db/plugins/simple/Directory.cxx#L237). But
`in_playlist` is not stored and only updated in
[`UpdateWalk::PurgeDanglingFromPlaylists`](https://github.com/MusicPlayerDaemon/MPD/blob/a57bcd02382947abbd961594cdf00302c0c7866a/src/db/update/Playlist.cxx#L139),
which will only be executed while updating DB.

This causes the problem that playlist target songs are correctly
hidden after database update, but will remain visible after mpd server
restarted. This pr solves the problem by storing `in_playlist` value
of songs into the `SimpleDatabase` file.
---
 src/SongSave.cxx                        | 8 +++++++-
 src/SongSave.hxx                        | 2 +-
 src/db/plugins/simple/DirectorySave.cxx | 4 +++-
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/SongSave.cxx b/src/SongSave.cxx
index 842c633b3..11e498904 100644
--- a/src/SongSave.cxx
+++ b/src/SongSave.cxx
@@ -63,6 +63,9 @@ song_save(BufferedOutputStream &os, const Song &song)
 	if (song.audio_format.IsDefined())
 		os.Format("Format: %s\n", ToString(song.audio_format).c_str());
 
+	if (song.in_playlist)
+		os.Write("InPlaylist: yes\n");
+
 	if (!IsNegative(song.mtime))
 		os.Format(SONG_MTIME ": %li\n",
 			  (long)std::chrono::system_clock::to_time_t(song.mtime));
@@ -86,7 +89,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
 
 DetachedSong
 song_load(LineReader &file, const char *uri,
-	  std::string *target_r)
+	  std::string *target_r, bool *in_playlist_r)
 {
 	DetachedSong song(uri);
 
@@ -132,6 +135,9 @@ song_load(LineReader &file, const char *uri,
 
 			song.SetStartTime(SongTime::FromMS(start_ms));
 			song.SetEndTime(SongTime::FromMS(end_ms));
+		} else if (StringIsEqual(line, "InPlaylist")) {
+			if (in_playlist_r != nullptr)
+				*in_playlist_r = StringIsEqual(value, "yes");
 		} else {
 			throw FormatRuntimeError("unknown line in db: %s", line);
 		}
diff --git a/src/SongSave.hxx b/src/SongSave.hxx
index 8ee6ce76b..7b276bd7e 100644
--- a/src/SongSave.hxx
+++ b/src/SongSave.hxx
@@ -44,6 +44,6 @@ song_save(BufferedOutputStream &os, const DetachedSong &song);
  */
 DetachedSong
 song_load(LineReader &file, const char *uri,
-	  std::string *target_r=nullptr);
+	  std::string *target_r=nullptr, bool *in_playlist_r=nullptr);
 
 #endif
diff --git a/src/db/plugins/simple/DirectorySave.cxx b/src/db/plugins/simple/DirectorySave.cxx
index e7648ac11..8dacae763 100644
--- a/src/db/plugins/simple/DirectorySave.cxx
+++ b/src/db/plugins/simple/DirectorySave.cxx
@@ -168,12 +168,14 @@ directory_load(LineReader &file, Directory &directory)
 				throw FormatRuntimeError("Duplicate song '%s'", name);
 
 			std::string target;
+			bool in_playlist = false;
 			auto detached_song = song_load(file, name,
-						       &target);
+						       &target, &in_playlist);
 
 			auto song = std::make_unique<Song>(std::move(detached_song),
 							   directory);
 			song->target = std::move(target);
+			song->in_playlist = in_playlist;
 
 			directory.AddSong(std::move(song));
 		} else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) {

From 4d357ab77c4b50710a5185b06e5b249339007f63 Mon Sep 17 00:00:00 2001
From: Benjamin Cremer <bc@benjamin-cremer.de>
Date: Thu, 13 Apr 2023 08:47:43 +0200
Subject: [PATCH 37/48] Fix syntax error in mpdconf.example

---
 doc/mpdconf.example | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/mpdconf.example b/doc/mpdconf.example
index eaa5e6411..9d14bd642 100644
--- a/doc/mpdconf.example
+++ b/doc/mpdconf.example
@@ -181,7 +181,7 @@
 #
 #database {
 #       plugin "simple"
-#       path "~/.local/share/mpd/db
+#       path "~/.local/share/mpd/db"
 #       cache_directory "~/.local/share/mpd/cache"
 #}
 #

From 75a39ed279bcf7f9e365a6b509daf8a667ebfeca Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sun, 30 Apr 2023 08:39:29 +0200
Subject: [PATCH 38/48] db/update/Archive: remove useless log message

---
 src/db/update/Archive.cxx | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/src/db/update/Archive.cxx b/src/db/update/Archive.cxx
index 996ceb145..9a097e900 100644
--- a/src/db/update/Archive.cxx
+++ b/src/db/update/Archive.cxx
@@ -65,11 +65,8 @@ UpdateWalk::UpdateArchiveTree(ArchiveFile &archive, Directory &directory,
 		//create directories first
 		UpdateArchiveTree(archive, *subdir, tmp + 1);
 	} else {
-		if (StringIsEmpty(name)) {
-			LogWarning(update_domain,
-				   "archive returned directory only");
+		if (StringIsEmpty(name))
 			return;
-		}
 
 		//add file
 		Song *song = LockFindSong(directory, name);

From 16a99804de603069efd730ce791cbfa339c5816b Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sun, 30 Apr 2023 08:40:40 +0200
Subject: [PATCH 39/48] db/update/Archive: move check to IsAcceptableFilename()

---
 src/db/update/Archive.cxx | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/db/update/Archive.cxx b/src/db/update/Archive.cxx
index 9a097e900..93adc35e1 100644
--- a/src/db/update/Archive.cxx
+++ b/src/db/update/Archive.cxx
@@ -51,6 +51,13 @@ LockFindSong(Directory &directory, std::string_view name) noexcept
 	return directory.FindSong(name);
 }
 
+[[gnu::pure]]
+static bool
+IsAcceptableFilename(std::string_view name) noexcept
+{
+	return !name.empty();
+}
+
 void
 UpdateWalk::UpdateArchiveTree(ArchiveFile &archive, Directory &directory,
 			      const char *name) noexcept
@@ -65,7 +72,7 @@ UpdateWalk::UpdateArchiveTree(ArchiveFile &archive, Directory &directory,
 		//create directories first
 		UpdateArchiveTree(archive, *subdir, tmp + 1);
 	} else {
-		if (StringIsEmpty(name))
+		if (!IsAcceptableFilename(name))
 			return;
 
 		//add file

From 96befa138c3f97ff701514555ad3308479c0ab89 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sun, 30 Apr 2023 08:41:59 +0200
Subject: [PATCH 40/48] db/update/Archive: ignore filenames with newline
 character

---
 src/db/update/Archive.cxx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/db/update/Archive.cxx b/src/db/update/Archive.cxx
index 93adc35e1..ccb7eb249 100644
--- a/src/db/update/Archive.cxx
+++ b/src/db/update/Archive.cxx
@@ -55,7 +55,9 @@ LockFindSong(Directory &directory, std::string_view name) noexcept
 static bool
 IsAcceptableFilename(std::string_view name) noexcept
 {
-	return !name.empty();
+	return !name.empty() &&
+		/* newlines cannot be represented in MPD's protocol */
+		name.find('\n') == name.npos;
 }
 
 void

From 1417578b3ddd9173578a9bd73e4ba92f792cfdb1 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sun, 30 Apr 2023 08:42:32 +0200
Subject: [PATCH 41/48] db/update/Archive: validate directory names

Fixes assertion failure if the ZIP file contains a path that begins
with a slash.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1793
---
 NEWS                      | 2 ++
 src/db/update/Archive.cxx | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/NEWS b/NEWS
index 2b14f80f2..0825c118e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 ver 0.23.13 (not yet released)
 * input
   - curl: fix busy loop after connection failed
+* archive
+  - zzip: fix crash bug
 * decoder
   - gme: require GME 0.6 or later
 * output
diff --git a/src/db/update/Archive.cxx b/src/db/update/Archive.cxx
index ccb7eb249..1383cf343 100644
--- a/src/db/update/Archive.cxx
+++ b/src/db/update/Archive.cxx
@@ -67,6 +67,9 @@ UpdateWalk::UpdateArchiveTree(ArchiveFile &archive, Directory &directory,
 	const char *tmp = std::strchr(name, '/');
 	if (tmp) {
 		const std::string_view child_name(name, tmp - name);
+		if (!IsAcceptableFilename(child_name))
+			return;
+
 		//add dir is not there already
 		Directory *subdir = LockMakeChild(directory, child_name);
 		subdir->device = DEVICE_INARCHIVE;

From a71e68db501f9a11438c36590dea047c27c3394b Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 15 May 2023 20:52:21 +0200
Subject: [PATCH 42/48] command/player, SongPrint: use AudioFormatFormatter()

libfmt version 10 apparently doesn't know how to format a
`StringBuffer`, failing the MPD build.  Since we have a formatter
specialization for `AudioFormat`, let's use that - it's better and
easier to use.

Closes https://github.com/MusicPlayerDaemon/MPD/issues/1807
---
 src/SongPrint.cxx              | 5 +++--
 src/command/PlayerCommands.cxx | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx
index dc05bed53..939ed8f1c 100644
--- a/src/SongPrint.cxx
+++ b/src/SongPrint.cxx
@@ -24,6 +24,7 @@
 #include "TagPrint.hxx"
 #include "client/Response.hxx"
 #include "fs/Traits.hxx"
+#include "lib/fmt/AudioFormatFormatter.hxx"
 #include "time/ChronoUtil.hxx"
 #include "util/StringBuffer.hxx"
 #include "util/UriUtil.hxx"
@@ -93,7 +94,7 @@ song_print_info(Response &r, const LightSong &song, bool base) noexcept
 		time_print(r, "Last-Modified", song.mtime);
 
 	if (song.audio_format.IsDefined())
-		r.Fmt(FMT_STRING("Format: {}\n"), ToString(song.audio_format));
+		r.Fmt(FMT_STRING("Format: {}\n"), song.audio_format);
 
 	tag_print_values(r, song.tag);
 
@@ -116,7 +117,7 @@ song_print_info(Response &r, const DetachedSong &song, bool base) noexcept
 		time_print(r, "Last-Modified", song.GetLastModified());
 
 	if (const auto &f = song.GetAudioFormat(); f.IsDefined())
-		r.Fmt(FMT_STRING("Format: {}\n"), ToString(f));
+		r.Fmt(FMT_STRING("Format: {}\n"), f);
 
 	tag_print_values(r, song.GetTag());
 
diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx
index d72949af6..838ef818f 100644
--- a/src/command/PlayerCommands.cxx
+++ b/src/command/PlayerCommands.cxx
@@ -28,6 +28,7 @@
 #include "Partition.hxx"
 #include "Instance.hxx"
 #include "IdleFlags.hxx"
+#include "lib/fmt/AudioFormatFormatter.hxx"
 #include "util/StringBuffer.hxx"
 #include "util/ScopeExit.hxx"
 #include "util/Exception.hxx"
@@ -185,7 +186,7 @@ handle_status(Client &client, [[maybe_unused]] Request args, Response &r)
 
 		if (player_status.audio_format.IsDefined())
 			r.Fmt(FMT_STRING(COMMAND_STATUS_AUDIO ": {}\n"),
-			      ToString(player_status.audio_format));
+			      player_status.audio_format);
 	}
 
 #ifdef ENABLE_DATABASE

From a8042885acfe0a17e493ad575520c4cb9adcc63c Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Mon, 15 May 2023 20:59:58 +0200
Subject: [PATCH 43/48] TimePrint: minor fixup for libfmt 10

libfmt version 10 has difficulties formatting a `StringBuffer`, and we
need to help it by explicitly invoking the `c_str()` method.
---
 NEWS              | 1 +
 src/TimePrint.cxx | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 0825c118e..41c2a23fe 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,7 @@ ver 0.23.13 (not yet released)
 * Linux
   - shut down if parent process dies in --no-daemon mode
   - determine systemd unit directories via pkg-config
+* support libfmt 10
 
 ver 0.23.12 (2023/01/17)
 * input
diff --git a/src/TimePrint.cxx b/src/TimePrint.cxx
index 6f8cca631..44f895e69 100644
--- a/src/TimePrint.cxx
+++ b/src/TimePrint.cxx
@@ -36,5 +36,5 @@ time_print(Response &r, const char *name,
 		return;
 	}
 
-	r.Fmt(FMT_STRING("{}: {}\n"), name, s);
+	r.Fmt(FMT_STRING("{}: {}\n"), name, s.c_str());
 }

From 381934985a238b6d78357e511fc2cc40c4709dab Mon Sep 17 00:00:00 2001
From: latex <latex@disroot.org>
Date: Fri, 12 May 2023 00:25:08 +0200
Subject: [PATCH 44/48] reorder ffmpeg to be lower priority than gme

This should prevent ffmpeg from taking priority over the gme plugin.
The ffmpeg plugin is more buggy than gme.
One of the prominent bugs of preferring ffmpeg over gme is that ffmpeg
cannot seek SAP files while gme can. This should prevent that from
happening.
---
 NEWS                        | 1 +
 src/decoder/DecoderList.cxx | 6 +++---
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/NEWS b/NEWS
index 41c2a23fe..69d88b777 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ ver 0.23.13 (not yet released)
 * archive
   - zzip: fix crash bug
 * decoder
+  - ffmpeg: reorder to a lower priority than "gme"
   - gme: require GME 0.6 or later
 * output
   - pipewire: fix corruption bug due to missing lock
diff --git a/src/decoder/DecoderList.cxx b/src/decoder/DecoderList.cxx
index b1cb25aff..965380089 100644
--- a/src/decoder/DecoderList.cxx
+++ b/src/decoder/DecoderList.cxx
@@ -114,11 +114,11 @@ constexpr const struct DecoderPlugin *decoder_plugins[] = {
 #ifdef ENABLE_ADPLUG
 	&adplug_decoder_plugin,
 #endif
-#ifdef ENABLE_FFMPEG
-	&ffmpeg_decoder_plugin,
-#endif
 #ifdef ENABLE_GME
 	&gme_decoder_plugin,
+#endif
+#ifdef ENABLE_FFMPEG
+	&ffmpeg_decoder_plugin,
 #endif
 	&pcm_decoder_plugin,
 	nullptr

From e1e37cfe3c1bac8617fce7075a150bda1dab69b2 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sun, 21 May 2023 21:02:29 +0200
Subject: [PATCH 45/48] TagPrint, command/File: two more libfmt 10 workarounds

libfmt 10 doesn't know how to format a StringView, and doesn't cast to
std::string_view anymore.  The StringView class has been removed from
MPD 0.24 completely, and this is a stable-branch-only workaround.

Closes https://github.com/MusicPlayerDaemon/MPD/pull/1814
---
 src/TagPrint.cxx             | 3 ++-
 src/command/FileCommands.cxx | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/TagPrint.cxx b/src/TagPrint.cxx
index e4d80e9bf..e5ecb4b35 100644
--- a/src/TagPrint.cxx
+++ b/src/TagPrint.cxx
@@ -35,8 +35,9 @@ tag_print_types(Response &r) noexcept
 }
 
 void
-tag_print(Response &r, TagType type, StringView value) noexcept
+tag_print(Response &r, TagType type, StringView _value) noexcept
 {
+	const std::string_view value{_value};
 	r.Fmt(FMT_STRING("{}: {}\n"), tag_item_names[type], value);
 }
 
diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx
index 4a14e9a2a..3b9a6339e 100644
--- a/src/command/FileCommands.cxx
+++ b/src/command/FileCommands.cxx
@@ -126,7 +126,8 @@ public:
 	explicit PrintCommentHandler(Response &_response) noexcept
 		:NullTagHandler(WANT_PAIR), response(_response) {}
 
-	void OnPair(StringView key, StringView value) noexcept override {
+	void OnPair(StringView _key, StringView _value) noexcept override {
+		const std::string_view key{_key}, value{_value};
 		if (IsValidName(key) && IsValidValue(value))
 			response.Fmt(FMT_STRING("{}: {}\n"), key, value);
 	}

From 4587bf759df1692fe7d39224dc5401ef9112d3f0 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sun, 21 May 2023 21:04:53 +0200
Subject: [PATCH 46/48] subprojects: update expat to 2.5.0-2

---
 subprojects/expat.wrap | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/subprojects/expat.wrap b/subprojects/expat.wrap
index 60eedb33c..90e742db9 100644
--- a/subprojects/expat.wrap
+++ b/subprojects/expat.wrap
@@ -3,10 +3,11 @@ directory = expat-2.5.0
 source_url = https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-2.5.0.tar.xz
 source_filename = expat-2.5.0.tar.bz2
 source_hash = ef2420f0232c087801abf705e89ae65f6257df6b7931d37846a193ef2e8cdcbe
-patch_filename = expat_2.5.0-1_patch.zip
-patch_url = https://wrapdb.mesonbuild.com/v2/expat_2.5.0-1/get_patch
-patch_hash = 0d0d6e07ed21cf4892126a8270f5fd182012ab34b3ebe24932a2bef5ca608a61
-wrapdb_version = 2.5.0-1
+patch_filename = expat_2.5.0-2_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/expat_2.5.0-2/get_patch
+patch_hash = f6cc5ff0d909a2f51a907cc6ca655fb18517a0f58bbe67e4a9c621f1549560c9
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/expat_2.5.0-2/expat-2.5.0.tar.bz2
+wrapdb_version = 2.5.0-2
 
 [provide]
 expat = expat_dep

From 2d22e6dee42db1637911e454f9279907a8032c16 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max.kellermann@gmail.com>
Date: Sun, 21 May 2023 21:05:14 +0200
Subject: [PATCH 47/48] subprojects: update sqlite to 3.41.2-2

---
 subprojects/sqlite3.wrap | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/subprojects/sqlite3.wrap b/subprojects/sqlite3.wrap
index ceb528205..9b85ce93e 100644
--- a/subprojects/sqlite3.wrap
+++ b/subprojects/sqlite3.wrap
@@ -1,13 +1,13 @@
 [wrap-file]
-directory = sqlite-amalgamation-3410000
-source_url = https://www.sqlite.org/2023/sqlite-amalgamation-3410000.zip
-source_filename = sqlite-amalgamation-3410000.zip
-source_hash = 146ce189b67fdbefbf2d72cdc81e198d07ff643614cc9102e9bf063255e8e7e1
-patch_filename = sqlite3_3.41.0-1_patch.zip
-patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.41.0-1/get_patch
-patch_hash = 784f9257df751765725e2c83356debabf12ea9f0b3c7750aa866e5077872de17
-source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.41.0-1/sqlite-amalgamation-3410000.zip
-wrapdb_version = 3.41.0-1
+directory = sqlite-amalgamation-3410200
+source_url = https://www.sqlite.org/2023/sqlite-amalgamation-3410200.zip
+source_filename = sqlite-amalgamation-3410200.zip
+source_hash = 01df06a84803c1ab4d62c64e995b151b2dbcf5dbc93bbc5eee213cb18225d987
+patch_filename = sqlite3_3.41.2-2_patch.zip
+patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.41.2-2/get_patch
+patch_hash = 246681dfb731a14bfa61bcde651d5581a7e1c7d14851bfb57a941fac540a6810
+source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.41.2-2/sqlite-amalgamation-3410200.zip
+wrapdb_version = 3.41.2-2
 
 [provide]
 sqlite3 = sqlite3_dep

From f7eb1c9a8324284b224e5adb2653eaf2f6ccf6e7 Mon Sep 17 00:00:00 2001
From: Michiel Beijen <mb@x14.nl>
Date: Sun, 21 May 2023 18:42:47 +0200
Subject: [PATCH 48/48] Fix meson build warning for get_pkgconfig_variable

Otherwise, building will generate these warnings:

systemd/system/meson.build:5: WARNING: Project targeting '>= 0.56.0' but tried to use feature deprecated since '0.56.0': Dependency.get_pkgconfig_variable. use Dependency.get_variable(pkgconfig : ...) instead
systemd/user/meson.build:5: WARNING: Project targeting '>= 0.56.0' but tried to use feature deprecated since '0.56.0': Dependency.get_pkgconfig_variable. use Dependency.get_variable(pkgconfig : ...) instead
---
 systemd/system/meson.build | 2 +-
 systemd/user/meson.build   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/systemd/system/meson.build b/systemd/system/meson.build
index b4b4d523c..f90f93d5a 100644
--- a/systemd/system/meson.build
+++ b/systemd/system/meson.build
@@ -2,7 +2,7 @@ systemd_system_unit_dir = get_option('systemd_system_unit_dir')
 if systemd_system_unit_dir == ''
   systemd = dependency('systemd', required: false)
   if systemd.found()
-      systemd_system_unit_dir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
+      systemd_system_unit_dir = systemd.get_variable(pkgconfig: 'systemdsystemunitdir')
   endif
 endif
 if systemd_system_unit_dir == ''
diff --git a/systemd/user/meson.build b/systemd/user/meson.build
index 8411f0856..86a1fd933 100644
--- a/systemd/user/meson.build
+++ b/systemd/user/meson.build
@@ -2,7 +2,7 @@ systemd_user_unit_dir = get_option('systemd_user_unit_dir')
 if systemd_user_unit_dir == ''
   systemd = dependency('systemd', required: false)
   if systemd.found()
-    systemd_user_unit_dir = systemd.get_pkgconfig_variable('systemduserunitdir')
+    systemd_user_unit_dir = systemd.get_variable(pkgconfig: 'systemduserunitdir')
   endif
 endif
 if systemd_user_unit_dir == ''