[vlc-commits] [Git][videolan/vlc][master] 4 commits: qt: introduce ModelRecoveryAgent

François Cartegnie (@fcartegnie) gitlab at videolan.org
Mon Oct 21 04:33:30 UTC 2024



François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
cccb2c8c by Fatih Uzunoglu at 2024-10-21T04:14:17+00:00
qt: introduce ModelRecoveryAgent

This class can be used to restore a model if
it supports serialization to a former state,
recent state.

The intention is to use this for the playlist
list model to recover the playlist if the
application crashes, or unexpectedly closes.

A message box is shown when there is a
recoverable model found. The user has the
right to choose whether to recover or not.

- - - - -
f45693f2 by Fatih Uzunoglu at 2024-10-21T04:14:17+00:00
qt: add append method to PlaylistController

- - - - -
3ca309e8 by Fatih Uzunoglu at 2024-10-21T04:14:17+00:00
qt: add serialize method to PlaylistController

- - - - -
e009cc4d by Fatih Uzunoglu at 2024-10-21T04:14:17+00:00
qt: use ModelRecoveryAgent for main playlist list model

- - - - -


7 changed files:

- modules/gui/qt/Makefile.am
- modules/gui/qt/meson.build
- modules/gui/qt/playlist/playlist_controller.cpp
- modules/gui/qt/playlist/playlist_controller.hpp
- modules/gui/qt/qt.cpp
- + modules/gui/qt/util/model_recovery_agent.hpp
- po/POTFILES.in


Changes:

=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -327,6 +327,7 @@ libqt_plugin_la_SOURCES = \
 	util/dismiss_popup_event_filter.hpp \
 	util/list_selection_model.cpp \
 	util/list_selection_model.hpp \
+	util/model_recovery_agent.hpp \
 	widgets/native/animators.cpp \
 	widgets/native/animators.hpp \
 	widgets/native/customwidgets.cpp widgets/native/customwidgets.hpp \


=====================================
modules/gui/qt/meson.build
=====================================
@@ -476,6 +476,7 @@ some_sources = files(
     'util/dismiss_popup_event_filter.hpp',
     'util/list_selection_model.cpp',
     'util/list_selection_model.hpp',
+    'util/model_recovery_agent.hpp',
     'widgets/native/animators.cpp',
     'widgets/native/animators.hpp',
     'widgets/native/customwidgets.cpp',


=====================================
modules/gui/qt/playlist/playlist_controller.cpp
=====================================
@@ -406,6 +406,11 @@ void PlaylistController::append(const QVariantList& sourceList, bool startPlayin
     append(toMediaList(sourceList), startPlaying);
 }
 
+void PlaylistController::append(const QVariant &source, bool startPlaying)
+{
+    append(QVariantList{source}, startPlaying);
+}
+
 void PlaylistController::insert(unsigned index, const QVariantList& sourceList, bool startPlaying)
 {
     insert(index, toMediaList(sourceList), startPlaying);
@@ -571,6 +576,13 @@ void PlaylistController::explore(const PlaylistItem& pItem)
     }
 }
 
