Hello community,

here is the log from the commit of package clementine for openSUSE:Factory 
checked in at 2019-03-28 22:48:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/clementine (Old)
 and      /work/SRC/openSUSE:Factory/.clementine.new.25356 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "clementine"

Thu Mar 28 22:48:39 2019 rev:48 rq:689094 version:1.3.1+git20190213

Changes:
--------
--- /work/SRC/openSUSE:Factory/clementine/clementine.changes    2019-03-26 
22:34:25.113676880 +0100
+++ /work/SRC/openSUSE:Factory/.clementine.new.25356/clementine.changes 
2019-03-28 22:48:41.135054753 +0100
@@ -1,0 +2,15 @@
+Wed Mar 27 11:54:32 UTC 2019 - plater <davejpla...@gmail.com>
+
+- Added use_system_qxtglobalshortcut.patch to improve kde
+  integration.
+_ Added cherrypicked patches to bring qt5 branch to master's state:
+  0001-Improved-support-for-APEv2-tags.-6280.patch,
+  0001-Prevent-UI-hang-during-device-scan.-6291.patch,
+  0001-Fix-thread-safety-issues-when-initially-loading-devi.patch,
+  0001-Handle-case-where-a-lister-adds-a-device-before-load.patch,
+  0001-Fix-MoodbarPipeline-crash-on-gstreamer-error.patch,
+  0001-Fix-potential-use-of-streamer-element-after-deletion.patch,
+  0001-Free-decoder-bin-if-error-occurs-during-setup.patch and
+  0001-Fix-several-gstreamer-object-leaks.patch.
+
+-------------------------------------------------------------------

New:
----
  0001-Fix-MoodbarPipeline-crash-on-gstreamer-error.patch
  0001-Fix-potential-use-of-streamer-element-after-deletion.patch
  0001-Fix-several-gstreamer-object-leaks.patch
  0001-Fix-thread-safety-issues-when-initially-loading-devi.patch
  0001-Free-decoder-bin-if-error-occurs-during-setup.patch
  0001-Handle-case-where-a-lister-adds-a-device-before-load.patch
  0001-Improved-support-for-APEv2-tags.-6280.patch
  0001-Prevent-UI-hang-during-device-scan.-6291.patch
  use_system_qxtglobalshortcut.patch

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ clementine.spec ++++++
--- /var/tmp/diff_new_pack.Io6t6p/_old  2019-03-28 22:48:43.339054354 +0100
+++ /var/tmp/diff_new_pack.Io6t6p/_new  2019-03-28 22:48:43.379054347 +0100
@@ -45,7 +45,17 @@
 Patch2:         clementine-moodbar-fpic.patch
 # PATCH-FIX-OPENSUSE clementine-hidden-systray-icon.patch 
davejpla...@gmail.com -- sys tray icon is hidden on some plasma5 systems.
 Patch4:         clementine-hidden-systray-icon.patch
-# Fix global shortcuts using Gnome (GSD) D-Bus backend
+# PATCH-FEATURE-OPENSUSE
+Patch6:         use_system_qxtglobalshortcut.patch
+#PATCH-FIX-GIT to 2019-03-26
+Patch7:         0001-Improved-support-for-APEv2-tags.-6280.patch
+Patch8:         0001-Prevent-UI-hang-during-device-scan.-6291.patch
+Patch9:         0001-Fix-thread-safety-issues-when-initially-loading-devi.patch
+Patch10:        0001-Handle-case-where-a-lister-adds-a-device-before-load.patch
+Patch11:        0001-Fix-MoodbarPipeline-crash-on-gstreamer-error.patch
+Patch12:        0001-Fix-potential-use-of-streamer-element-after-deletion.patch
+Patch13:        0001-Free-decoder-bin-if-error-occurs-during-setup.patch
+Patch14:        0001-Fix-several-gstreamer-object-leaks.patch
 
 %if 0%{?suse_version} > 1325
 BuildRequires:  libboost_headers-devel
@@ -77,6 +87,7 @@
 BuildRequires:  pkgconfig(Qt5X11Extras)
 BuildRequires:  pkgconfig(Qt5Xml)
 BuildRequires:  pkgconfig(libmygpo-qt5)
+BuildRequires:  pkgconfig(qxtglobalshortcut)
 #BuildRequires:  pkgconfig(QxtCore-qt5)
 %else
 BuildRequires:  liblastfm-devel
@@ -145,9 +156,8 @@
 %else
 %setup -q -n Clementine-%{rev}
 %endif
-%patch1
-%patch2
-%patch4
+%autopatch -p1
+
 # NOTE: Build using system versions of libraries.
 rm -rvf 3rdparty/taglib
 rm -rvf 3rdparty/SPMediaKeyTap
@@ -162,7 +172,7 @@
   -DUSE_SYSTEM_PROJECTM=ON             \
   -DBUNDLE_PROJECTM_PRESETS=OFF        \
 %if %{with qt5}
-  -DUSE_SYSTEM_QXT=OFF                 \
+  -DUSE_SYSTEM_QXT=ON                 \
 %else
   -DUSE_SYSTEM_QXT=ON                  \
 %endif

++++++ 0001-Fix-MoodbarPipeline-crash-on-gstreamer-error.patch ++++++
>From 55edcf5321051e44281f067a7e3ee44871982c12 Mon Sep 17 00:00:00 2001
From: Jim Broadus <jbroa...@gmail.com>
Date: Sun, 10 Mar 2019 23:34:11 -0700
Subject: [PATCH] Fix MoodbarPipeline crash on gstreamer error.

As reported in issue 6302, playing a stream that causes gstreamer to error at
start can cause a crash. The problem occurs when the MoodbarPipeline receives a
pad-added signal after it has handled an error callback. In the error callback,
the builder_ is freed. In the pad-added handler (NewPadCallback), this object
is accessed.

This change adds a running_ flag that is set when the pipeline is started and
cleared on an error, end of stream, or object destruction. We check this flag at
the beginning of NewPadCallback. For sanity sake, we also check the builder_
pointer before dereferencing. Note that checking the state of the pipeline
wasn't an option since the pipeline is in the process of changing states during
the pad-added callback and gst_element_get_state wants to block during a state
change.

This solution is not complete as there are still some syncronization issues.
With this specific situation, the error and new pad callbacks appear to always
occur on the same thread, but that's probably not true for all error conditions.
The object is also destroyed by a different thread, so it may be possible that a
callback can occur at the wrong time during or after the deletion of the object.

