Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package mpvqt for openSUSE:Factory checked 
in at 2026-05-24 19:35:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mpvqt (Old)
 and      /work/SRC/openSUSE:Factory/.mpvqt.new.2084 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mpvqt"

Sun May 24 19:35:12 2026 rev:4 rq:1354901 version:1.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/mpvqt/mpvqt.changes      2025-04-20 
20:04:01.327214697 +0200
+++ /work/SRC/openSUSE:Factory/.mpvqt.new.2084/mpvqt.changes    2026-05-24 
19:36:17.888859757 +0200
@@ -1,0 +2,10 @@
+Sun May 24 06:59:39 UTC 2026 - Christophe Marin <[email protected]>
+
+- Update to 1.2.0
+  * cmake: install Qt metatypes files
+  * mpvcontroller: eventHandler: handle `stop` reason for
+    `MPV_EVENT_END_FILE`
+  * Refactor code
+  * Update examples
+
+-------------------------------------------------------------------

Old:
----
  mpvqt-1.1.1.tar.xz
  mpvqt-1.1.1.tar.xz.sig

New:
----
  mpvqt-1.2.0.tar.xz
  mpvqt-1.2.0.tar.xz.sig

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

Other differences:
------------------
++++++ mpvqt.spec ++++++
--- /var/tmp/diff_new_pack.c7PjwV/_old  2026-05-24 19:36:18.544886652 +0200
+++ /var/tmp/diff_new_pack.c7PjwV/_new  2026-05-24 19:36:18.548886816 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package mpvqt
 #
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,12 +16,12 @@
 #
 
 
-%define kf6_version 6.0.0
+%define kf6_version 6.15.0
 %define qt6_version 6.5.0
 
 %bcond_without released
 Name:           mpvqt
-Version:        1.1.1
+Version:        1.2.0
 Release:        0
 Summary:        Libmpv wrapper for QtQuick2 and QML
 License:        LGPL-2.1-or-later
@@ -39,17 +39,17 @@
 %description
 MpvQt is a libmpv wrapper for QtQuick2 and QML.
 
-%package -n libMpvQt2
+%package -n libMpvQt3
 Summary:        Libmpv wrapper for QtQuick2 and QML
 # Marked as runtime deps in the build system, but looks unneeded
 # Recommends:     (yt-dlp or youtube-dl)
 
-%description -n libMpvQt2
+%description -n libMpvQt3
 MpvQt is a libmpv wrapper for QtQuick2 and QML.
 
 %package devel
 Summary:        Development files for mpvqt
-Requires:       libMpvQt2 = %{version}
+Requires:       libMpvQt3 = %{version}
 Requires:       cmake(Qt6Quick) >= %{qt6_version}
 Requires:       pkgconfig(mpv)
 
@@ -67,9 +67,9 @@
 %install
 %kf6_install
 
-%ldconfig_scriptlets -n libMpvQt2
+%ldconfig_scriptlets -n libMpvQt3
 