+void PlaylistController::serialize(const QString &fileName)
+{
+    Q_D(PlaylistController);
+    vlc_playlist_locker lock{d->m_playlist};
+    vlc_playlist_Export(d->m_playlist, fileName.toUtf8(), "export-m3u8");
+}
+
 int PlaylistController::currentIndex() const
 {
     Q_D(const PlaylistController);


=====================================
modules/gui/qt/playlist/playlist_controller.hpp
=====================================
@@ -125,6 +125,7 @@ public:
     Q_INVOKABLE void goTo(uint index, bool startPlaying = false);
 
     Q_INVOKABLE void append(const QVariantList&, bool startPlaying = false);
+    Q_INVOKABLE void append(const QVariant&, bool startPlaying = false);
     Q_INVOKABLE void insert(unsigned index, const QVariantList&, bool startPlaying = false);
 
     void append(const QVector<Media> &, bool startPlaying = false);
@@ -141,6 +142,8 @@ public:
 
     Q_INVOKABLE void explore(const PlaylistItem& pItem);
 
+    void serialize(const QString& fileName);
+
 public:
     PlaylistController(vlc_playlist_t *playlist, QObject *parent = nullptr);
     virtual ~PlaylistController();


=====================================
modules/gui/qt/qt.cpp
=====================================
@@ -83,6 +83,7 @@ extern "C" char **environ;
 #include "maininterface/compositor.hpp"
 #include "util/vlctick.hpp"
 #include "util/shared_input_item.hpp"
+#include "util/model_recovery_agent.hpp"
 #include "network/networkmediamodel.hpp"
 #include "playlist/playlist_common.hpp"
 #include "playlist/playlist_item.hpp"
@@ -1017,6 +1018,15 @@ static void *Thread( void *obj )
     p_intf->p_mainPlayerController = new PlayerController(p_intf);
     p_intf->p_mainPlaylistController = new vlc::playlist::PlaylistController(p_intf->p_playlist);
 
+    std::unique_ptr<ModelRecoveryAgent> playlistModelRecoveryAgent;
+    QMetaObject::invokeMethod(&app, [&playlistModelRecoveryAgent, p_intf]() {
+        try {
+            playlistModelRecoveryAgent = std::make_unique<ModelRecoveryAgent>(p_intf->mainSettings,
+                                                                              QStringLiteral("Playlist"),
+                                                                              p_intf->p_mainPlaylistController);
+        } catch (...){ }
+    }, Qt::QueuedConnection);
+
     /* Create the normal interface in non-DP mode */
 #ifdef _WIN32
     p_intf->p_mi = new MainCtxWin32(p_intf);
@@ -1096,6 +1106,8 @@ static void *Thread( void *obj )
     app.exec();
 
     msg_Dbg( p_intf, "QApp exec() finished" );
+
+    playlistModelRecoveryAgent.reset();
     return ThreadCleanup( p_intf, CLEANUP_APP_TERMINATED );
 }
 