See https://github.com/clementine-player/Clementine/issues/6302
---
 src/moodbar/moodbarpipeline.cpp | 17 +++++++++++++++--
 src/moodbar/moodbarpipeline.h   |  1 +
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/moodbar/moodbarpipeline.cpp b/src/moodbar/moodbarpipeline.cpp
index 7163efda1..bddbf678c 100644
--- a/src/moodbar/moodbarpipeline.cpp
+++ b/src/moodbar/moodbarpipeline.cpp
@@ -37,7 +37,8 @@ MoodbarPipeline::MoodbarPipeline(const QUrl& local_filename)
       local_filename_(local_filename),
       pipeline_(nullptr),
       convert_element_(nullptr),
-      success_(false) {}
+      success_(false),
+      running_(false) {}
 
 MoodbarPipeline::~MoodbarPipeline() { Cleanup(); }
 
@@ -117,6 +118,7 @@ void MoodbarPipeline::Start() {
   gst_object_unref(bus);
 
   // Start playing
+  running_ = true;
   gst_element_set_state(pipeline_, GST_STATE_PLAYING);
 }
 
@@ -135,6 +137,12 @@ void MoodbarPipeline::ReportError(GstMessage* msg) {
 
 void MoodbarPipeline::NewPadCallback(GstElement*, GstPad* pad, gpointer data) {
   MoodbarPipeline* self = reinterpret_cast<MoodbarPipeline*>(data);
+
+  if (!self->running_) {
+    qLog(Warning) << "Received gstreamer callback after pipeline has stopped.";
+    return;
+  }
+
   GstPad* const audiopad =
       gst_element_get_static_pad(self->convert_element_, "sink");
 
@@ -152,7 +160,10 @@ void MoodbarPipeline::NewPadCallback(GstElement*, GstPad* 
pad, gpointer data) {
   gst_structure_get_int(structure, "rate", &rate);
   gst_caps_unref(caps);
 
-  self->builder_->Init(kBands, rate);
+  if (self->builder_ != nullptr)
+    self->builder_->Init(kBands, rate);
+  else
+    qLog(Error) << "Builder does not exist";
 }
 
 GstBusSyncReply MoodbarPipeline::BusCallbackSync(GstBus*, GstMessage* msg,
@@ -177,6 +188,7 @@ GstBusSyncReply MoodbarPipeline::BusCallbackSync(GstBus*, 
GstMessage* msg,
 
 void MoodbarPipeline::Stop(bool success) {
   success_ = success;
+  running_ = false;
   if (builder_ != nullptr) {
     data_ = builder_->Finish(1000);
     builder_.reset();
@@ -189,6 +201,7 @@ void MoodbarPipeline::Cleanup() {
   Q_ASSERT(QThread::currentThread() == thread());
   Q_ASSERT(QThread::currentThread() != qApp->thread());
 
+  running_ = false;
   if (pipeline_) {
     GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_));
     gst_bus_set_sync_handler(bus, nullptr, nullptr, nullptr);
diff --git a/src/moodbar/moodbarpipeline.h b/src/moodbar/moodbarpipeline.h
index c7acad8e5..629781d64 100644
--- a/src/moodbar/moodbarpipeline.h
+++ b/src/moodbar/moodbarpipeline.h
@@ -71,6 +71,7 @@ class MoodbarPipeline : public QObject {
   std::unique_ptr<MoodbarBuilder> builder_;
 
   bool success_;
+  bool running_;
   QByteArray data_;
 };
 
-- 
2.16.4

++++++ 0001-Fix-potential-use-of-streamer-element-after-deletion.patch ++++++
>From 102c529f80c057d90d45c13e10771fcf2742e337 Mon Sep 17 00:00:00 2001
From: Jim Broadus <jbroa...@gmail.com>
Date: Tue, 19 Mar 2019 18:47:19 -0700
Subject: [PATCH] Fix potential use of streamer element after deletion.

If ReplaceDecodeBin fails from TransitionToNext, uridecodebin_ will not be
replaced with a new element. Since TransitionToNext does not check the return
value, it unknowingly deletes uridecodebin_.
---
 src/engines/gstenginepipeline.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

Index: 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/engines/gstenginepipeline.cpp
===================================================================
--- 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/src/engines/gstenginepipeline.cpp
  2019-02-13 06:02:56.000000000 +0200
+++ 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/engines/gstenginepipeline.cpp
       2019-03-27 12:24:45.226455370 +0200
@@ -1011,7 +1011,10 @@ void GstEnginePipeline::TransitionToNext
 
   ignore_tags_ = true;
 
-  ReplaceDecodeBin(next_url_);
+  if (!ReplaceDecodeBin(next_url_)) {
+    qLog(Error) << "ReplaceDecodeBin failed with " << next_url_;
+    return;
+  }
   gst_element_set_state(uridecodebin_, GST_STATE_PLAYING);
   MaybeLinkDecodeToAudio();
 
++++++ 0001-Fix-several-gstreamer-object-leaks.patch ++++++
>From 5c2ceb349019e98108c0c8a451646742bb76848a Mon Sep 17 00:00:00 2001
From: Jim Broadus <jbroa...@gmail.com>
Date: Mon, 25 Mar 2019 22:37:15 -0700
Subject: [PATCH] Fix several gstreamer object leaks.

There are a number of cases where gst_pipeline_get_bus,
gst_element_get_static_pad, and g_object_get are called without releasing
references. In addition to memory usage, some of these elements hold file
descriptors. In normal operation, two file descriptors are leaked for each
played track. The default fd ulimit for many linux distros is 1024. This
is likely the cause of the crash reported in issue 6309.

This change fixes the obvious and consistent leaks, but it's probably not a
complete solution. There are many error and corner conditions that need to be
examined.
---
 src/engines/gstenginepipeline.cpp | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/src/engines/gstenginepipeline.cpp 
b/src/engines/gstenginepipeline.cpp
index 7f12c2628..17ddd6089 100644
--- a/src/engines/gstenginepipeline.cpp
+++ b/src/engines/gstenginepipeline.cpp
@@ -417,10 +417,13 @@ bool GstEnginePipeline::Init() {
   gst_element_link(probe_converter, probe_sink);
 
   // Link the outputs of tee to the queues on each path.
-  gst_pad_link(gst_element_get_request_pad(tee, "src_%u"),
-               gst_element_get_static_pad(probe_queue, "sink"));
-  gst_pad_link(gst_element_get_request_pad(tee, "src_%u"),
-               gst_element_get_static_pad(audio_queue, "sink"));
+  pad = gst_element_get_static_pad(probe_queue, "sink");
+  gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), pad);
+  gst_object_unref(pad);
+
+  pad = gst_element_get_static_pad(audio_queue, "sink");
+  gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), pad);
+  gst_object_unref(pad);
 
   // Link replaygain elements if enabled.
   if (rg_enabled_) {
@@ -454,12 +457,14 @@ bool GstEnginePipeline::Init() {
   gst_caps_unref(caps);
 
   // Add probes and handlers.
-  gst_pad_add_probe(gst_element_get_static_pad(probe_converter, "src"),
-                    GST_PAD_PROBE_TYPE_BUFFER, HandoffCallback, this, nullptr);
-  gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)),
-                           BusCallbackSync, this, nullptr);
-  bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)),
-                                 BusCallback, this);
+  pad = gst_element_get_static_pad(probe_converter, "src");
+  gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, HandoffCallback, this,
+                    nullptr);
+  gst_object_unref(pad);
+  GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_));
+  gst_bus_set_sync_handler(bus, BusCallbackSync, this, nullptr);
+  bus_cb_id_ = gst_bus_add_watch(bus, BusCallback, this);
+  gst_object_unref(bus);
 
   MaybeLinkDecodeToAudio();
 