-%files -n libMpvQt2
+%files -n libMpvQt3
 %license LICENSES/*
 %{_kf6_libdir}/libMpvQt.so.*
 
@@ -78,4 +78,7 @@
 %{_includedir}/MpvQt/
 %{_kf6_cmakedir}/MpvQt/
 %{_kf6_libdir}/libMpvQt.so
+%if %{pkg_vcmp kf6-extra-cmake-modules >= 6.27}
+%{_qt6_metatypesdir}/qt6mpvqt_metatypes.json
+%endif
 

++++++ mpvqt-1.1.1.tar.xz -> mpvqt-1.2.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/.gitlab-ci.yml 
new/mpvqt-1.2.0/.gitlab-ci.yml
--- old/mpvqt-1.1.1/.gitlab-ci.yml      2025-04-15 16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/.gitlab-ci.yml      2026-05-18 10:42:52.000000000 +0200
@@ -8,4 +8,4 @@
       - /gitlab-templates/linux-qt6.yml
       - /gitlab-templates/linux-qt6-next.yml
       - /gitlab-templates/freebsd-qt6.yml
-      - /gitlab-templates/android-qt6.yml
+      # - /gitlab-templates/android-qt6.yml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/.kde-ci.yml new/mpvqt-1.2.0/.kde-ci.yml
--- old/mpvqt-1.1.1/.kde-ci.yml 2025-04-15 16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/.kde-ci.yml 2026-05-18 10:42:52.000000000 +0200
@@ -5,3 +5,7 @@
 - 'on': ['@all']
   'require':
     'frameworks/extra-cmake-modules': '@latest-kf6'
+
+Options:
+  require-passing-tests-on: [ '@all' ]
+  enable-lsan: True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/CMakeLists.txt 
new/mpvqt-1.2.0/CMakeLists.txt
--- old/mpvqt-1.1.1/CMakeLists.txt      2025-04-15 16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/CMakeLists.txt      2026-05-18 10:42:52.000000000 +0200
@@ -4,19 +4,52 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
-cmake_minimum_required(VERSION 3.15)
+cmake_minimum_required(VERSION 3.16...3.31)
 
-project(MpvQt VERSION 1.1.1 LANGUAGES CXX)
+project(MpvQt VERSION 1.2.0 LANGUAGES CXX)
 
 set(REQUIRED_QT_VERSION 6.5.0)
 
 include(FeatureSummary)
 
-find_package(ECM 6.0.0 NO_MODULE)
+find_package(ECM 6.15.0 NO_MODULE)
 set_package_properties(ECM PROPERTIES TYPE REQUIRED
     URL "https://api.kde.org/ecm/";
-    DESCRIPTION "extra cmake modules")
-set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+    DESCRIPTION "Extra modules and scripts for CMake")
+
+if(ECM_FOUND)
+    set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+    include(KDEInstallDirs)
+    include(KDECMakeSettings NO_POLICY_SCOPE)
+    include(KDECompilerSettings NO_POLICY_SCOPE)
+    include(ECMGenerateHeaders)
+    include(ECMGenerateExportHeader)
+
+    set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/MpvQt")
+
+    include(KDEClangFormat)
+    file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.hpp *.c)
+    kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
+
+    include(KDEGitCommitHooks)
+    kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
+
+    include(ECMSetupVersion)
+    ecm_setup_version(PROJECT
+        VARIABLE_PREFIX MPVQT
+        VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/src/mpvqt_version.h"
+        PACKAGE_VERSION_FILE 
"${CMAKE_CURRENT_BINARY_DIR}/MpvQtConfigVersion.cmake"
+        SOVERSION 3
+    )
+
+    include(CMakePackageConfigHelpers)
+    configure_package_config_file(
+        "${CMAKE_CURRENT_SOURCE_DIR}/MpvQtConfig.cmake.in"
+        "${CMAKE_CURRENT_BINARY_DIR}/MpvQtConfig.cmake"
+        INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
+    )
+endif()
 
 find_package(Qt6Quick ${REQUIRED_QT_VERSION})
 set_package_properties(Qt6Quick PROPERTIES TYPE REQUIRED)
@@ -27,39 +60,10 @@
 
 feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
 
-include(KDEInstallDirs)
-include(KDECMakeSettings NO_POLICY_SCOPE)
-include(KDECompilerSettings NO_POLICY_SCOPE)
-include(ECMGenerateHeaders)
-include(ECMGenerateExportHeader)
-
-set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/MpvQt")
-
-include(KDEClangFormat)
-file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.hpp *.c)
-kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
-
-include(KDEGitCommitHooks)
-kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
-
-include(ECMSetupVersion)
-ecm_setup_version(PROJECT
-    VARIABLE_PREFIX MPVQT
-    VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/src/mpvqt_version.h"
-    PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/MpvQtConfigVersion.cmake"
-    SOVERSION 2
-)
-
-include(CMakePackageConfigHelpers)
-configure_package_config_file(
-    "${CMAKE_CURRENT_SOURCE_DIR}/MpvQtConfig.cmake.in"
-    "${CMAKE_CURRENT_BINARY_DIR}/MpvQtConfig.cmake"
-    INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
-)
-
 add_subdirectory(src)
 
 option(BUILD_EXAMPLES "Whether to build the examples" OFF)
 if (BUILD_EXAMPLES)
     add_subdirectory(examples/video-player)
+    add_subdirectory(examples/multiple-video-players)
 endif()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/README.md new/mpvqt-1.2.0/README.md
--- old/mpvqt-1.1.1/README.md   2025-04-15 16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/README.md   2026-05-18 10:42:52.000000000 +0200
@@ -8,10 +8,44 @@
 
 ## How to use
 
-- Create a class extending `MpvAbstractItem` (check the 
[mpvitem.h](examples/video-player/mpvitem.h)/[mpvitem.cpp](examples/video-player/mpvitem.cpp)
 files in the example)
-- Register the class `qmlRegisterType<ClassName>("com.example.mpv", 1, 0, 
"NameUsedInQml");`
-- In your qml file import mpv `import com.example.mpv 1.0`
-- Then create an instance `NameUsedInQml {}` (check the 
[Main.qml](examples/video-player/Main.qml) file in the example)
+- Create a class extending `MpvAbstractItem`
+
+```c++
+class MpvItem : public MpvAbstractItem
+{
+    Q_OBJECT
+    QML_ELEMENT
+    // ...
+}
+```
+- Add the class to a qml module
+
+```cmake
+qt_add_qml_module(videoplayer
+    URI com.example.mpvqt
+    QML_FILES
+        Main.qml
+)
+
+target_sources(videoplayer
+    PRIVATE
+        mpvitem.h mpvitem.cpp
+)
+```
+or
+```cmake
+qt_add_qml_module(videoplayer
+    URI com.example.mpvqt
+    QML_FILES
+        Main.qml
+    SOURCES
+        mpvitem.h mpvitem.cpp
+)
+```
+
+- In your qml file import mpv `import com.example.mpvqt`
+- Then create an instance `MpvItem {}`, `MpvItem` is the class name extending 
MpvAbstractItem,
+ if you want to use another name replace `QML_ELEMENT` with 
`QML_NAMED_ELEMENT(VideoPlayer)`
 
 ## Config file
 MpvQt loads a config file located at `<config_folder>/mpvqt/mpvqt.conf`,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mpvqt-1.1.1/examples/multiple-video-players/CMakeLists.txt 
new/mpvqt-1.2.0/examples/multiple-video-players/CMakeLists.txt
--- old/mpvqt-1.1.1/examples/multiple-video-players/CMakeLists.txt      
1970-01-01 01:00:00.000000000 +0100
+++ new/mpvqt-1.2.0/examples/multiple-video-players/CMakeLists.txt      
2026-05-18 10:42:52.000000000 +0200
@@ -0,0 +1,48 @@
+#
+# SPDX-FileCopyrightText: 2023 George Florea Bănuș <[email protected]>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+cmake_minimum_required(VERSION 3.16...3.31)
+
+project(multiplevideoplayers LANGUAGES CXX)
+
+set(CMAKE_AUTOUIC ON)
+
+find_package(ECM 6.0.0 REQUIRED NO_MODULE)
+set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+include(KDEInstallDirs)
+include(KDECMakeSettings NO_POLICY_SCOPE)
+include(KDECompilerSettings NO_POLICY_SCOPE)
+
+find_package(Qt6 COMPONENTS Core Quick REQUIRED)
+
+find_package(MpvQt)
+
+qt_standard_project_setup(REQUIRES 6.5)
+qt_add_executable(multiplevideoplayers)
+
+qt_policy(SET QTP0001 NEW)
+qt_add_qml_module(multiplevideoplayers
+    URI com.example.mpvqt
+    QML_FILES
+        Main.qml
+)
+
+target_sources(multiplevideoplayers
+    PRIVATE
+        main.cpp
+        mpvitem.h mpvitem.cpp
+        mpvproperties.h
+)
+
+target_link_libraries(multiplevideoplayers
+    PRIVATE
+        Qt6::Core
+        Qt6::Quick
+        MpvQt::MpvQt
+)
+
+install(TARGETS multiplevideoplayers DESTINATION 
${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/examples/multiple-video-players/Main.qml 
new/mpvqt-1.2.0/examples/multiple-video-players/Main.qml
--- old/mpvqt-1.1.1/examples/multiple-video-players/Main.qml    1970-01-01 
01:00:00.000000000 +0100
+++ new/mpvqt-1.2.0/examples/multiple-video-players/Main.qml    2026-05-18 
10:42:52.000000000 +0200
@@ -0,0 +1,128 @@
+/*
+ * SPDX-FileCopyrightText: 2023 George Florea Bănuș <[email protected]>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import com.example.mpvqt
+
+Window {
+    id: window
+
+    width: 1000
+    height: 600
+    visible: true
+    title: "multiple videos"
+
+    function addTab(videoFile: string) {
+        const index = tabBar.count
+        const tabPage = mpvComponent.createObject(tabLayout, {index: index, 
videoFile: videoFile})
+        var tab = tabComponent.createObject(tabBar, {index: index, mpvItem: 
tabPage})
+
+        tabBar.currentIndex = index
+    }
+
+    Component.onCompleted: {
+        // window.addTab("/path/to/video/file1.mkv")
+        // window.addTab("/path/to/video/file2.mkv")
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+        spacing: 0
+
+        TabBar {
+            id: tabBar
+
+            background: Item {}
+
+            Layout.fillWidth: true
+            Layout.preferredHeight: 50
+
+        }
+
+        StackLayout {
+            id: tabLayout
+
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            currentIndex: tabBar.currentIndex
+        }
+    }
+
+    Component {
+        id: tabComponent
+
+        TabButton {
+            id: tabDelegate
+
+            required property int index
+            required property MpvItem mpvItem
+
+            implicitHeight: parent.height
+            text: `Tab ${index}`
+            width: 190
+
+            TapHandler {
+                acceptedButtons: Qt.MiddleButton
+                onSingleTapped: {
+                    tabDelegate.destroy()
+                    tabDelegate.mpvItem.destroy()
+                }
+            }
+        }
+    }
+
+    Component {
+        id: mpvComponent
+
+        MpvItem {
+            id: mpv
+
+            required property int index
+            required property string videoFile
+
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            // do not load file on Component.onCompleted
+            onReady: loadFile([videoFile])
+            volume: index === 0 ? 80 : 0
+
+            Rectangle {
+                width: mpv.width
+                height: 40
+                color: Qt.alpha("#333", 0.5)
+                anchors.bottom: mpv.bottom
+
+                RowLayout {
+                    anchors.fill: parent
+
+                    Text {
+                        text: `${mpv.formattedPosition} / 
${mpv.formattedDuration}`
+                        color: "#fff"
+                        Layout.leftMargin: 10
+                    }
+
+                    Slider {
+                        id: slider
+                        from: 0
+                        to: mpv.duration
+                        value: mpv.position
+                        onValueChanged: mpv.position = value
+
+                        Layout.fillWidth: true
+                        Layout.fillHeight: true
+                        Layout.rightMargin: 10
+                    }
+                }
+            }
+        }
+    }
+
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/examples/multiple-video-players/main.cpp 
new/mpvqt-1.2.0/examples/multiple-video-players/main.cpp
--- old/mpvqt-1.1.1/examples/multiple-video-players/main.cpp    1970-01-01 
01:00:00.000000000 +0100
+++ new/mpvqt-1.2.0/examples/multiple-video-players/main.cpp    2026-05-18 
10:42:52.000000000 +0200
@@ -0,0 +1,25 @@
+/*
+ * SPDX-FileCopyrightText: 2023 George Florea Bănuș <[email protected]>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QQuickWindow>
+
+int main(int argc, char *argv[])
+{
+    QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
+    QGuiApplication app(argc, argv);
+
+    QQmlApplicationEngine engine(&app);
+    // clang-format off
+    QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, 
&app, []() {
+        QCoreApplication::exit(-1);
+    }, Qt::QueuedConnection);
+    // clang-format on
+    engine.loadFromModule("com.example.mpvqt", "Main");
+
+    return app.exec();
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mpvqt-1.1.1/examples/multiple-video-players/mpvitem.cpp 
new/mpvqt-1.2.0/examples/multiple-video-players/mpvitem.cpp
--- old/mpvqt-1.1.1/examples/multiple-video-players/mpvitem.cpp 1970-01-01 
01:00:00.000000000 +0100
+++ new/mpvqt-1.2.0/examples/multiple-video-players/mpvitem.cpp 2026-05-18 
10:42:52.000000000 +0200
@@ -0,0 +1,180 @@
+/*
+ * SPDX-FileCopyrightText: 2023 George Florea Bănuș <[email protected]>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "mpvitem.h"
+
+#include <MpvController>
+
+#include "mpvproperties.h"
+
+MpvItem::MpvItem(QQuickItem *parent)
+    : MpvAbstractItem(parent)
+{
+    observeProperty(MpvProperties::self()->MediaTitle, MPV_FORMAT_STRING);
+    observeProperty(MpvProperties::self()->Position, MPV_FORMAT_DOUBLE);
+    observeProperty(MpvProperties::self()->Duration, MPV_FORMAT_DOUBLE);
+    observeProperty(MpvProperties::self()->Pause, MPV_FORMAT_FLAG);
+    observeProperty(MpvProperties::self()->Volume, MPV_FORMAT_INT64);
+
+    setupConnections();
+    setProperty(QStringLiteral("mute"), false);
+    setProperty(QStringLiteral("volume"), 80);
+}
+
+void MpvItem::setupConnections()
+{
+    // clang-format off
+    connect(mpvController(), &MpvController::propertyChanged,
+            this, &MpvItem::onPropertyChanged, Qt::QueuedConnection);
+
+    connect(mpvController(), &MpvController::fileStarted,
+            this, &MpvItem::fileStarted, Qt::QueuedConnection);
+
+    connect(mpvController(), &MpvController::fileLoaded,
+            this, &MpvItem::fileLoaded, Qt::QueuedConnection);
+
+    connect(mpvController(), &MpvController::endFile,
+            this, &MpvItem::endFile, Qt::QueuedConnection);
+
+    connect(mpvController(), &MpvController::videoReconfig,
+            this, &MpvItem::videoReconfig, Qt::QueuedConnection);
+
+    connect(mpvController(), &MpvController::asyncReply,
+            this, &MpvItem::onAsyncReply, Qt::QueuedConnection);
+    // clang-format on
+}
+
+void MpvItem::onPropertyChanged(const QString &property, const QVariant &value)
+{
+    if (property == MpvProperties::self()->MediaTitle) {
+        Q_EMIT mediaTitleChanged();
+
+    } else if (property == MpvProperties::self()->Position) {
+        m_formattedPosition = formatTime(value.toDouble());
+        Q_EMIT positionChanged();
+
+    } else if (property == MpvProperties::self()->Duration) {
+        m_formattedDuration = formatTime(value.toDouble());
+        Q_EMIT durationChanged();
+
+    } else if (property == MpvProperties::self()->Pause) {
+        Q_EMIT pauseChanged();
+
+    } else if (property == MpvProperties::self()->Volume) {
+        Q_EMIT volumeChanged();
+    }
+}
+
+void MpvItem::onAsyncReply(const QVariant &data, mpv_event event)
+{
+    switch (static_cast<AsyncIds>(event.reply_userdata)) {
+    case AsyncIds::None: {
+        break;
+    }
+    case AsyncIds::SetVolume: {
+        qDebug() << "onSetPropertyReply" << event.reply_userdata;
+        break;
+    }
+    case AsyncIds::GetVolume: {
+        qDebug() << "onGetPropertyReply" << event.reply_userdata << data;
+        break;
+    }
+    case AsyncIds::ExpandText: {
+        qDebug() << "onGetPropertyReply" << event.reply_userdata << data;
+        break;
+    }
+    }
+}
+
+QString MpvItem::formatTime(const double time)
+{
+    int totalNumberOfSeconds = static_cast<int>(time);
+    int seconds = totalNumberOfSeconds % 60;
+    int minutes = (totalNumberOfSeconds / 60) % 60;
+    int hours = (totalNumberOfSeconds / 60 / 60);
+
+    QString timeString =
+        QStringLiteral("%1:%2:%3").arg(hours, 2, 10, 
QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, 
QLatin1Char('0'));
+
+    return timeString;
+}
+
+void MpvItem::loadFile(const QString &file)
+{
+    auto url = QUrl::fromUserInput(file);
+    if (m_currentUrl != url) {
+        m_currentUrl = url;
+        Q_EMIT currentUrlChanged();
+    }
+
+    command(QStringList() << QStringLiteral("loadfile") << 
m_currentUrl.toString(QUrl::PreferLocalFile));
+}
+
+QString MpvItem::mediaTitle()
+{
+    return getProperty(MpvProperties::self()->MediaTitle).toString();
+}
+
+double MpvItem::position()
+{
+    return getProperty(MpvProperties::self()->Position).toDouble();
+}
+
+void MpvItem::setPosition(double value)
+{
+    if (qFuzzyCompare(value, position())) {
+        return;
+    }
+    setPropertyAsync(MpvProperties::self()->Position, value);
+}
+
+double MpvItem::duration()
+{
+    return getProperty(MpvProperties::self()->Duration).toDouble();
+}
+
+bool MpvItem::pause()
+{
+    return getProperty(MpvProperties::self()->Pause).toBool();
+}
+
+void MpvItem::setPause(bool value)
+{
+    if (value == pause()) {
+        return;
+    }
+    setPropertyAsync(MpvProperties::self()->Pause, value);
+}
+
+int MpvItem::volume()
+{
+    return getProperty(MpvProperties::self()->Volume).toInt();
+}
+
+void MpvItem::setVolume(int value)
+{
+    if (value == volume()) {
+        return;
+    }
+    setPropertyAsync(MpvProperties::self()->Volume, value);
+}
+
+QString MpvItem::formattedDuration() const
+{
+    return m_formattedDuration;
+}
+
+QString MpvItem::formattedPosition() const
+{
+    return m_formattedPosition;
+}
+
+QUrl MpvItem::currentUrl() const
+{
+    return m_currentUrl;
+}
+
+#include "moc_mpvitem.cpp"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mpvqt-1.1.1/examples/multiple-video-players/mpvitem.h 
new/mpvqt-1.2.0/examples/multiple-video-players/mpvitem.h
--- old/mpvqt-1.1.1/examples/multiple-video-players/mpvitem.h   1970-01-01 
01:00:00.000000000 +0100
+++ new/mpvqt-1.2.0/examples/multiple-video-players/mpvitem.h   2026-05-18 
10:42:52.000000000 +0200
@@ -0,0 +1,86 @@
+/*
+ * SPDX-FileCopyrightText: 2023 George Florea Bănuș <[email protected]>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef MPVOBJECT_H
+#define MPVOBJECT_H
+
+#include <MpvAbstractItem>
+#include <qqmlintegration.h>
+
+class MpvRenderer;
+
+class MpvItem : public MpvAbstractItem
+{
+    Q_OBJECT
+    QML_ELEMENT
+public:
+    explicit MpvItem(QQuickItem *parent = nullptr);
+    ~MpvItem() = default;
+
+    enum class AsyncIds {
+        None,
+        SetVolume,
+        GetVolume,
+        ExpandText,
+    };
+    Q_ENUM(AsyncIds)
+
+    Q_PROPERTY(QString mediaTitle READ mediaTitle NOTIFY mediaTitleChanged)
+    QString mediaTitle();
+
+    Q_PROPERTY(double position READ position WRITE setPosition NOTIFY 
positionChanged)
+    double position();
+    void setPosition(double value);
+
+    Q_PROPERTY(double duration READ duration NOTIFY durationChanged)
+    double duration();
+
+    Q_PROPERTY(QString formattedPosition READ formattedPosition NOTIFY 
positionChanged)
+    QString formattedPosition() const;
+
+    Q_PROPERTY(QString formattedDuration READ formattedDuration NOTIFY 
durationChanged)
+    QString formattedDuration() const;
+
+    Q_PROPERTY(bool pause READ pause WRITE setPause NOTIFY pauseChanged)
+    bool pause();
+    void setPause(bool value);
+
+    Q_PROPERTY(int volume READ volume WRITE setVolume NOTIFY volumeChanged)
+    int volume();
+    void setVolume(int value);
+
+    Q_PROPERTY(QUrl currentUrl READ currentUrl NOTIFY currentUrlChanged)
+    QUrl currentUrl() const;
+
+    Q_INVOKABLE void loadFile(const QString &file);
+
+Q_SIGNALS:
+    void mediaTitleChanged();
+    void currentUrlChanged();
+    void positionChanged();
+    void durationChanged();
+    void pauseChanged();
+    void volumeChanged();
+
+    void fileStarted();
+    void fileLoaded();
+    void endFile(QString reason);
+    void videoReconfig();
+
+private:
+    void setupConnections();
+    void onPropertyChanged(const QString &property, const QVariant &value);
+    void onAsyncReply(const QVariant &data, mpv_event event);
+    QString formatTime(const double time);
+
+    double m_position{0.0};
+    QString m_formattedPosition;
+    double m_duration{0.0};
+    QString m_formattedDuration;
+    QUrl m_currentUrl;
+};
+
+#endif // MPVOBJECT_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/mpvqt-1.1.1/examples/multiple-video-players/mpvproperties.h 
new/mpvqt-1.2.0/examples/multiple-video-players/mpvproperties.h
--- old/mpvqt-1.1.1/examples/multiple-video-players/mpvproperties.h     
1970-01-01 01:00:00.000000000 +0100
+++ new/mpvqt-1.2.0/examples/multiple-video-players/mpvproperties.h     
2026-05-18 10:42:52.000000000 +0200
@@ -0,0 +1,53 @@
+/*
+ * SPDX-FileCopyrightText: 2023 George Florea Bănuș <[email protected]>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef MPVPROPERTIES_H
+#define MPVPROPERTIES_H
+
+#include <QObject>
+#include <qqmlintegration.h>
+
+class MpvProperties : public QObject
+{
+    Q_OBJECT
+    QML_ELEMENT
+    QML_SINGLETON
+
+public:
+    explicit MpvProperties(QObject *parent = nullptr)
+        : QObject(parent)
+    {
+    }
+
+    static MpvProperties *self()
+    {
+        static MpvProperties p;
+        return &p;
+    }
+
+    Q_PROPERTY(QString MediaTitle MEMBER MediaTitle CONSTANT)
+    const QString MediaTitle{QStringLiteral("media-title")};
+
+    Q_PROPERTY(QString Position MEMBER Position CONSTANT)
+    const QString Position{QStringLiteral("time-pos")};
+
+    Q_PROPERTY(QString Duration MEMBER Duration CONSTANT)
+    const QString Duration{QStringLiteral("duration")};
+
+    Q_PROPERTY(QString Pause MEMBER Pause CONSTANT)
+    const QString Pause{QStringLiteral("pause")};
+
+    Q_PROPERTY(QString Volume MEMBER Volume CONSTANT)
+    const QString Volume{QStringLiteral("volume")};
+
+    Q_PROPERTY(QString Mute MEMBER Mute CONSTANT)
+    const QString Mute{QStringLiteral("mute")};
+
+private:
+    Q_DISABLE_COPY_MOVE(MpvProperties)
+};
+
+#endif // MPVPROPERTIES_H
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/examples/video-player/CMakeLists.txt 
new/mpvqt-1.2.0/examples/video-player/CMakeLists.txt
--- old/mpvqt-1.1.1/examples/video-player/CMakeLists.txt        2025-04-15 
16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/examples/video-player/CMakeLists.txt        2026-05-18 
10:42:52.000000000 +0200
@@ -4,7 +4,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 #
 
-cmake_minimum_required(VERSION 3.15)
+cmake_minimum_required(VERSION 3.16...3.31)
 
 project(videoplayer LANGUAGES CXX)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/examples/video-player/mpvitem.cpp 
new/mpvqt-1.2.0/examples/video-player/mpvitem.cpp
--- old/mpvqt-1.1.1/examples/video-player/mpvitem.cpp   2025-04-15 
16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/examples/video-player/mpvitem.cpp   2026-05-18 
10:42:52.000000000 +0200
@@ -119,7 +119,7 @@
         Q_EMIT currentUrlChanged();
     }
 
-    Q_EMIT command(QStringList() << QStringLiteral("loadfile") << 
m_currentUrl.toString(QUrl::PreferLocalFile));
+    command(QStringList() << QStringLiteral("loadfile") << 
m_currentUrl.toString(QUrl::PreferLocalFile));
 }
 
 QString MpvItem::mediaTitle()
@@ -137,7 +137,7 @@
     if (qFuzzyCompare(value, position())) {
         return;
     }
-    Q_EMIT setPropertyAsync(MpvProperties::self()->Position, value);
+    setPropertyAsync(MpvProperties::self()->Position, value);
 }
 
 double MpvItem::duration()
@@ -155,7 +155,7 @@
     if (value == pause()) {
         return;
     }
-    Q_EMIT setPropertyAsync(MpvProperties::self()->Pause, value);
+    setPropertyAsync(MpvProperties::self()->Pause, value);
 }
 
 int MpvItem::volume()
@@ -168,7 +168,7 @@
     if (value == volume()) {
         return;
     }
-    Q_EMIT setPropertyAsync(MpvProperties::self()->Volume, value);
+    setPropertyAsync(MpvProperties::self()->Volume, value);
 }
 
 QString MpvItem::formattedDuration() const
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/CMakeLists.txt 
new/mpvqt-1.2.0/src/CMakeLists.txt
--- old/mpvqt-1.1.1/src/CMakeLists.txt  2025-04-15 16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/src/CMakeLists.txt  2026-05-18 10:42:52.000000000 +0200
@@ -13,6 +13,15 @@
     EXPORT_NAME MpvQt
 )
 
+if(${ECM_VERSION} VERSION_GREATER_EQUAL "6.27")
+    qt_extract_metatypes(MpvQt OUTPUT_FILES METATYPES_FILE)
+    get_filename_component(METATYPES_FILE_NAME ${METATYPES_FILE} NAME)
+    target_sources(MpvQt
+        INTERFACE
+            
$<INSTALL_INTERFACE:${KDE_INSTALL_QTMETATYPESDIR}/${METATYPES_FILE_NAME}>
+    )
+endif()
+
 target_sources(MpvQt
     PRIVATE
         mpvabstractitem.h mpvabstractitem.cpp
@@ -22,7 +31,7 @@
 
 ecm_generate_export_header(MpvQt
     BASE_NAME MpvQt
-    VERSION ${CMAKE_PROJECT_VERSION}
+    VERSION ${PROJECT_VERSION}
     USE_VERSION_HEADER
 )
 
@@ -43,6 +52,9 @@
 )
 
 install(TARGETS MpvQt EXPORT MpvQtTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
+if(${ECM_VERSION} VERSION_GREATER_EQUAL "6.27")
+    install(FILES ${METATYPES_FILE} DESTINATION ${KDE_INSTALL_QTMETATYPESDIR})
+endif()
 
 install(FILES
     ${CMAKE_CURRENT_BINARY_DIR}/mpvqt_export.h
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/mpvabstractitem.cpp 
new/mpvqt-1.2.0/src/mpvabstractitem.cpp
--- old/mpvqt-1.1.1/src/mpvabstractitem.cpp     2025-04-15 16:03:38.000000000 
+0200
+++ new/mpvqt-1.2.0/src/mpvabstractitem.cpp     2026-05-18 10:42:52.000000000 
+0200
@@ -7,12 +7,15 @@
 #include "mpvabstractitem.h"
 #include "mpvabstractitem_p.h"
 
+#include <QLoggingCategory>
 #include <QQuickWindow>
 #include <QThread>
 
 #include "mpvcontroller.h"
 #include "mpvrenderer.h"
 
+Q_LOGGING_CATEGORY(MpvQt_MpvAbstractItem, "MpvQt.MpvAbstractItem")
+
 MpvAbstractItemPrivate::MpvAbstractItemPrivate(MpvAbstractItem *q)
     : q_ptr(q)
 {
@@ -23,42 +26,39 @@
     , d_ptr{std::make_unique<MpvAbstractItemPrivate>(this)}
 {
     if (QQuickWindow::graphicsApi() != QSGRendererInterface::OpenGL) {
-        qDebug() << "MpvAbstractItem: "
-                    "The graphics api must be set to opengl or mpv won't be 
able to render the video.\n"
-                    
"QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)\n"
-                    "The call to the function must happen before constructing "
-                    "the first QQuickWindow in the application.";
+        qCCritical(MpvQt_MpvAbstractItem) << "The graphics api must be set to 
opengl "
+                                             "or mpv won't be able to render 
the video.\n"
+                                             
"QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)\n"
+                                             "The call to the function must 
happen before constructing "
+                                             "the first QQuickWindow in the 
application.";
     }
 
-    d_ptr->m_workerThread = new QThread;
+    d_ptr->m_workerThread = new QThread(this);
     d_ptr->m_mpvController = new MpvController;
-    d_ptr->m_workerThread->start();
+
+    connect(d_ptr->m_workerThread, &QThread::finished, d_ptr->m_mpvController, 
&MpvController::deleteLater);
+
     d_ptr->m_mpvController->moveToThread(d_ptr->m_workerThread);
+    d_ptr->m_workerThread->start();
+
+    // must wait for init to finish or the mpv object could be accessed while 
not initialized
     QMetaObject::invokeMethod(d_ptr->m_mpvController, &MpvController::init, 
Qt::BlockingQueuedConnection);
-    d_ptr->m_mpv = d_ptr->m_mpvController->mpv();
 
-    connect(d_ptr->m_workerThread, &QThread::finished, d_ptr->m_mpvController, 
&MpvController::deleteLater);
-    connect(this, &MpvAbstractItem::observeProperty, d_ptr->m_mpvController, 
&MpvController::observeProperty, Qt::QueuedConnection);
-    connect(this, &MpvAbstractItem::setProperty, d_ptr->m_mpvController, 
&MpvController::setProperty, Qt::QueuedConnection);
-    connect(this, &MpvAbstractItem::command, d_ptr->m_mpvController, 
&MpvController::command, Qt::QueuedConnection);
+    auto mpvHandleManager = mpvController()->mpvHandleManager();
+    auto renderContext{nullptr};
+    d_ptr->m_mpvResourceManager = 
std::make_shared<MpvResourceManager>(renderContext, mpvHandleManager);
 }
 
 MpvAbstractItem::~MpvAbstractItem()
 {
-    if (d_ptr->m_mpv_gl) {
-        mpv_render_context_free(d_ptr->m_mpv_gl);
-    }
-    mpv_set_wakeup_callback(d_ptr->m_mpv, nullptr, nullptr);
-
     d_ptr->m_workerThread->quit();
     d_ptr->m_workerThread->wait();
     d_ptr->m_workerThread->deleteLater();
-    mpv_terminate_destroy(d_ptr->m_mpv);
 }
 
 QQuickFramebufferObject::Renderer *MpvAbstractItem::createRenderer() const
 {
-    return new MpvRenderer(const_cast<MpvAbstractItem *>(this));
+    return new MpvRenderer();
 }
 
 MpvController *MpvAbstractItem::mpvController()
@@ -66,15 +66,48 @@
     return d_ptr->m_mpvController;
 }
 
+// clang-format off
+
+void MpvAbstractItem::observeProperty(const QString &property, mpv_format 
format, uint64_t id)
+{
+    QMetaObject::invokeMethod(d_ptr->m_mpvController,
+                              &MpvController::observeProperty,
+                              Qt::QueuedConnection,
+                              property,
+                              format,
+                              id);
+}
+
+int MpvAbstractItem::unobserveProperty(uint64_t id)
+{
+    int result = 0;
+    QMetaObject::invokeMethod(d_ptr->m_mpvController,
+                              &MpvController::unobserveProperty,
+                              Qt::BlockingQueuedConnection,
+                              Q_RETURN_ARG(int, result),
+                              id);
+
+    return result;
+}
+
+void MpvAbstractItem::setProperty(const QString &property, const QVariant 
&value)
+{
+    QMetaObject::invokeMethod(d_ptr->m_mpvController,
+                              &MpvController::setProperty,
+                              Qt::QueuedConnection,
+                              property,
+                              value);
+}
+
 int MpvAbstractItem::setPropertyBlocking(const QString &property, const 
QVariant &value)
 {
-    int error;
+    int error = 0;
     QMetaObject::invokeMethod(d_ptr->m_mpvController,
-                              "setProperty",
+                              &MpvController::setProperty,
                               Qt::BlockingQueuedConnection,
                               Q_RETURN_ARG(int, error),
-                              Q_ARG(QString, property),
-                              Q_ARG(QVariant, value));
+                              property,
+                              value);
 
     return error;
 }
@@ -82,52 +115,78 @@
 void MpvAbstractItem::setPropertyAsync(const QString &property, const QVariant 
&value, int id)
 {
     QMetaObject::invokeMethod(d_ptr->m_mpvController,
-                              "setPropertyAsync",
+                              &MpvController::setPropertyAsync,
                               Qt::QueuedConnection,
-                              Q_ARG(QString, property),
-                              Q_ARG(QVariant, value),
-                              Q_ARG(int, id));
+                              property,
+                              value,
+                              id);
 }
 
 QVariant MpvAbstractItem::getProperty(const QString &property)
 {
     QVariant value;
-    QMetaObject::invokeMethod(d_ptr->m_mpvController, "getProperty", 
Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, value), Q_ARG(QString, 
property));
+    QMetaObject::invokeMethod(d_ptr->m_mpvController,
+                              &MpvController::getProperty,
+                              Qt::BlockingQueuedConnection,
+                              Q_RETURN_ARG(QVariant, value),
+                              property);
 
     return value;
 }
 
 void MpvAbstractItem::getPropertyAsync(const QString &property, int id)
 {
-    QMetaObject::invokeMethod(d_ptr->m_mpvController, "getPropertyAsync", 
Qt::QueuedConnection, Q_ARG(QString, property), Q_ARG(int, id));
+    QMetaObject::invokeMethod(d_ptr->m_mpvController,
+                              &MpvController::getPropertyAsync,
+                              Qt::QueuedConnection,
+                              property,
+                              id);
+}
+
+void MpvAbstractItem::command(const QStringList &params)
+{
+    QMetaObject::invokeMethod(d_ptr->m_mpvController,
+                              &MpvController::command,
+                              Qt::QueuedConnection,
+                              params);
 }
 
-QVariant MpvAbstractItem::commandBlocking(const QVariant &params)
+QVariant MpvAbstractItem::commandBlocking(const QStringList &params)
 {
     QVariant value;
-    QMetaObject::invokeMethod(d_ptr->m_mpvController, "command", 
Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, value), Q_ARG(QVariant, 
params));
+    QMetaObject::invokeMethod(d_ptr->m_mpvController,
+                              &MpvController::command,
+                              Qt::BlockingQueuedConnection,
+                              Q_RETURN_ARG(QVariant, value),
+                              params);
     return value;
 }
 
 void MpvAbstractItem::commandAsync(const QStringList &params, int id)
 {
-    QMetaObject::invokeMethod(d_ptr->m_mpvController, "commandAsync", 
Qt::QueuedConnection, Q_ARG(QVariant, params), Q_ARG(int, id));
+    QMetaObject::invokeMethod(d_ptr->m_mpvController,
+                              &MpvController::commandAsync,
+                              Qt::QueuedConnection,
+                              params,
+                              id);
 }
 
 QVariant MpvAbstractItem::expandText(const QString &text)
 {
     QVariant value;
     QMetaObject::invokeMethod(d_ptr->m_mpvController,
-                              "command",
+                              &MpvController::command,
                               Qt::BlockingQueuedConnection,
                               Q_RETURN_ARG(QVariant, value),
-                              Q_ARG(QVariant, 
QVariant::fromValue(QStringList{QStringLiteral("expand-text"), text})));
+                              QStringList() << QStringLiteral("expand-text") 
<< text);
     return value;
 }
 
-int MpvAbstractItem::unobserveProperty(uint64_t id)
+// clang-format on
+
+void MpvAbstractItem::requestUpdateFromRenderer()
 {
-    return mpvController()->unobserveProperty(id);
+    update();
 }
 
 #include "moc_mpvabstractitem.cpp"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/mpvabstractitem.h 
new/mpvqt-1.2.0/src/mpvabstractitem.h
--- old/mpvqt-1.1.1/src/mpvabstractitem.h       2025-04-15 16:03:38.000000000 
+0200
+++ new/mpvqt-1.2.0/src/mpvabstractitem.h       2026-05-18 10:42:52.000000000 
+0200
@@ -7,15 +7,55 @@
 #ifndef MPVABSTRACTITEM_H
 #define MPVABSTRACTITEM_H
 
+#include "mpvcontroller.h"
 #include "mpvqt_export.h"
 
 #include <QtQuick/QQuickFramebufferObject>
+
 #include <mpv/client.h>
 #include <mpv/render_gl.h>
 
 class MpvController;
 class MpvAbstractItemPrivate;
 
+/**
+ * MpvResourceManager is a lifecycle management utility designed
+ * to handle the safe initialization and destruction of mpv_render_context.
+ * Its primary goal is to ensure that GPU-bound resources are released
+ * on the correct thread and that the underlying mpv_handle remains valid
+ * until all rendering resources are fully cleaned up
+ */
+struct MpvResourceManager {
+    // raw pointer to the mpv OpenGL render context
+    mpv_render_context *mpvRenderContext{nullptr};
+    // Shared pointer to the manager owning the mpv_handle
+    // Ensures the core mpv instance outlives the rendering context
+    std::shared_ptr<MpvHandleManager> mpvHandleManager;
+
+    MpvResourceManager(mpv_render_context *c, 
std::shared_ptr<MpvHandleManager> owner)
+        : mpvRenderContext(c)
+        , mpvHandleManager(owner)
+    {
+    }
+
+    /**
+     * cleans up mpv's rendering context
+     *
+     * MUST be called from the Qt Render Thread (MpvRenderer)
+     *
+     * An OpenGL context must be current in the calling thread.
+     * This must be the same context used to create the mpvRenderContext.
+     */
+    void freeContext()
+    {
+        if (mpvRenderContext) {
+            mpv_render_context_set_update_callback(mpvRenderContext, nullptr, 
nullptr);
+            mpv_render_context_free(mpvRenderContext);
+            mpvRenderContext = nullptr;
+        }
+    }
+};
+
 class MPVQT_EXPORT MpvAbstractItem : public QQuickFramebufferObject
 {
     Q_OBJECT
@@ -24,22 +64,28 @@
     ~MpvAbstractItem();
 
     Renderer *createRenderer() const override;
-    Q_INVOKABLE int setPropertyBlocking(const QString &property, const 
QVariant &value);
+
+    Q_INVOKABLE void observeProperty(const QString &property, mpv_format 
format, uint64_t id = 0);
+    Q_INVOKABLE int unobserveProperty(uint64_t id);
+
+    Q_INVOKABLE void setProperty(const QString &property, const QVariant 
&value);
     Q_INVOKABLE void setPropertyAsync(const QString &property, const QVariant 
&value, int id = 0);
+    Q_INVOKABLE int setPropertyBlocking(const QString &property, const 
QVariant &value);
+
     Q_INVOKABLE QVariant getProperty(const QString &property);
     Q_INVOKABLE void getPropertyAsync(const QString &property, int id = 0);
-    Q_INVOKABLE QVariant commandBlocking(const QVariant &params);
+
+    Q_INVOKABLE void command(const QStringList &params);
+    Q_INVOKABLE QVariant commandBlocking(const QStringList &params);
     Q_INVOKABLE void commandAsync(const QStringList &params, int id = 0);
+
     Q_INVOKABLE QVariant expandText(const QString &text);
-    Q_INVOKABLE int unobserveProperty(uint64_t id);
+    Q_INVOKABLE void requestUpdateFromRenderer();
 
     friend class MpvRenderer;
 
 Q_SIGNALS:
     void ready();
-    void observeProperty(const QString &property, mpv_format format, uint64_t 
id = 0);
-    void setProperty(const QString &property, const QVariant &value);
-    void command(const QStringList &params);
 
 protected:
     MpvController *mpvController();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/mpvabstractitem_p.h 
new/mpvqt-1.2.0/src/mpvabstractitem_p.h
--- old/mpvqt-1.1.1/src/mpvabstractitem_p.h     2025-04-15 16:03:38.000000000 
+0200
+++ new/mpvqt-1.2.0/src/mpvabstractitem_p.h     2026-05-18 10:42:52.000000000 
+0200
@@ -17,8 +17,8 @@
     MpvAbstractItem *q_ptr;
     QThread *m_workerThread{nullptr};
     MpvController *m_mpvController{nullptr};
-    mpv_handle *m_mpv{nullptr};
-    mpv_render_context *m_mpv_gl{nullptr};
+    bool m_isRendererReady{false};
+    std::shared_ptr<MpvResourceManager> m_mpvResourceManager;
 };
 
 #endif // MPVABSTRACTITEM_P_H_INCLUDED
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/mpvcontroller.cpp 
new/mpvqt-1.2.0/src/mpvcontroller.cpp
--- old/mpvqt-1.1.1/src/mpvcontroller.cpp       2025-04-15 16:03:38.000000000 
+0200
+++ new/mpvqt-1.2.0/src/mpvcontroller.cpp       2026-05-18 10:42:52.000000000 
+0200
@@ -7,11 +7,14 @@
 #include "mpvcontroller.h"
 #include "mpvcontroller_p.h"
 