=====================================
modules/gui/qt/util/model_recovery_agent.hpp
=====================================
@@ -0,0 +1,149 @@
+/*****************************************************************************
+ * Copyright (C) 2024 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef MODEL_RECOVERY_AGENT_HPP
+#define MODEL_RECOVERY_AGENT_HPP
+
+#include <QFile>
+#include <QTimer>
+#include <QSettings>
+#include <QPointer>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QTemporaryFile>
+#include <QAbstractItemModel>
+
+#include <cstdio>
+
+#include "qt.hpp"
+
+class ModelRecoveryAgent
+{
+    const QPointer<QSettings> m_settings;
+    const QString m_key;
+    QString m_recoveryFileName;
+    QTimer m_timer;
+    bool m_conditionDismissInitialDirtiness = false;
+
+public:
+    // NOTE: settings and model must outlive the instance of this class.
+    template<class T>
+    ModelRecoveryAgent(class QSettings *settings, const QString& modelIdentifier, T* model)
+        : m_settings(settings), m_key(modelIdentifier + QStringLiteral("/RecoveryFilePath"))
+    {
+        assert(settings);
+        assert(model);
+        settings->sync();
+        if (settings->contains(m_key))
+        {
+            assert(settings->value(m_key).typeId() == QMetaType::QString);
+            QString recoveryFileName = settings->value(m_key).toString();
+            if (!recoveryFileName.isEmpty())
+            {
+                m_recoveryFileName = std::move(recoveryFileName);
+                const QFileInfo fileInfo(m_recoveryFileName);
+                if (fileInfo.size() > 0)
+                {
+                    QMessageBox msgBox;
+                    msgBox.setText(qtr("The application closed abruptly."));
+                    msgBox.setInformativeText(qtr("Do you want to restore the %1 model from %2?").arg(modelIdentifier.toLower(),
+                                                                                                      fileInfo.lastModified().toString()));
+                    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+                    msgBox.setDefaultButton(QMessageBox::Yes);
+                    if (msgBox.exec() == QMessageBox::Yes)
+                    {
+                        model->append(m_recoveryFileName);
+                        m_conditionDismissInitialDirtiness = true;
+                    }
+                }
+            }
+        }
+
+        if (m_recoveryFileName.isEmpty())
+        {
+            QTemporaryFile temporaryFile;
+            temporaryFile.setAutoRemove(false);
+            if (!temporaryFile.open())
+                throw std::exception();
+            m_recoveryFileName = temporaryFile.fileName();
+            settings->setValue(m_key, m_recoveryFileName);
+            settings->sync();
+        }
+
+        m_timer.setInterval(10000); // 10 seconds
+        m_timer.setSingleShot(true);
+
+        QObject::connect(&m_timer, &QTimer::timeout, model, [this, model]() {
+            if (m_conditionDismissInitialDirtiness)
+            {
+                m_conditionDismissInitialDirtiness = false;
+                return;
+            }
+
+            assert(!m_recoveryFileName.isEmpty());
+
+            const char* tmpFileName = (m_recoveryFileName + QStringLiteral(".part")).toLatin1();
+            const char* recoveryFileName = m_recoveryFileName.toLatin1();
+
+            model->serialize(tmpFileName);
+
+            remove(recoveryFileName);
+            if (!rename(tmpFileName, recoveryFileName))
+            {
+                assert(m_settings);
+                m_settings->sync();
+                if (!m_settings->contains(m_key))
+                {
+                    m_settings->setValue(m_key, m_recoveryFileName);
+                    m_settings->sync();
+                }
+                else if (m_settings->value(m_key) != m_recoveryFileName)
+                {
+                    QObject::disconnect(model, nullptr, &m_timer, nullptr);
+                    m_timer.stop();
+                }
+            }
+        });
+
+        QObject::connect(model, &T::itemsReset, &m_timer, QOverload<>::of(&QTimer::start));
+        QObject::connect(model, &T::itemsAdded, &m_timer, QOverload<>::of(&QTimer::start));
+        QObject::connect(model, &T::itemsMoved, &m_timer, QOverload<>::of(&QTimer::start));
+        QObject::connect(model, &T::itemsRemoved, &m_timer, QOverload<>::of(&QTimer::start));
+        QObject::connect(model, &T::itemsUpdated, &m_timer, QOverload<>::of(&QTimer::start));
+    }
+
+    ~ModelRecoveryAgent()
+    {
+        if (!m_recoveryFileName.isEmpty())
+        {
+            QFile::remove(m_recoveryFileName);
+        }
+
+        assert(m_settings);
+
+        m_settings->sync();
+        assert(m_settings->contains(m_key));
+        if (m_settings->value(m_key) == m_recoveryFileName)
+        {
+            m_settings->remove(m_key);
+            m_settings->sync();
+        }
+    }
+};
+
+#endif // MODEL_RECOVERY_AGENT_HPP


=====================================
po/POTFILES.in
=====================================
@@ -900,6 +900,7 @@ modules/gui/qt/util/singleton.hpp
 modules/gui/qt/util/validators.cpp
 modules/gui/qt/util/validators.hpp
 modules/gui/qt/util/vlctick.cpp
+modules/gui/qt/util/model_recovery_agent.hpp
 modules/gui/qt/widgets/native/animators.cpp
 modules/gui/qt/widgets/native/animators.hpp
 modules/gui/qt/widgets/native/customwidgets.cpp



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fdf37bda22388ecaa4b2eef4653a51667f984a2d...e009cc4da6689f648d5fe32d500b0a6f16adeeb8

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fdf37bda22388ecaa4b2eef4653a51667f984a2d...e009cc4da6689f648d5fe32d500b0a6f16adeeb8
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list