@@ -519,8 +524,10 @@ bool GstEnginePipeline::InitFromUrl(const QUrl& url, 
qint64 end_nanosec) {
 
 GstEnginePipeline::~GstEnginePipeline() {
   if (pipeline_) {
-    gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)),
-                             nullptr, nullptr, nullptr);
+    GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_));
+    gst_bus_set_sync_handler(bus, nullptr, nullptr, nullptr);
+    gst_object_unref(bus);
+
     g_source_remove(bus_cb_id_);
     gst_element_set_state(pipeline_, GST_STATE_NULL);
     gst_object_unref(GST_OBJECT(pipeline_));
@@ -1011,6 +1018,7 @@ void 
GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin* bin,
     g_object_set(element, "ssl-strict", TRUE, nullptr);
 #endif
   }
+  g_object_unref(element);
 }
 
 void GstEnginePipeline::TransitionToNext() {
-- 
2.16.4

++++++ 0001-Fix-thread-safety-issues-when-initially-loading-devi.patch ++++++
>From a62062127e0d17ff10e961355ca3807fbd13811a Mon Sep 17 00:00:00 2001
From: Jim Broadus <jbroa...@gmail.com>
Date: Sat, 23 Feb 2019 22:12:07 -0800
Subject: [PATCH] Fix thread-safety issues when initially loading devices from
 the database.

When DeviceManager initializes, it creates a thread to load device information
from the database. Part of this process includes use of QPixMap for icons which
produced a warning message:

22:32:53.763 WARN  unknown                          QPixmap: It is not safe to 
use pixmaps outside the GUI thread

In addition, the device is added to the view using beginInsertRows and
endInsertRows. This could contend with a device added by a lister signaling
PhysicalDeviceAdded.

To solve these problems, this change moves the icon loading and insertion to the
main thread. LoadAllDevices reads the data from the database and creates the
DeviceInfo object, then sends a signal to the main thread. In the signal
handler, the icon is loaded and the device is added to the master list and view.
---
 src/devices/deviceinfo.cpp    | 11 +++--------
 src/devices/devicemanager.cpp | 22 +++++++++++++++++++---
 src/devices/devicemanager.h   |  2 ++
 3 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/src/devices/deviceinfo.cpp b/src/devices/deviceinfo.cpp
index 9c1d83dac..85b148b43 100644
--- a/src/devices/deviceinfo.cpp
+++ b/src/devices/deviceinfo.cpp
@@ -58,14 +58,9 @@ void DeviceInfo::InitFromDb(const 
DeviceDatabaseBackend::Device& dev) {
   size_ = dev.size_;
   transcode_mode_ = dev.transcode_mode_;
   transcode_format_ = dev.transcode_format_;
-
-  QStringList icon_names = dev.icon_name_.split(',');
-  QVariantList icons;
-  for (const QString& icon_name : icon_names) {
-    icons << icon_name;
-  }
-
-  LoadIcon(icons, friendly_name_);
+  // Store the raw value for now. If it's a comma delimited list, it will be
+  // sorted out later.
+  icon_name_ = dev.icon_name_;
 
   QStringList unique_ids = dev.unique_id_.split(',');
   for (const QString& id : unique_ids) {
diff --git a/src/devices/devicemanager.cpp b/src/devices/devicemanager.cpp
index 6e911d23e..940907509 100644
--- a/src/devices/devicemanager.cpp
+++ b/src/devices/devicemanager.cpp
@@ -83,6 +83,8 @@ DeviceManager::DeviceManager(Application* app, QObject* 
parent)
   backend_->moveToThread(app_->database()->thread());
   backend_->Init(app_->database());
 
+  connect(this, SIGNAL(DeviceCreatedFromDb(DeviceInfo*)),
+          SLOT(AddDeviceFromDb(DeviceInfo*)));
   // This reads from the database and contends on the database mutex, which can
   // be very slow on startup.
   ConcurrentRun::Run<void>(&thread_pool_,
@@ -140,11 +142,25 @@ void DeviceManager::LoadAllDevices() {
   for (const DeviceDatabaseBackend::Device& device : devices) {
     DeviceInfo* info = new DeviceInfo(DeviceInfo::Type_Device, root_);
     info->InitFromDb(device);
+    // Use of QPixMap and device insertion should only be done on the main
+    // thread. Send a signal to finish the device addition.
+    emit DeviceCreatedFromDb(info);
+  }
+}
 
-    beginInsertRows(ItemToIndex(root_), devices_.count(), devices_.count());
-    devices_ << info;
-    endInsertRows();
+void DeviceManager::AddDeviceFromDb(DeviceInfo* info) {
+  // At this point, icon_name_ contains the value from the database where the
+  // value is allowed to be a comma delimited list.
+  QStringList icon_names = info->icon_name_.split(',');
+  QVariantList icons;
+  for (const QString& icon_name : icon_names) {
+    icons << icon_name;
   }
+  info->LoadIcon(icons, info->friendly_name_);
+
+  beginInsertRows(ItemToIndex(root_), devices_.count(), devices_.count());
+  devices_ << info;
+  endInsertRows();
 }
 
 QVariant DeviceManager::data(const QModelIndex& idx, int role) const {
diff --git a/src/devices/devicemanager.h b/src/devices/devicemanager.h
index 595a5d5c7..80044256b 100644
--- a/src/devices/devicemanager.h
+++ b/src/devices/devicemanager.h
@@ -101,6 +101,7 @@ class DeviceManager : public SimpleTreeModel<DeviceInfo> {
  signals:
   void DeviceConnected(QModelIndex idx);
   void DeviceDisconnected(QModelIndex idx);
+  void DeviceCreatedFromDb(DeviceInfo* info);
 
  private slots:
   void PhysicalDeviceAdded(const QString& id);
@@ -111,6 +112,7 @@ class DeviceManager : public SimpleTreeModel<DeviceInfo> {
   void DeviceSongCountUpdated(int count);
   void LoadAllDevices();
   void DeviceConnectFinished(const QString& id, bool success);
+  void AddDeviceFromDb(DeviceInfo* info);
 
  protected:
   void LazyPopulate(DeviceInfo* item) { LazyPopulate(item, true); }
-- 
2.16.4

++++++ 0001-Free-decoder-bin-if-error-occurs-during-setup.patch ++++++
>From ca8db288d57dec6dcfaadd94329af4668edc1bdb Mon Sep 17 00:00:00 2001
From: Jim Broadus <jbroa...@gmail.com>
Date: Fri, 22 Mar 2019 19:07:06 -0700
Subject: [PATCH] Free decoder bin if error occurs during setup.

In the case that an error occurs in ReplaceDecodeBin before the bin is added to
the pipeline, unreference the object to allow cleanup. This change also 
separates
CreateDecodeBinFromUrl from ReplaceDecodeBin, following the pattern of
CreateDecodeBinFromString.
---
 src/engines/gstenginepipeline.cpp | 26 ++++++++++++++++++++------
 src/engines/gstenginepipeline.h   |  1 +
 2 files changed, 21 insertions(+), 6 deletions(-)

Index: 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/engines/gstenginepipeline.cpp
===================================================================
--- 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/src/engines/gstenginepipeline.cpp
  2019-03-27 12:30:08.162577265 +0200
+++ 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/engines/gstenginepipeline.cpp
       2019-03-27 12:33:21.501836558 +0200
@@ -153,17 +153,28 @@ bool GstEnginePipeline::ReplaceDecodeBin
 }
 
 bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
-  GstElement* new_bin = nullptr;
+  GstElement* new_bin = CreateDecodeBinFromUrl(url);
+  return ReplaceDecodeBin(new_bin);
+}
 
+GstElement* GstEnginePipeline::CreateDecodeBinFromUrl(const QUrl& url) {
+  GstElement* new_bin = nullptr;
 #ifdef HAVE_SPOTIFY
   if (url.scheme() == "spotify") {
     new_bin = gst_bin_new("spotify_bin");
+    if (!new_bin) return nullptr;
 
     // Create elements
     GstElement* src = engine_->CreateElement("tcpserversrc", new_bin);
-    if (!src) return false;
+    if (!src) {
+      gst_object_unref(GST_OBJECT(new_bin));
+      return nullptr;
+    }
     GstElement* gdp = engine_->CreateElement("gdpdepay", new_bin);
-    if (!gdp) return false;
+    if (!gdp) {
+      gst_object_unref(GST_OBJECT(new_bin));
+      return nullptr;
+    }
 
     // Pick a port number
     const int port = Utilities::PickUnusedPort();
@@ -196,7 +207,7 @@ bool GstEnginePipeline::ReplaceDecodeBin
       uri = url.toEncoded();
     }
     new_bin = engine_->CreateElement("uridecodebin");
-    if (!new_bin) return false;
+    if (!new_bin) return nullptr;
     g_object_set(G_OBJECT(new_bin), "uri", uri.constData(), nullptr);
     CHECKED_GCONNECT(G_OBJECT(new_bin), "drained", &SourceDrainedCallback,
                      this);
@@ -207,7 +218,7 @@ bool GstEnginePipeline::ReplaceDecodeBin
   }
 #endif
 
-  return ReplaceDecodeBin(new_bin);
+  return new_bin;
 }
 
 GstElement* GstEnginePipeline::CreateDecodeBinFromString(const char* pipeline) 
{
@@ -481,7 +492,10 @@ bool GstEnginePipeline::InitFromString(c
     return false;
   }
 
-  if (!ReplaceDecodeBin(new_bin)) return false;
+  if (!ReplaceDecodeBin(new_bin)) {
+    gst_object_unref(GST_OBJECT(new_bin));
+    return false;
+  }
 
   if (!Init()) return false;
   return gst_element_link(new_bin, audiobin_);
Index: 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/engines/gstenginepipeline.h
===================================================================
--- 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/src/engines/gstenginepipeline.h
    2019-03-27 12:30:08.162577265 +0200
+++ 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/engines/gstenginepipeline.h
 2019-03-27 12:31:00.916557865 +0200
@@ -151,6 +151,7 @@ signals:
 
   bool Init();
   GstElement* CreateDecodeBinFromString(const char* pipeline);
+  GstElement* CreateDecodeBinFromUrl(const QUrl& url);
 
   void UpdateVolume();
   void UpdateEqualizer();
++++++ 0001-Handle-case-where-a-lister-adds-a-device-before-load.patch ++++++
>From d041da18cc2e73ef2a6f1982546702b4844b9893 Mon Sep 17 00:00:00 2001
From: Jim Broadus <jbroa...@gmail.com>
Date: Sat, 23 Feb 2019 23:42:31 -0800
Subject: [PATCH] Handle case where a lister adds a device before loaded from
 database.

There is a small chance that a device lister is able to discover and add a
previously known device before it is added by the database loader thread.
In this case, copy the data that is user-settable to the existing DeviceInfo
object and destroy the object created from the database query.

This adds and utilizes a new FindEquivalentDevice method that compares the
device unique IDs. This could probably be made more robust as the unique
IDs for some listers may change. However, this is a problem with the database
storage implementation in general.
---
 src/devices/devicemanager.cpp | 27 ++++++++++++++++++++++++---
 src/devices/devicemanager.h   |  1 +
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/src/devices/devicemanager.cpp b/src/devices/devicemanager.cpp
index 940907509..11123229d 100644
--- a/src/devices/devicemanager.cpp
+++ b/src/devices/devicemanager.cpp
@@ -158,9 +158,22 @@ void DeviceManager::AddDeviceFromDb(DeviceInfo* info) {
   }
   info->LoadIcon(icons, info->friendly_name_);
 
-  beginInsertRows(ItemToIndex(root_), devices_.count(), devices_.count());
-  devices_ << info;
-  endInsertRows();
+  DeviceInfo* existing = FindEquivalentDevice(info);
+  if (existing) {
+    qLog(Info) << "Found existing device: " << info->friendly_name_;
+    // Update user configuration from the database.
+    existing->icon_name_ = info->icon_name_;
+    existing->icon_ = info->icon_;
+    QModelIndex idx = ItemToIndex(existing);
+    if (idx.isValid()) emit dataChanged(idx, idx);
+    // Discard the info loaded from the database.
+    delete info;
+  } else {
+    qLog(Info) << "Device added from database: " << info->friendly_name_;
+    beginInsertRows(ItemToIndex(root_), devices_.count(), devices_.count());
+    devices_ << info;
+    endInsertRows();
+  }
 }
 
 QVariant DeviceManager::data(const QModelIndex& idx, int role) const {
@@ -328,6 +341,14 @@ DeviceInfo* DeviceManager::FindDeviceByUrl(const 
QList<QUrl>& urls) const {
   return nullptr;
 }
 
+DeviceInfo* DeviceManager::FindEquivalentDevice(DeviceInfo* info) const {
+  for (const DeviceInfo::Backend& backend : info->backends_) {
+    DeviceInfo* match = FindDeviceById(backend.unique_id_);
+    if (match) return match;
+  }
+  return nullptr;
+}
+
 void DeviceManager::PhysicalDeviceAdded(const QString& id) {
   DeviceLister* lister = qobject_cast<DeviceLister*>(sender());
 
diff --git a/src/devices/devicemanager.h b/src/devices/devicemanager.h
index 80044256b..2caccb6f5 100644
--- a/src/devices/devicemanager.h
+++ b/src/devices/devicemanager.h
@@ -79,6 +79,7 @@ class DeviceManager : public SimpleTreeModel<DeviceInfo> {
 
   DeviceInfo* FindDeviceById(const QString& id) const;
   DeviceInfo* FindDeviceByUrl(const QList<QUrl>& url) const;
+  DeviceInfo* FindEquivalentDevice(DeviceInfo* info) const;
 
   // Actions on devices
   std::shared_ptr<ConnectedDevice> Connect(DeviceInfo* info);
-- 
2.16.4

++++++ 0001-Improved-support-for-APEv2-tags.-6280.patch ++++++
>From 8dd5750efa7565f341e8ed2cabfdc43588e3d441 Mon Sep 17 00:00:00 2001
From: smithjd15 <46389639+smithj...@users.noreply.github.com>
Date: Wed, 13 Feb 2019 23:37:44 -0700
Subject: [PATCH] Improved support for APEv2 tags. (#6280)

---
 ext/libclementine-tagreader/tagreader.cpp          | 215 ++++++++++++++++++++-
 .../tagreadermessages.proto                        |   1 +
 src/core/song.h                                    |   1 +
 3 files changed, 208 insertions(+), 9 deletions(-)

diff --git a/ext/libclementine-tagreader/tagreader.cpp 
b/ext/libclementine-tagreader/tagreader.cpp
index d3a89099f..900f042b4 100644
--- a/ext/libclementine-tagreader/tagreader.cpp
+++ b/ext/libclementine-tagreader/tagreader.cpp
@@ -28,6 +28,7 @@
 #include <QVector>
 
 #include <aifffile.h>
+#include <apefile.h>
 #include <asffile.h>
 #include <attachedpictureframe.h>
 #include <commentsframe.h>
@@ -161,6 +162,83 @@ void TagReader::ReadFile(const QString& filename,
   QString compilation;
   QString lyrics;
 
+  auto parseApeTag = [&](TagLib::APE::Tag* tag) {
+    const TagLib::APE::ItemListMap& items = tag->itemListMap();
+
+    // Find album artists
+    TagLib::APE::ItemListMap::ConstIterator it = items.find("ALBUM ARTIST");
+    if (it != items.end()) {
+      TagLib::StringList album_artists = it->second.toStringList();
+      if (!album_artists.isEmpty()) {
+        Decode(album_artists.front(), nullptr, song->mutable_albumartist());
+      }
+    }
+
+    // Find album cover art
+    if (items.find("COVER ART (FRONT)") != items.end()) {
+      song->set_art_automatic(kEmbeddedCover);
+    }
+
+    if (items.contains("COMPILATION")) {
+      compilation = TStringToQString(
+          TagLib::String::number(items["COMPILATION"].toString().toInt()));
+    }
+
+    if (items.contains("DISC")) {
+      disc = TStringToQString(
+          TagLib::String::number(items["DISC"].toString().toInt()));
+    }
+
+    if (items.contains("FMPS_RATING")) {
+      float rating =
+          TStringToQString(items["FMPS_RATING"].toString()).toFloat();
+      if (song->rating() <= 0 && rating > 0) {
+        song->set_rating(rating);
+      }
+    }
+    if (items.contains("FMPS_PLAYCOUNT")) {
+      int playcount =
+          TStringToQString(items["FMPS_PLAYCOUNT"].toString()).toFloat();
+      if (song->playcount() <= 0 && playcount > 0) {
+        song->set_playcount(playcount);
+      }
+    }
+    if (items.contains("FMPS_RATING_AMAROK_SCORE")) {
+      int score = 
TStringToQString(items["FMPS_RATING_AMAROK_SCORE"].toString())
+                      .toFloat() *
+                  100;
+      if (song->score() <= 0 && score > 0) {
+        song->set_score(score);
+      }
+    }
+
+    if (items.contains("BPM")) {
+      Decode(items["BPM"].toStringList().toString(", "), nullptr,
+             song->mutable_performer());
+    }
+
+    if (items.contains("PERFORMER")) {
+      Decode(items["PERFORMER"].toStringList().toString(", "), nullptr,
+             song->mutable_performer());
+    }
+
+    if (items.contains("COMPOSER")) {
+      Decode(items["COMPOSER"].toStringList().toString(", "), nullptr,
+             song->mutable_composer());
+    }
+
+    if (items.contains("GROUPING")) {
+      Decode(items["GROUPING"].toStringList().toString(" "), nullptr,
+             song->mutable_grouping());
+    }
+
+    if (items.contains("LYRICS")) {
+      Decode(items["LYRICS"].toString(), nullptr, song->mutable_lyrics());
+    }
+
+    Decode(tag->comment(), nullptr, song->mutable_comment());
+  };
+
   // Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the 
same
   // way;
   // apart, so we keep specific behavior for some formats by adding another
@@ -351,6 +429,21 @@ void TagReader::ReadFile(const QString& filename,
 
       Decode(mp4_tag->comment(), nullptr, song->mutable_comment());
     }
+  } else if (TagLib::APE::File* file =
+                 dynamic_cast<TagLib::APE::File*>(fileref->file())) {
+    if (file->tag()) {
+      parseApeTag(file->APETag());
+    }
+  } else if (TagLib::MPC::File* file =
+                 dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
+    if (file->tag()) {
+      parseApeTag(file->APETag());
+    }
+  } else if (TagLib::WavPack::File* file =
+                 dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
+    if (file->tag()) {
+      parseApeTag(file->APETag());
+    }
   }
 #ifdef TAGLIB_WITH_ASF
   else if (TagLib::ASF::File* file =
@@ -668,6 +761,8 @@ pb::tagreader::SongMetadata_Type TagReader::GuessFileType(
     return pb::tagreader::SongMetadata_Type_TRUEAUDIO;
   if (dynamic_cast<TagLib::WavPack::File*>(fileref->file()))
     return pb::tagreader::SongMetadata_Type_WAVPACK;
+  if (dynamic_cast<TagLib::APE::File*>(fileref->file()))
+    return pb::tagreader::SongMetadata_Type_APE;
 
   return pb::tagreader::SongMetadata_Type_UNKNOWN;
 }
@@ -691,6 +786,38 @@ bool TagReader::SaveFile(const QString& filename,
   fileref->tag()->setYear(song.year());
   fileref->tag()->setTrack(song.track());
 
+  auto saveApeTag = [&](TagLib::APE::Tag* tag) {
+    tag->setItem(
+        "disc",
+        TagLib::APE::Item("disc", TagLib::String::number(
+                                      song.disc() <= 0 - 1 ? 0 : 
song.disc())));
+    tag->setItem("bpm",
+                 TagLib::APE::Item(
+                     "bpm", TagLib::StringList(
+                                song.bpm() <= 0 - 1
+                                    ? "0"
+                                    : TagLib::String::number(song.bpm()))));
+    tag->setItem("composer",
+                 TagLib::APE::Item(
+                     "composer", TagLib::StringList(song.composer().c_str())));
+    tag->setItem("grouping",
+                 TagLib::APE::Item(
+                     "grouping", TagLib::StringList(song.grouping().c_str())));
+    tag->setItem("performer",
+                 TagLib::APE::Item("performer", TagLib::StringList(
+                                                    
song.performer().c_str())));
+    tag->setItem(
+        "album artist",
+        TagLib::APE::Item("album artist",
+                          TagLib::StringList(song.albumartist().c_str())));
+    tag->setItem("lyrics",
+                 TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
+    tag->setItem(
+        "compilation",
+        TagLib::APE::Item("compilation",
+                          TagLib::StringList(song.compilation() ? "1" : "0")));
+  };
+
   if (TagLib::MPEG::File* file =
           dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
     TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
@@ -723,17 +850,15 @@ bool TagReader::SaveFile(const QString& filename,
     tag->itemListMap()["aART"] = 
TagLib::StringList(song.albumartist().c_str());
     tag->itemListMap()["cpil"] =
         TagLib::StringList(song.compilation() ? "1" : "0");
+  } else if (TagLib::APE::File* file =
+                 dynamic_cast<TagLib::APE::File*>(fileref->file())) {
+    saveApeTag(file->APETag(true));
+  } else if (TagLib::MPC::File* file =
+                 dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
+    saveApeTag(file->APETag(true));
   } else if (TagLib::WavPack::File* file =
                  dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
-    TagLib::APE::Tag* tag = file->APETag(true);
-    if (!tag) return false;
-    tag->setArtist(StdStringToTaglibString(song.artist()));
-    tag->setAlbum(StdStringToTaglibString(song.album()));
-    tag->setTitle(StdStringToTaglibString(song.title()));
-    tag->setGenre(StdStringToTaglibString(song.genre()));
-    tag->setComment(StdStringToTaglibString(song.comment()));
-    tag->setYear(song.year());
-    tag->setTrack(song.track());
+    saveApeTag(file->APETag(true));
   }
 
   // Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the 
same
@@ -768,6 +893,19 @@ bool TagReader::SaveSongStatisticsToFile(
   if (!fileref || fileref->isNull())  // The file probably doesn't exist
     return false;
 
+  auto saveApeSongStats = [&](TagLib::APE::Tag* tag) {
+    tag->setItem(
+        "FMPS_Rating_Amarok_Score",
+        TagLib::APE::Item("FMPS_Rating_Amarok_Score",
+                          TagLib::StringList(QStringToTaglibString(
+                              QString::number(song.score() / 100.0)))));
+    tag->setItem(
+        "FMPS_PlayCount",
+        TagLib::APE::Item(
+            "FMPS_PlayCount",
+            TagLib::StringList(TagLib::String::number(song.playcount()))));
+  };
+
   if (TagLib::MPEG::File* file =
           dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
     TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
@@ -806,6 +944,15 @@ bool TagReader::SaveSongStatisticsToFile(
         QStringToTaglibString(QString::number(song.score() / 100.0)));
     tag->itemListMap()[kMP4_FMPS_Playcount_ID] =
         TagLib::StringList(TagLib::String::number(song.playcount()));
+  } else if (TagLib::APE::File* file =
+                 dynamic_cast<TagLib::APE::File*>(fileref->file())) {
+    saveApeSongStats(file->APETag(true));
+  } else if (TagLib::MPC::File* file =
+                 dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
+    saveApeSongStats(file->APETag(true));
+  } else if (TagLib::WavPack::File* file =
+                 dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
+    saveApeSongStats(file->APETag(true));
   } else {
     // Nothing to save: stop now
     return true;
@@ -841,6 +988,13 @@ bool TagReader::SaveSongRatingToFile(
   if (!fileref || fileref->isNull())  // The file probably doesn't exist
     return false;
 
+  auto saveApeSongRating = [&](TagLib::APE::Tag* tag) {
+    tag->setItem("FMPS_Rating",
+                 TagLib::APE::Item("FMPS_Rating",
+                                   TagLib::StringList(QStringToTaglibString(
+                                       QString::number(song.rating())))));
+  };
+
   if (TagLib::MPEG::File* file =
           dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
     TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
@@ -873,6 +1027,15 @@ bool TagReader::SaveSongRatingToFile(
     TagLib::MP4::Tag* tag = file->tag();
     tag->itemListMap()[kMP4_FMPS_Rating_ID] = TagLib::StringList(
         QStringToTaglibString(QString::number(song.rating())));
+  } else if (TagLib::APE::File* file =
+                 dynamic_cast<TagLib::APE::File*>(fileref->file())) {
+    saveApeSongRating(file->APETag(true));
+  } else if (TagLib::MPC::File* file =
+                 dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
+    saveApeSongRating(file->APETag(true));
+  } else if (TagLib::WavPack::File* file =
+                 dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
+    saveApeSongRating(file->APETag(true));
   } else {
     // Nothing to save: stop now
     return true;
@@ -1110,6 +1273,40 @@ QByteArray TagReader::LoadEmbeddedArt(const QString& 
filename) const {
     }
   }
 
+  // APE formats
+  auto apeTagCover = [&](TagLib::APE::Tag* tag) {
+    QByteArray cover;
+    const TagLib::APE::ItemListMap& items = tag->itemListMap();
+    TagLib::APE::ItemListMap::ConstIterator it =
+        items.find("COVER ART (FRONT)");
+    if (it != items.end()) {
+      TagLib::ByteVector data = it->second.binaryData();
+
+      int pos = data.find('\0') + 1;
+      if ((pos > 0) && (pos < data.size())) {
+          cover = QByteArray(data.data() + pos, data.size() - pos);
+      }
+    }
+
+    return cover;
+  };
+
+  TagLib::APE::File* ape_file = dynamic_cast<TagLib::APE::File*>(ref.file());
+  if (ape_file) {
+    return apeTagCover(ape_file->APETag());
+  }
+
+  TagLib::MPC::File* mpc_file = dynamic_cast<TagLib::MPC::File*>(ref.file());
+  if (mpc_file) {
+    return apeTagCover(mpc_file->APETag());
+  }
+
+  TagLib::WavPack::File* wavPack_file =
+      dynamic_cast<TagLib::WavPack::File*>(ref.file());
+  if (wavPack_file) {
+    return apeTagCover(wavPack_file->APETag());
+  }
+
   return QByteArray();
 }
 
diff --git a/ext/libclementine-tagreader/tagreadermessages.proto 
b/ext/libclementine-tagreader/tagreadermessages.proto
index b1b8e3ea7..531efb717 100644
--- a/ext/libclementine-tagreader/tagreadermessages.proto
+++ b/ext/libclementine-tagreader/tagreadermessages.proto
@@ -21,6 +21,7 @@ message SongMetadata {
     WAVPACK = 14;
     SPC = 15;
     VGM = 16;
+    APE = 17;
     STREAM = 99;
   }
 
diff --git a/src/core/song.h b/src/core/song.h
index bc109a622..e2fd9e8c2 100644
--- a/src/core/song.h
+++ b/src/core/song.h
@@ -108,6 +108,7 @@ class Song {
     Type_WavPack = 14,
     Type_Spc = 15,
     Type_VGM = 16,
+    Type_APE = 17,
     Type_Stream = 99,
   };
   static QString TextForFiletype(FileType type);
-- 
2.16.4

++++++ 0001-Prevent-UI-hang-during-device-scan.-6291.patch ++++++
>From 248f1d8596b69d58a885130bcada4799f116341c Mon Sep 17 00:00:00 2001
From: Jim Broadus <jbroa...@gmail.com>
Date: Wed, 20 Feb 2019 00:03:44 -0800
Subject: [PATCH] Prevent UI hang during device scan. (#6291)

When unmounting a device, the ConnectedDevice object is destroyed. The
FileSystemDevice destructor waits on its worker thread. If a scan is in
progress, this will block until completion.

There is an existing Stop method in the LibraryWatcher class that is intended to
stop long running operations. To fix, or at least significantly shorten this
hang, we'll call this before waiting for the thread to exit. Also add a
stop_requested check in the cover art scan.

In addition, add a call to Stop in the Library destructor, which has a similar
usage.
---
 src/devices/filesystemdevice.cpp | 1 +
 src/library/library.cpp          | 1 +
 src/library/librarywatcher.cpp   | 2 ++
 3 files changed, 4 insertions(+)

diff --git a/src/devices/filesystemdevice.cpp b/src/devices/filesystemdevice.cpp
index 3b1bd3ec0..342384718 100644
--- a/src/devices/filesystemdevice.cpp
+++ b/src/devices/filesystemdevice.cpp
@@ -71,6 +71,7 @@ void FilesystemDevice::Init() {
 }
 
 FilesystemDevice::~FilesystemDevice() {
+  watcher_->Stop();
   watcher_->deleteLater();
   watcher_thread_->exit();
   watcher_thread_->wait();
diff --git a/src/library/library.cpp b/src/library/library.cpp
index 6603e6043..60aafabda 100644
--- a/src/library/library.cpp
+++ b/src/library/library.cpp
@@ -121,6 +121,7 @@ Library::Library(Application* app, QObject* parent)
 }
 
 Library::~Library() {
+  watcher_->Stop();
   watcher_->deleteLater();
   watcher_thread_->exit();
   watcher_thread_->wait(5000 /* five seconds */);
diff --git a/src/library/librarywatcher.cpp b/src/library/librarywatcher.cpp
index 4cb7d934b..b77843c32 100644
--- a/src/library/librarywatcher.cpp
+++ b/src/library/librarywatcher.cpp
@@ -678,6 +678,8 @@ QString LibraryWatcher::PickBestImage(const QStringList& 
images) {
   QString biggest_path;
 
   for (const QString& path : filtered) {
+    if (stop_requested_) return "";
+
     QImage image(path);
     if (image.isNull()) continue;
 
-- 
2.16.4

++++++ clementine-hidden-systray-icon.patch ++++++
--- /var/tmp/diff_new_pack.Io6t6p/_old  2019-03-28 22:48:44.955054061 +0100
+++ /var/tmp/diff_new_pack.Io6t6p/_new  2019-03-28 22:48:44.991054054 +0100
@@ -1,7 +1,7 @@
-Index: dist/clementine.desktop
+Index: 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/dist/clementine.desktop
 ===================================================================
---- dist/clementine.desktop.orig       2019-01-15 09:06:34.053172793 +0200
-+++ dist/clementine.desktop    2019-01-15 09:06:42.977475930 +0200
+--- 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/dist/clementine.desktop
   2019-02-13 06:02:56.000000000 +0200
++++ 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/dist/clementine.desktop     
   2019-03-27 09:08:14.514962063 +0200
 @@ -36,7 +36,7 @@ TryExec=clementine
  Icon=clementine
  Terminal=false
@@ -11,10 +11,10 @@
  
MimeType=application/ogg;application/x-ogg;application/x-ogm-audio;audio/aac;audio/mp4;audio/mpeg;audio/mpegurl;audio/ogg;audio/vnd.rn-realaudio;audio/vorbis;audio/x-flac;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-ms-wma;audio/x-musepack;audio/x-oggflac;audio/x-pn-realaudio;audio/x-scpls;audio/x-speex;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-wav;video/x-ms-asf;x-content/audio-player;x-scheme-handler/zune;x-scheme-handler/itpc;x-scheme-handler/itms;x-scheme-handler/feed;
  Actions=Play;Pause;Stop;StopAfterCurrent;Previous;Next;
  
-Index: src/ui/mainwindow.cpp
+Index: 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/ui/mainwindow.cpp
 ===================================================================
---- src/ui/mainwindow.cpp.orig 2018-12-03 02:45:20.000000000 +0200
-+++ src/ui/mainwindow.cpp      2019-01-15 09:41:32.575111320 +0200
+--- 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/src/ui/mainwindow.cpp  
   2019-02-13 06:02:56.000000000 +0200
++++ Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/ui/mainwindow.cpp  
2019-03-27 09:08:14.514962063 +0200
 @@ -1041,6 +1041,7 @@ MainWindow::MainWindow(Application* app,
    if (hidden && (!QSystemTrayIcon::isSystemTrayAvailable() || !tray_icon_ ||
                   !tray_icon_->IsVisible())) {

++++++ clementine-moodbar-fpic.patch ++++++
--- /var/tmp/diff_new_pack.Io6t6p/_old  2019-03-28 22:48:45.135054028 +0100
+++ /var/tmp/diff_new_pack.Io6t6p/_new  2019-03-28 22:48:45.203054016 +0100
@@ -1,7 +1,7 @@
-Index: gst/moodbar/CMakeLists.txt
+Index: 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/gst/moodbar/CMakeLists.txt
 ===================================================================
---- gst/moodbar/CMakeLists.txt.orig    2018-10-05 11:56:49.000000000 +0200
-+++ gst/moodbar/CMakeLists.txt 2018-11-02 13:33:13.223890411 +0200
+--- 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/gst/moodbar/CMakeLists.txt
        2019-02-13 06:02:56.000000000 +0200
++++ 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/gst/moodbar/CMakeLists.txt  
   2019-03-27 09:08:06.882659408 +0200
 @@ -1,7 +1,7 @@
  cmake_minimum_required(VERSION 2.8.11)
  

++++++ clementine-udisks-headers.patch ++++++
--- /var/tmp/diff_new_pack.Io6t6p/_old  2019-03-28 22:48:45.359053988 +0100
+++ /var/tmp/diff_new_pack.Io6t6p/_new  2019-03-28 22:48:45.391053981 +0100
@@ -1,8 +1,8 @@
-Index: src/CMakeLists.txt
+Index: Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/CMakeLists.txt
 ===================================================================
---- src/CMakeLists.txt.orig    2018-11-02 14:30:43.839117342 +0200
-+++ src/CMakeLists.txt 2018-11-02 14:31:29.000773095 +0200
-@@ -946,6 +946,8 @@ if(HAVE_DBUS)
+--- 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/src/CMakeLists.txt     
   2019-02-13 06:02:56.000000000 +0200
++++ Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/CMakeLists.txt     
2019-03-27 09:07:45.077792580 +0200
+@@ -965,6 +965,8 @@ if(UNIX AND HAVE_DBUS)
    list(APPEND HEADERS ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.h)
    list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dbus/avahientrygroup.cpp)
  

++++++ use_system_qxtglobalshortcut.patch ++++++
Index: Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/CMakeLists.txt
===================================================================
--- Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/CMakeLists.txt     
2019-02-13 06:02:56.000000000 +0200
+++ Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/CMakeLists.txt  
2019-03-27 09:11:18.298146436 +0200
@@ -399,11 +399,10 @@ endif(NOT QTIOCOMPRESSOR_INCLUDE_DIRS OR
 # used to link to system installed qxt instead.
 option(USE_SYSTEM_QXT "Don't set this option unless your system Qxt library 
has been compiled with the Clementine patches in 3rdparty" OFF)
 if (USE_SYSTEM_QXT)
-  find_path(QXTCORE_INCLUDE_DIRS qxtglobal.h PATH_SUFFIXES qt5/QxtCore)
-  find_path(QXTGUI_INCLUDE_DIRS qxtglobalshortcut.h PATH_SUFFIXES 
qt5/QxtWidgets)
-  set(QXT_INCLUDE_DIRS ${QXTCORE_INCLUDE_DIRS} ${QXTGUI_INCLUDE_DIRS})
+  find_path(QXTGLOBALSHORTCUT_INCLUDE_DIRS qxtglobalshortcut.h PATH_SUFFIXES 
qxtglobalshortcut)
+  set(QXT_INCLUDE_DIRS ${QXTGLOBALSHORTCUT_INCLUDE_DIRS})
   # We only need its header. We don't need to link to QxtCore.
-  find_library(QXT_LIBRARIES QxtWidgets-qt5)
+  find_library(QXT_LIBRARIES qxtglobalshortcut)
 else (USE_SYSTEM_QXT)
   add_definitions(-DQXT_STATIC -DBUILD_QXT_GUI -DBUILD_QXT_CORE)
   set(QXT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qxt)
Index: 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/core/qxtglobalshortcutbackend.cpp
===================================================================
--- 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b.orig/src/core/qxtglobalshortcutbackend.cpp
      2019-02-13 06:02:56.000000000 +0200
+++ 
Clementine-36cc5b82f4daf5c2d4e93dc8072665e5a3ca622b/src/core/qxtglobalshortcutbackend.cpp
   2019-03-27 09:11:18.298146436 +0200
@@ -41,7 +41,7 @@ bool QxtGlobalShortcutBackend::DoRegiste
 void QxtGlobalShortcutBackend::AddShortcut(QAction* action) {
   if (action->shortcut().isEmpty()) return;
   QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(action->shortcut(), 
this);
-  connect(shortcut, SIGNAL(activated()), action, SLOT(trigger()));
+  connect(shortcut, &QxtGlobalShortcut::activated, action, &QAction::trigger);
   shortcuts_ << shortcut;
 }
 

Reply via email to