diff --git a/NEWS b/NEWS
index 04e6f1dbc..831a7aad4 100644
--- a/NEWS
+++ b/NEWS
@@ -16,11 +16,16 @@ ver 0.23 (not yet released)
- new tags "ComposerSort", "Ensemble", "Movement", "MovementNumber", and "Location"
* new build-time dependency: libfmt
-ver 0.22.9 (not yet released)
+ver 0.22.9 (2021/06/23)
* database
- simple: load all .mpdignore files of all parent directories
+* tags
+ - fix "readcomments" and "readpicture" on remote files with ID3 tags
* decoder
- ffmpeg: support the tags "sort_album", "album-sort", "artist-sort"
+ - ffmpeg: fix build failure with FFmpeg 3.4
+* Android
+ - fix auto-start on boot in Android 8 or later
* Windows
- fix build failure with SQLite
diff --git a/README.md b/README.md
index ea588710d..1c65bb12d 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ For basic installation instructions
- [Manual](http://www.musicpd.org/doc/user/)
- [Forum](http://forum.musicpd.org/)
-- [IRC](irc://chat.freenode.net/#mpd)
+- [IRC](ircs://irc.libera.chat:6697/#mpd)
- [Bug tracker](https://github.com/MusicPlayerDaemon/MPD/issues/)
# Developers
diff --git a/android/src/Main.java b/android/src/Main.java
index 2c307811a..15c7ba419 100644
--- a/android/src/Main.java
+++ b/android/src/Main.java
@@ -414,6 +414,15 @@ public class Main extends Service implements Runnable {
* start Main service without any callback
*/
public static void start(Context context, boolean wakelock) {
- context.startService(new Intent(context, Main.class).putExtra("wakelock", wakelock));
+ Intent intent = new Intent(context, Main.class)
+ .putExtra("wakelock", wakelock);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ /* in Android 8+, we need to use this method
+ or else we'll get "IllegalStateException:
+ app is in background" */
+ context.startForegroundService(intent);
+ else
+ context.startService(intent);
}
}
diff --git a/doc/plugins.rst b/doc/plugins.rst
index dc097c25c..7d7213fcc 100644
--- a/doc/plugins.rst
+++ b/doc/plugins.rst
@@ -760,7 +760,7 @@ A resampler using `libsamplerate `_ a.k.a. Secret
* - Name
- Description
* - **type**
- - The interpolator type. See below for a list of known types.
+ - The interpolator type. Defaults to :samp:`2`. See below for a list of known types.
The following converter types are provided by libsamplerate:
diff --git a/doc/user.rst b/doc/user.rst
index 51d952c20..2acfbc0dc 100644
--- a/doc/user.rst
+++ b/doc/user.rst
@@ -689,6 +689,8 @@ The State File
- Specify the state file location. The parent directory must be writable by the :program:`MPD` user (+wx).
* - **state_file_interval SECONDS**
- Auto-save the state file this number of seconds after each state change. Defaults to 120 (2 minutes).
+ * - **restore_paused yes|no**
+ - If set to :samp:`yes`, then :program:`MPD` is put into pause mode instead of starting playback after startup. Default is :samp:`no`.
The Sticker Database
^^^^^^^^^^^^^^^^^^^^
@@ -1121,7 +1123,7 @@ Support
Getting Help
^^^^^^^^^^^^
-The :program:`MPD` project runs a `forum `_ and an IRC channel (#mpd on Freenode) for requesting help. Visit the MPD help page for details on how to get help.
+The :program:`MPD` project runs a `forum `_ and an IRC channel (#mpd on Libera.Chat) for requesting help. Visit the MPD help page for details on how to get help.
Common Problems
^^^^^^^^^^^^^^^
diff --git a/python/build/project.py b/python/build/project.py
index a0cfa60ba..374ccdb14 100644
--- a/python/build/project.py
+++ b/python/build/project.py
@@ -20,7 +20,7 @@ class Project:
self.base = base
if name is None or version is None:
- m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-alpha\d+)?)$', self.base)
+ m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*(?:-alpha\d+)?)(\+.*)?$', self.base)
if name is None: name = m.group(1)
if version is None: version = m.group(2)
diff --git a/src/Main.cxx b/src/Main.cxx
index 76ecff3a2..b3dcb4f33 100644
--- a/src/Main.cxx
+++ b/src/Main.cxx
@@ -489,6 +489,15 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
LogError(std::current_exception(),
"Zeroconf initialization failed");
}
+
+ AtScopeExit(&zeroconf, &instance) {
+ if (zeroconf) {
+ auto &event_loop = instance.io_thread.GetEventLoop();
+ BlockingCall(event_loop, [&](){
+ zeroconf.reset();
+ });
+ }
+ };
#endif
#ifdef ENABLE_DATABASE
@@ -550,16 +559,6 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
instance.state_file->Write();
instance.BeginShutdownUpdate();
-
-#ifdef HAVE_ZEROCONF
- if (zeroconf) {
- auto &event_loop = instance.io_thread.GetEventLoop();
- BlockingCall(event_loop, [&](){
- zeroconf.reset();
- });
- }
-#endif
-
instance.BeginShutdownPartitions();
}
diff --git a/src/TagAny.cxx b/src/TagAny.cxx
index faa237b4c..6f875846e 100644
--- a/src/TagAny.cxx
+++ b/src/TagAny.cxx
@@ -25,6 +25,7 @@
#include "client/Client.hxx"
#include "protocol/Ack.hxx"
#include "fs/AllocatedPath.hxx"
+#include "input/InputStream.hxx"
#include "util/Compiler.h"
#include "util/UriExtract.hxx"
#include "LocateUri.hxx"
@@ -32,8 +33,13 @@
static void
TagScanStream(const char *uri, TagHandler &handler)
{
- if (!tag_stream_scan(uri, handler))
+ Mutex mutex;
+
+ auto is = InputStream::OpenReady(uri, mutex);
+ if (!tag_stream_scan(*is, handler))
throw ProtocolError(ACK_ERROR_NO_EXIST, "Failed to load file");
+
+ ScanGenericTags(*is, handler);
}
static void
diff --git a/src/db/plugins/ProxyDatabasePlugin.cxx b/src/db/plugins/ProxyDatabasePlugin.cxx
index 6f8aaeca2..df886851d 100644
--- a/src/db/plugins/ProxyDatabasePlugin.cxx
+++ b/src/db/plugins/ProxyDatabasePlugin.cxx
@@ -430,6 +430,7 @@ SendGroup(mpd_connection *connection, TagType group)
return mpd_search_add_group_tag(connection, tag);
#else
(void)connection;
+ (void)group;
throw std::runtime_error("Grouping requires libmpdclient 2.12");
#endif
diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
index 889062ee6..c817277e1 100644
--- a/src/decoder/plugins/FfmpegDecoderPlugin.cxx
+++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx
@@ -470,6 +470,7 @@ IsSeekable(const AVFormatContext &format_context) noexcept
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 6, 100)
return (format_context.ctx_flags & AVFMTCTX_UNSEEKABLE) != 0;
#else
+ (void)format_context;
return false;
#endif
}
@@ -658,6 +659,8 @@ ffmpeg_scan_stream(InputStream &is, TagHandler &handler)
return FfmpegScanStream(*f, handler);
}
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 9, 100)
+
static void
ffmpeg_uri_decode(DecoderClient &client, const char *uri)
{
@@ -689,6 +692,8 @@ ffmpeg_protocols() noexcept
return protocols;
}
+#endif
+
/**
* A list of extensions found for the formats supported by ffmpeg.
* This list is current as of 02-23-09; To find out if there are more
@@ -812,6 +817,8 @@ static const char *const ffmpeg_mime_types[] = {
constexpr DecoderPlugin ffmpeg_decoder_plugin =
DecoderPlugin("ffmpeg", ffmpeg_decode, ffmpeg_scan_stream)
.WithInit(ffmpeg_init, ffmpeg_finish)
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 9, 100)
.WithProtocols(ffmpeg_protocols, ffmpeg_uri_decode)
+#endif
.WithSuffixes(ffmpeg_suffixes)
.WithMimeTypes(ffmpeg_mime_types);
diff --git a/src/input/LastInputStream.cxx b/src/input/LastInputStream.cxx
index 40bd5ccc2..e0829c122 100644
--- a/src/input/LastInputStream.cxx
+++ b/src/input/LastInputStream.cxx
@@ -32,6 +32,7 @@ LastInputStream::~LastInputStream() noexcept = default;
void
LastInputStream::Close() noexcept
{
+ uri.clear();
is.reset();
close_timer.Cancel();
}
diff --git a/src/input/LastInputStream.hxx b/src/input/LastInputStream.hxx
index 635e87741..559d45c16 100644
--- a/src/input/LastInputStream.hxx
+++ b/src/input/LastInputStream.hxx
@@ -64,8 +64,7 @@ public:
return is.get();
}
- is.reset();
- close_timer.Cancel();
+ Close();
is = open(new_uri, mutex);
uri = std::forward(new_uri);