[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