From 44b200240f1f4b8394dd2e58fec72da3d3ec448f Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 25 Apr 2018 21:19:26 +0200
Subject: [PATCH 1/8] player/Thread: never reuse decoder when switching radio
 streams

When switching to another song manually, the player checks if the
decoder is already decoding that song; if so, it will attempt to reuse
it by seeking it to the new position.  That however fails if the
decoder is not seekable (e.g. a radio stream) which leaves the user
unable to switch to that song with the bogus error message "Not
seekable".
---
 NEWS                           | 2 ++
 src/decoder/DecoderControl.hxx | 9 +++++++--
 src/player/Thread.cxx          | 2 +-
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/NEWS b/NEWS
index f422b2239..c27ab3d35 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,8 @@ ver 0.20.19 (not yet released)
   - validate absolute seek time, reject negative values
 * input
   - mms: fix lockup bug and a crash bug
+* player
+  - fix spurious "Not seekable" error when switching radio streams
 * macOS: fix crash bug
 
 ver 0.20.18 (2018/02/24)
diff --git a/src/decoder/DecoderControl.hxx b/src/decoder/DecoderControl.hxx
index dfd8232e6..920982e80 100644
--- a/src/decoder/DecoderControl.hxx
+++ b/src/decoder/DecoderControl.hxx
@@ -308,9 +308,14 @@ struct DecoderControl {
 	bool IsCurrentSong(const DetachedSong &_song) const noexcept;
 
 	gcc_pure
-	bool LockIsCurrentSong(const DetachedSong &_song) const noexcept {
+	bool IsSeekableCurrentSong(const DetachedSong &_song) const noexcept {
+		return seekable && IsCurrentSong(_song);
+	}
+
+	gcc_pure
+	bool LockIsSeeakbleCurrentSong(const DetachedSong &_song) const noexcept {
 		const std::lock_guard<Mutex> protect(mutex);
-		return IsCurrentSong(_song);
+		return IsSeekableCurrentSong(_song);
 	}
 
 private:
diff --git a/src/player/Thread.cxx b/src/player/Thread.cxx
index 58883307d..651117088 100644
--- a/src/player/Thread.cxx
+++ b/src/player/Thread.cxx
@@ -584,7 +584,7 @@ Player::SeekDecoder()
 
 	const SongTime start_time = pc.next_song->GetStartTime();
 
-	if (!dc.LockIsCurrentSong(*pc.next_song)) {
+	if (!dc.LockIsSeeakbleCurrentSong(*pc.next_song)) {
 		/* the decoder is already decoding the "next" song -
 		   stop it and start the previous song again */
 

From 1e54297be8322546b0b9c651989ce7fdc5ea2ce2 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 25 Apr 2018 21:35:33 +0200
Subject: [PATCH 2/8] lib/ffmpeg/Init: fix av_register_all() deprecation
 warning

av_register_all() was deprecated in
FFmpeg/FFmpeg@0694d8702421e7aff1340038559c438b61bb30dd
---
 NEWS                    | 2 ++
 src/lib/ffmpeg/Init.cxx | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/NEWS b/NEWS
index c27ab3d35..11a77af8d 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,8 @@ ver 0.20.19 (not yet released)
   - validate absolute seek time, reject negative values
 * input
   - mms: fix lockup bug and a crash bug
+* decoder
+  - ffmpeg: fix av_register_all() deprecation warning (FFmpeg 4.0)
 * player
   - fix spurious "Not seekable" error when switching radio streams
 * macOS: fix crash bug
diff --git a/src/lib/ffmpeg/Init.cxx b/src/lib/ffmpeg/Init.cxx
index 7f11a72d4..9a7872618 100644
--- a/src/lib/ffmpeg/Init.cxx
+++ b/src/lib/ffmpeg/Init.cxx
@@ -33,6 +33,9 @@ FfmpegInit()
 {
 	av_log_set_callback(FfmpegLogCallback);
 
+#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
+	/* deprecated as of FFmpeg 4.0 */
 	av_register_all();
+#endif
 }
 

From d40e9de2d2b82063853bebb87afce4e2199afa6e Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 26 Apr 2018 19:14:26 +0200
Subject: [PATCH 3/8] python/build/libs.py: upgrade libvorbis to 1.3.6

---
 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 1c99a37cb..c51465c31 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -17,8 +17,8 @@ libogg = AutotoolsProject(
 )
 
 libvorbis = AutotoolsProject(
-    'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz',
-    '28cb28097c07a735d6af56e598e1c90f',
+    'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.tar.xz',
+    'af00bb5a784e7c9e69f56823de4637c350643deedaf333d0fa86ecdba6fcb415',
     'lib/libvorbis.a',
     [
         '--disable-shared', '--enable-static',

From 5c4169e64e8b6b86ebc7ca2de260e075fdf27f5f Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 26 Apr 2018 19:16:16 +0200
Subject: [PATCH 4/8] python/build/libs.py: upgrade FFmpeg to 4.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 c51465c31..c11be6188 100644
--- a/python/build/libs.py
+++ b/python/build/libs.py
@@ -105,8 +105,8 @@ liblame = AutotoolsProject(
 )
 
 ffmpeg = FfmpegProject(
-    'http://ffmpeg.org/releases/ffmpeg-3.4.2.tar.xz',
-    '2b92e9578ef8b3e49eeab229e69305f5f4cbc1fdaa22e927fc7fca18acccd740',
+    'http://ffmpeg.org/releases/ffmpeg-4.0.tar.xz',
+    'ed945daf40b124e77a685893cc025d086f638bc703183460aff49508edb3a43f',
     'lib/libavcodec.a',
     [
         '--disable-shared', '--enable-static',

From 388768b3a65c8b9264070090480c948663d746e8 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 26 Apr 2018 19:38:57 +0200
Subject: [PATCH 5/8] db/proxy: call mpd_search_cancel() after search error

Fixes "search already in progress" errors.
---
 NEWS                                   |  2 ++
 src/db/plugins/ProxyDatabasePlugin.cxx | 14 ++++++++++++--
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 11a77af8d..50210f28e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 ver 0.20.19 (not yet released)
 * protocol
   - validate absolute seek time, reject negative values
+* database
+  - proxy: fix "search already in progress" errors
 * input
   - mms: fix lockup bug and a crash bug
 * decoder
diff --git a/src/db/plugins/ProxyDatabasePlugin.cxx b/src/db/plugins/ProxyDatabasePlugin.cxx
index 13f174024..9b5da1dd9 100644
--- a/src/db/plugins/ProxyDatabasePlugin.cxx
+++ b/src/db/plugins/ProxyDatabasePlugin.cxx
@@ -682,7 +682,7 @@ static void
 SearchSongs(struct mpd_connection *connection,
 	    const DatabaseSelection &selection,
 	    VisitSong visit_song)
-{
+try {
 	assert(selection.recursive);
 	assert(visit_song);
 
@@ -709,6 +709,11 @@ SearchSongs(struct mpd_connection *connection,
 
 	if (!mpd_response_finish(connection))
 		ThrowError(connection);
+} catch (...) {
+	if (connection != nullptr)
+		mpd_search_cancel(connection);
+
+	throw;
 }
 
 /**
@@ -758,7 +763,7 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
 			       TagType tag_type,
 			       gcc_unused tag_mask_t group_mask,
 			       VisitTag visit_tag) const
-{
+try {
 	// TODO: eliminate the const_cast
 	const_cast<ProxyDatabase *>(this)->EnsureConnected();
 
@@ -801,6 +806,11 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
 
 	if (!mpd_response_finish(connection))
 		ThrowError(connection);
+} catch (...) {
+	if (connection != nullptr)
+		mpd_search_cancel(connection);
+
+	throw;
 }
 
 DatabaseStats

From ac395429c39b846d48144e4311246abd8059a3a8 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 26 Apr 2018 19:28:47 +0200
Subject: [PATCH 6/8] db/proxy: implement the group_mask parameter in
 VisitUniqueTags()

Closes #258
---
 NEWS                                   |  1 +
 src/db/plugins/ProxyDatabasePlugin.cxx | 63 ++++++++++++++++++++++----
 2 files changed, 54 insertions(+), 10 deletions(-)

diff --git a/NEWS b/NEWS
index 50210f28e..29b121cef 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ ver 0.20.19 (not yet released)
   - validate absolute seek time, reject negative values
 * database
   - proxy: fix "search already in progress" errors
+  - proxy: implement "list ... group"
 * input
   - mms: fix lockup bug and a crash bug
 * decoder
diff --git a/src/db/plugins/ProxyDatabasePlugin.cxx b/src/db/plugins/ProxyDatabasePlugin.cxx
index 9b5da1dd9..c2bc33c6c 100644
--- a/src/db/plugins/ProxyDatabasePlugin.cxx
+++ b/src/db/plugins/ProxyDatabasePlugin.cxx
@@ -325,6 +325,34 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection)
 	return true;
 }
 
+static bool
+SendGroupMask(mpd_connection *connection, tag_mask_t mask)
+{
+#if LIBMPDCLIENT_CHECK_VERSION(2,12,0)
+	for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
+		if ((mask & (tag_mask_t(1) << i)) == 0)
+			continue;
+
+		const auto tag = Convert(TagType(i));
+		if (tag == MPD_TAG_COUNT)
+			throw std::runtime_error("Unsupported tag");
+
+		if (!mpd_search_add_group_tag(connection, tag))
+			return false;
+	}
+
+	return true;
+#else
+	(void)connection;
+	(void)mask;
+
+	if (mask != 0)
+		throw std::runtime_error("Grouping requires libmpdclient 2.12");
+
+	return true;
+#endif
+}
+
 ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
 			     const ConfigBlock &block)
 	:Database(proxy_db_plugin),
@@ -761,7 +789,7 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
 void
 ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
 			       TagType tag_type,
-			       gcc_unused tag_mask_t group_mask,
+			       tag_mask_t group_mask,
 			       VisitTag visit_tag) const
 try {
 	// TODO: eliminate the const_cast
@@ -772,32 +800,47 @@ try {
 		throw std::runtime_error("Unsupported tag");
 
 	if (!mpd_search_db_tags(connection, tag_type2) ||
-	    !SendConstraints(connection, selection))
+	    !SendConstraints(connection, selection) ||
+	    !SendGroupMask(connection, group_mask))
 		ThrowError(connection);
 
-	// TODO: use group_mask
-
 	if (!mpd_search_commit(connection))
 		ThrowError(connection);
 
-	while (auto *pair = mpd_recv_pair_tag(connection, tag_type2)) {
+	TagBuilder builder;
+
+	while (auto *pair = mpd_recv_pair(connection)) {
 		AtScopeExit(this, pair) {
 			mpd_return_pair(connection, pair);
 		};
 
-		TagBuilder tag;
-		tag.AddItem(tag_type, pair->value);
+		const auto current_type = tag_name_parse_i(pair->name);
+		if (current_type == TAG_NUM_OF_ITEM_TYPES)
+			continue;
 
-		if (tag.IsEmpty())
+		if (current_type == tag_type && !builder.IsEmpty()) {
+			try {
+				visit_tag(builder.Commit());
+			} catch (...) {
+				mpd_response_finish(connection);
+				throw;
+			}
+		}
+
+		builder.AddItem(current_type, pair->value);
+
+		if (!builder.HasType(current_type))
 			/* if no tag item has been added, then the
 			   given value was not acceptable
 			   (e.g. empty); forcefully insert an empty
 			   tag in this case, as the caller expects the
 			   given tag type to be present */
-			tag.AddEmptyItem(tag_type);
+			builder.AddEmptyItem(current_type);
+	}
 
+	if (!builder.IsEmpty()) {
 		try {
-			visit_tag(tag.Commit());
+			visit_tag(builder.Commit());
 		} catch (...) {
 			mpd_response_finish(connection);
 			throw;

From 504e8d564aff6ecf9608a35c6aa08b55e0eab676 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 26 Apr 2018 19:56:39 +0200
Subject: [PATCH 7/8] android/AndroidManifest.xml: increment version number to
 0.20.19

---
 android/AndroidManifest.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index afa8c8475..16f96a9cf 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -2,8 +2,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="org.musicpd"
           android:installLocation="auto"
-          android:versionCode="17"
-          android:versionName="0.20.18">
+          android:versionCode="18"
+          android:versionName="0.20.19">
 
   <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"/>
 

From 7b94f0e36ba9ab4ee50ce1984dbd2007475106f5 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Thu, 26 Apr 2018 19:57:04 +0200
Subject: [PATCH 8/8] release v0.20.19

---
 NEWS | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 29b121cef..132758bc2 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-ver 0.20.19 (not yet released)
+ver 0.20.19 (2018/04/26)
 * protocol
   - validate absolute seek time, reject negative values
 * database