+#include <QLoggingCategory>
 #include <QStandardPaths>
 #include <QVariant>
 
 #include <clocale>
 
+Q_LOGGING_CATEGORY(MpvQt_MpvController, "MpvQt.MpvController")
+
 MpvControllerPrivate::MpvControllerPrivate(MpvController *q)
     : q_ptr(q)
 {
@@ -168,6 +171,13 @@
 {
 }
 
+MpvController::~MpvController()
+{
+    if (d_ptr && d_ptr->m_mpv) {
+        mpv_set_wakeup_callback(d_ptr->m_mpv, nullptr, nullptr);
+    }
+}
+
 void MpvController::init()
 {
     d_ptr = std::make_unique<MpvControllerPrivate>(this);
@@ -190,6 +200,7 @@
     configPath.append(QStringLiteral("/mpvqt.conf"));
     setProperty(QStringLiteral("include"), configPath);
     setProperty(QStringLiteral("vo"), QStringLiteral("libmpv"));
+    d_ptr->m_mpvHandleManager = 
std::make_shared<MpvHandleManager>(d_ptr->m_mpv);
 }
 
 void MpvController::mpvEvents(void *ctx)
@@ -219,6 +230,8 @@
             auto prop = static_cast<mpv_event_end_file *>(event->data);
             if (prop->reason == MPV_END_FILE_REASON_EOF) {
                 Q_EMIT endFile(QStringLiteral("eof"));
+            } else if (prop->reason == MPV_END_FILE_REASON_STOP) {
+                Q_EMIT endFile(QStringLiteral("stop"));
             } else if (prop->reason == MPV_END_FILE_REASON_ERROR) {
                 Q_EMIT endFile(QStringLiteral("error"));
             }
@@ -257,7 +270,7 @@
                 data = *reinterpret_cast<double *>(prop->data);
                 break;
             case MPV_FORMAT_STRING:
-                data = QString::fromStdString(*reinterpret_cast<char 
**>(prop->data));
+                data = QString::fromUtf8(*reinterpret_cast<char 
**>(prop->data));
                 break;
             case MPV_FORMAT_INT64:
                 data = qlonglong(*reinterpret_cast<int64_t *>(prop->data));
@@ -275,7 +288,7 @@
             case MPV_FORMAT_BYTE_ARRAY:
                 break;
             }
