diff --git a/modules/gui/qt/widgets/qml/DragItem.qml b/modules/gui/qt/widgets/qml/DragItem.qml
index 2ceb9a269337052e67c06f2e3e9d1bf48f79e057..3ecc2dabfb10c932d561f095a002db9d7228ba2c 100644
--- a/modules/gui/qt/widgets/qml/DragItem.qml
+++ b/modules/gui/qt/widgets/qml/DragItem.qml
@@ -426,12 +426,15 @@ Item {
     Repeater {
         id: coverRepeater
 
-        model: dragItem._covers
+        readonly property var _model: dragItem._covers
 
-        property int notReadyCount: count
+        property int notReadyCount
 
-        onModelChanged: {
-            notReadyCount = count
+        on_ModelChanged: {
+            // Repeater signals model and count change after it reloads the items.
+            // So we need to adjust the ready count before that:
+            notReadyCount = _model.length
+            model = _model
         }
 
         onNotReadyCountChanged: {
@@ -450,53 +453,67 @@ Item {
             width: dragItem.coverSize
             height: dragItem.coverSize
 
-            Rectangle {
-                id: bg
-
-                radius: coverRepeater.count > 1 ? dragItem.coverSize : 0.0
-                anchors.fill: parent
-                color: theme.bg.primary
-
-                DefaultShadow {
-                    anchors.centerIn: parent
-
-                    sourceItem: bg
-                }
-            }
-
             Widgets.ImageExt {
                 id: artworkCover
 
                 anchors.centerIn: parent
                 width: coverSize
                 height: coverSize
-                radius: bg.radius
+                radius: coverRepeater.count > 1 ? dragItem.coverSize : 0.0
                 source: modelData.artwork ?? ""
                 sourceSize: dragItem.imageSourceSize ?? Qt.size(width * eDPR, height * eDPR)
+                backgroundColor: theme.bg.primary
+                borderWidth: VLCStyle.dp(1, VLCStyle.scale)
+                borderColor: theme.border
                 fillMode: Image.PreserveAspectCrop
 
+                // FIXME: Qt bug, asynchronous + texture provider + custom shader does not work properly with `grabToImage()`:
+                asynchronous: false
+
                 readonly property real eDPR: MainCtx.effectiveDevicePixelRatio(Window.window)
+                property bool _triggerReadiness: false
 
-                onStatusChanged: {
-                    if (status === Image.Ready)
+                on_TriggerReadinessChanged: {
+                    // If it was already true (readiness already signalled), do not decrease the counter.
+                    // This handler is only called when the property changes.
+                    if (_triggerReadiness) {
                         coverRepeater.notReadyCount -= 1
-                    else if (status === Image.Error) {
+                    }
+                }
+
+                readonly property var _combinedStatus: [status, shaderStatus]
+
+                on_CombinedStatusChanged: {
+                    // Qt `ShaderEffect` documentation states:
+                    // > When runtime compilation is not in use and the shader properties
+                    // > refer to files with bytecode, the status is always Compiled.
+                    // However this is not correct, the status is reported to be "uncompiled" initially.
+                    // And sometimes the status remains "uncompiled" even when the shader is in use, for
+                    // that reason I only care about `ShaderEffect.Error` here and not `Compiled`.
+
+                    if (shaderStatus === ShaderEffect.Error) {
+                        _triggerReadiness = true // Not much to do in this case, shader could not be loaded.
+                        return
+                    }
+
+                    if (status === Image.Error) {
                         const fallbackSource = modelData.fallback ?? defaultCover
-                        if (source === fallbackSource)
-                            coverRepeater.notReadyCount -= 1
-                        else
+                        if (source === fallbackSource) {
+                            _triggerReadiness = true // Not much to do in this case either, fallback image could not be loaded.
+                        } else {
                             source = fallbackSource
+                        }
+                    } else if (status === Image.Ready /* && shaderStatus === ShaderEffect.Compiled */) {
+                        // FIXME: When Qt starts to report `ShaderEffect.Compiled` properly, start using it.
+                        _triggerReadiness = true // Only in this case the image is loaded and shown.
                     }
                 }
-            }
 
-            Rectangle {
-                // for cover border
-                color: "transparent"
-                border.width: VLCStyle.dp(1, VLCStyle.scale)
-                border.color: theme.border
-                anchors.fill: parent
-                radius: bg.radius
+                DefaultShadow {
+                    anchors.centerIn: parent
+
+                    sourceItem: parent
+                }
             }
         }
     }
diff --git a/modules/gui/qt/widgets/qml/ImageExt.qml b/modules/gui/qt/widgets/qml/ImageExt.qml
index 551e5f5fde60e35e8f2d5f504abde53c741fb590..2013a973779ea9cc3c8dd44c86544ed26e03f34d 100644
--- a/modules/gui/qt/widgets/qml/ImageExt.qml
+++ b/modules/gui/qt/widgets/qml/ImageExt.qml
@@ -51,6 +51,7 @@ Item {
     property alias sourceSize: image.sourceSize
     property alias sourceClipRect: image.sourceClipRect
     property alias status: image.status
+    property alias shaderStatus: shaderEffect.status
     property alias cache: image.cache
 
     // Padding represents how much the content is shrunk. For now this is a readonly property.