-            Q_EMIT propertyChanged(QString::fromStdString(prop->name), data);
+            Q_EMIT propertyChanged(QString::fromUtf8(prop->name), data);
             break;
         }
         case MPV_EVENT_NONE:
@@ -343,14 +356,14 @@
     return err;
 }
 
-QVariant MpvController::command(const QVariant &params)
+QVariant MpvController::command(const QStringList &params)
 {
     mpv_node node;
     d_ptr->setNode(&node, params);
     mpv_node result;
     int err = mpv_command_node(d_ptr->m_mpv, &node, &result);
     if (err < 0) {
-        qDebug() << getError(err) << params;
+        qCDebug(MpvQt_MpvController) << getError(err) << params;
         return QVariant::fromValue(ErrorReturn(err));
     }
     node_autofree f(&result);
@@ -364,6 +377,11 @@
     return mpv_command_node_async(d_ptr->m_mpv, id, &node);
 }
 
+std::shared_ptr<MpvHandleManager> MpvController::mpvHandleManager() const
+{
+    return d_ptr->m_mpvHandleManager;
+}
+
 QString MpvController::getError(int error)
 {
     ErrorReturn err{error};
@@ -373,7 +391,7 @@
     case MPV_ERROR_EVENT_QUEUE_FULL:
         return QStringLiteral("MPV_ERROR_EVENT_QUEUE_FULL");
     case MPV_ERROR_NOMEM:
-        return QStringLiteral("MPV_ERROR_EVENT_QUEUE_FULL");
+        return QStringLiteral("MPV_ERROR_NOMEM");
     case MPV_ERROR_UNINITIALIZED:
         return QStringLiteral("MPV_ERROR_UNINITIALIZED");
     case MPV_ERROR_INVALID_PARAMETER:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/mpvcontroller.h 
new/mpvqt-1.2.0/src/mpvcontroller.h
--- old/mpvqt-1.1.1/src/mpvcontroller.h 2025-04-15 16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/src/mpvcontroller.h 2026-05-18 10:42:52.000000000 +0200
@@ -18,6 +18,20 @@
 
 #include <memory>
 
+struct MpvHandleManager {
+    mpv_handle *mpvHandle{nullptr};
+    explicit MpvHandleManager(mpv_handle *h)
+        : mpvHandle(h)
+    {
+    }
+    ~MpvHandleManager()
+    {
+        if (mpvHandle) {
+            mpv_terminate_destroy(mpvHandle);
+        }
+    }
+};
+
 class MpvControllerPrivate;
 
 /**
@@ -62,13 +76,16 @@
 class MPVQT_EXPORT MpvController : public QObject
 {
     Q_OBJECT
+    friend class MpvAbstractItem;
+
 public:
     explicit MpvController(QObject *parent = nullptr);
+    ~MpvController();
 
     /**
      * Return an error string from an ErrorReturn.
      */
-    QString getError(int error);
+    static QString getError(int error);
 
     static void mpvEvents(void *ctx);
     void eventHandler();
@@ -145,7 +162,7 @@
      * @param `params` command arguments, with args[0] being the command name 
as string
      * @return the property value, or an ErrorReturn with the error code
      */
-    QVariant command(const QVariant &params);
+    QVariant command(const QStringList &params);
 
     /**
      * Run commands asynchronously. The result of the operation as well
@@ -168,6 +185,7 @@
     void videoReconfig();
 
 private:
+    std::shared_ptr<MpvHandleManager> mpvHandleManager() const;
     std::unique_ptr<MpvControllerPrivate> d_ptr;
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/mpvcontroller_p.h 
new/mpvqt-1.2.0/src/mpvcontroller_p.h
--- old/mpvqt-1.1.1/src/mpvcontroller_p.h       2025-04-15 16:03:38.000000000 
+0200
+++ new/mpvqt-1.2.0/src/mpvcontroller_p.h       2026-05-18 10:42:52.000000000 
+0200
@@ -22,6 +22,7 @@
 
     MpvController *q_ptr;
     mpv_handle *m_mpv{nullptr};
+    std::shared_ptr<MpvHandleManager> m_mpvHandleManager;
 };
 
 #endif // MPVCONTROLLER_P_H_INCLUDED
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/mpvrenderer.cpp 
new/mpvqt-1.2.0/src/mpvrenderer.cpp
--- old/mpvqt-1.1.1/src/mpvrenderer.cpp 2025-04-15 16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/src/mpvrenderer.cpp 2026-05-18 10:42:52.000000000 +0200
@@ -5,15 +5,19 @@
  */
 
 #include "mpvrenderer.h"
-#include "mpvabstractitem.h"
-#include "mpvabstractitem_p.h"
-#include "mpvcontroller.h"
 
 #include <QGuiApplication>
+#include <QLoggingCategory>
 #include <QOpenGLContext>
 #include <QOpenGLFramebufferObject>
 #include <QQuickWindow>
 
+#include "mpvabstractitem.h"
+#include "mpvabstractitem_p.h"
+#include "mpvcontroller.h"
+
+Q_STATIC_LOGGING_CATEGORY(MpvQt_MpvRenderer, "MpvQt.MpvRenderer")
+
 static void *get_proc_address_mpv(void *ctx, const char *name)
 {
     Q_UNUSED(ctx)
@@ -28,13 +32,35 @@
 
 void on_mpv_redraw(void *ctx)
 {
-    QMetaObject::invokeMethod(static_cast<MpvAbstractItem *>(ctx), 
&MpvAbstractItem::update, Qt::QueuedConnection);
+    auto *r = static_cast<MpvRenderer *>(ctx);
+    r->requestUpdate();
 }
 
-MpvRenderer::MpvRenderer(MpvAbstractItem *new_obj)
-    : m_mpvAItem{new_obj}
+MpvRenderer::MpvRenderer()
 {
-    m_mpvAItem->window()->setPersistentSceneGraph(true);
+}
+
+MpvRenderer::~MpvRenderer()
+{
+    if (m_mpvResourceManager) {
+        m_mpvResourceManager->freeContext();
+    }
+}
+
+void MpvRenderer::synchronize(QQuickFramebufferObject *item)
+{
+    MpvAbstractItem *mpvAItem = static_cast<MpvAbstractItem *>(item);
+    m_mpvAItem = mpvAItem;
+
+    if (!m_mpvResourceManager) {
+        m_mpvResourceManager = mpvAItem->d_ptr->m_mpvResourceManager;
+    }
+
+    if (mpvAItem->d_ptr->m_isRendererReady != m_isFramebufferReady) {
+        mpvAItem->d_ptr->m_isRendererReady = m_isFramebufferReady;
+
+        Q_EMIT mpvAItem->ready();
+    }
 }
 
 void MpvRenderer::render()
@@ -57,44 +83,60 @@
                                  {MPV_RENDER_PARAM_INVALID, nullptr}};
     // See render_gl.h on what OpenGL environment mpv expects, and
     // other API details.
-    mpv_render_context_render(m_mpvAItem->d_ptr->m_mpv_gl, params);
+    int result = 
mpv_render_context_render(m_mpvResourceManager->mpvRenderContext, params);
+    if (result < 0) {
+        qCWarning(MpvQt_MpvRenderer) << "mpv_render_context_render failed:" << 
MpvController::getError(result);
+        return;
+    }
 }
 
 QOpenGLFramebufferObject *MpvRenderer::createFramebufferObject(const QSize 
&size)
 {
-    // init mpv_gl:
-    if (!m_mpvAItem->d_ptr->m_mpv_gl) {
-#if MPV_CLIENT_API_VERSION < MPV_MAKE_VERSION(2, 0)
-        mpv_opengl_init_params gl_init_params{get_proc_address_mpv, nullptr, 
nullptr};
-#else
-        mpv_opengl_init_params gl_init_params{get_proc_address_mpv, nullptr};
-#endif
+    if (m_mpvResourceManager && !m_mpvResourceManager->mpvRenderContext) {
+        m_mpvResourceManager->mpvRenderContext = createMpvRenderContext();
+        m_isFramebufferReady = true;
+    }
+
+    return QQuickFramebufferObject::Renderer::createFramebufferObject(size);
+}
+
+mpv_render_context *MpvRenderer::createMpvRenderContext()
+{
+    mpv_opengl_init_params gl_init_params{get_proc_address_mpv, nullptr};
 
-        mpv_render_param display{MPV_RENDER_PARAM_INVALID, nullptr};
+    mpv_render_param display{MPV_RENDER_PARAM_INVALID, nullptr};
 #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && 
!defined(Q_OS_HAIKU)
-        if (QGuiApplication::platformName() == QStringLiteral("xcb")) {
-            display.type = MPV_RENDER_PARAM_X11_DISPLAY;
-            display.data = 
qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display();
-        }
-
-        if (QGuiApplication::platformName() == QStringLiteral("wayland")) {
-            display.type = MPV_RENDER_PARAM_WL_DISPLAY;
-            display.data = 
qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>()->display();
-        }
+    if (QGuiApplication::platformName() == QStringLiteral("xcb")) {
+        display.type = MPV_RENDER_PARAM_X11_DISPLAY;
+        display.data = 
qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display();
+    }
+
+    if (QGuiApplication::platformName() == QStringLiteral("wayland")) {
+        display.type = MPV_RENDER_PARAM_WL_DISPLAY;
+        display.data = 
qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>()->display();
+    }
 #endif
-        mpv_render_param params[]{{MPV_RENDER_PARAM_API_TYPE, const_cast<char 
*>(MPV_RENDER_API_TYPE_OPENGL)},
-                                  {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, 
&gl_init_params},
-                                  display,
-                                  {MPV_RENDER_PARAM_INVALID, nullptr}};
-
-        int result = mpv_render_context_create(&m_mpvAItem->d_ptr->m_mpv_gl, 
m_mpvAItem->d_ptr->m_mpv, params);
-        if (result < 0) {
-            qFatal("failed to initialize mpv GL context");
-        }
 
-        mpv_render_context_set_update_callback(m_mpvAItem->d_ptr->m_mpv_gl, 
on_mpv_redraw, m_mpvAItem);
-        Q_EMIT m_mpvAItem->ready();
+    mpv_render_param params[]{{MPV_RENDER_PARAM_API_TYPE, const_cast<char 
*>(MPV_RENDER_API_TYPE_OPENGL)},
+                              {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, 
&gl_init_params},
+                              display,
+                              {MPV_RENDER_PARAM_INVALID, nullptr}};
+
+    mpv_render_context *renderCtx = nullptr;
+    mpv_handle *handle = m_mpvResourceManager->mpvHandleManager->mpvHandle;
+    int result = mpv_render_context_create(&renderCtx, handle, params);
+    if (result < 0) {
+        qCritical() << "failed to initialize mpv GL context:" << 
mpv_error_string(result);
+        return nullptr;
     }
 
-    return QQuickFramebufferObject::Renderer::createFramebufferObject(size);
+    mpv_render_context_set_update_callback(renderCtx, on_mpv_redraw, this);
+    return renderCtx;
+}
+
+void MpvRenderer::requestUpdate()
+{
+    if (m_mpvAItem) {
+        QMetaObject::invokeMethod(m_mpvAItem.data(), 
"requestUpdateFromRenderer", Qt::QueuedConnection);
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mpvqt-1.1.1/src/mpvrenderer.h 
new/mpvqt-1.2.0/src/mpvrenderer.h
--- old/mpvqt-1.1.1/src/mpvrenderer.h   2025-04-15 16:03:38.000000000 +0200
+++ new/mpvqt-1.2.0/src/mpvrenderer.h   2026-05-18 10:42:52.000000000 +0200
@@ -9,21 +9,31 @@
 
 #include <QtQuick/QQuickFramebufferObject>
 
+#include <mpv/render_gl.h>
+
+#include "mpvabstractitem.h"
+
 class MpvAbstractItem;
 
 class MpvRenderer : public QQuickFramebufferObject::Renderer
 {
 public:
-    explicit MpvRenderer(MpvAbstractItem *new_obj);
-    ~MpvRenderer() = default;
-
-    MpvAbstractItem *m_mpvAItem{nullptr};
+    explicit MpvRenderer();
+    ~MpvRenderer();
 
     // This function is called when a new FBO is needed.
     // This happens on the initial frame.
     QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) 
override;
 
     void render() override;
+    void synchronize(QQuickFramebufferObject *item) override;
+    void requestUpdate();
+
+private:
+    mpv_render_context *createMpvRenderContext();
+    QPointer<MpvAbstractItem> m_mpvAItem{nullptr};
+    bool m_isFramebufferReady{false};
+    std::shared_ptr<MpvResourceManager> m_mpvResourceManager;
 };
 
 #endif // MPVRENDERER_H

Reply via email to