[vlc-commits] qt: add ServicesDiscoveryModel
Prince Gupta
git at videolan.org
Mon Sep 28 14:01:25 CEST 2020
vlc | branch: master | Prince Gupta <guptaprince8832 at gmail.com> | Fri Sep 11 17:14:03 2020 +0530| [de193b72860146cf0a8ed8e2a55897ceb9adfe81] | committer: Pierre Lamot
qt: add ServicesDiscoveryModel
Signed-off-by: Pierre Lamot <pierre at videolabs.io>
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=de193b72860146cf0a8ed8e2a55897ceb9adfe81
---
modules/gui/qt/Makefile.am | 3 +
modules/gui/qt/maininterface/mainui.cpp | 2 +
modules/gui/qt/network/servicesdiscoverymodel.cpp | 264 ++++++++++++++++++++++
modules/gui/qt/network/servicesdiscoverymodel.hpp | 124 ++++++++++
4 files changed, 393 insertions(+)
diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 12d41fc14f..92e8269f0b 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -174,6 +174,8 @@ libqt_plugin_la_SOURCES = \
gui/qt/network/networkmediamodel.hpp \
gui/qt/network/networksourcelistener.cpp \
gui/qt/network/networksourcelistener.hpp \
+ gui/qt/network/servicesdiscoverymodel.cpp \
+ gui/qt/network/servicesdiscoverymodel.hpp \
gui/qt/player/input_models.cpp gui/qt/player/input_models.hpp \
gui/qt/player/player_controller.cpp gui/qt/player/player_controller.hpp gui/qt/player/player_controller_p.hpp \
gui/qt/player/playercontrolbarmodel.cpp gui/qt/player/playercontrolbarmodel.hpp \
@@ -321,6 +323,7 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/network/networkdevicemodel.moc.cpp \
gui/qt/network/networksourcesmodel.moc.cpp \
gui/qt/network/networkmediamodel.moc.cpp \
+ gui/qt/network/servicesdiscoverymodel.moc.cpp \
gui/qt/player/input_models.moc.cpp \
gui/qt/player/player_controller.moc.cpp \
gui/qt/player/playercontrolbarmodel.moc.cpp \
diff --git a/modules/gui/qt/maininterface/mainui.cpp b/modules/gui/qt/maininterface/mainui.cpp
index 2ce8b8d2af..80716c910f 100644
--- a/modules/gui/qt/maininterface/mainui.cpp
+++ b/modules/gui/qt/maininterface/mainui.cpp
@@ -32,6 +32,7 @@
#include "network/networkmediamodel.hpp"
#include "network/networkdevicemodel.hpp"
#include "network/networksourcesmodel.hpp"
+#include "network/servicesdiscoverymodel.hpp"
#include "maininterface/main_interface.hpp"
@@ -167,6 +168,7 @@ void MainUI::registerQMLTypes()
qmlRegisterType<NetworkMediaModel>( "org.videolan.medialib", 0, 1, "NetworkMediaModel");
qmlRegisterType<NetworkDeviceModel>( "org.videolan.medialib", 0, 1, "NetworkDeviceModel");
qmlRegisterType<NetworkSourcesModel>( "org.videolan.medialib", 0, 1, "NetworkSourcesModel");
+ qmlRegisterType<ServicesDiscoveryModel>( "org.videolan.medialib", 0, 1, "ServicesDiscoveryModel");
qmlRegisterType<MlFoldersModel>( "org.videolan.medialib", 0, 1, "MLFolderModel");
//expose base object, they aren't instanciable from QML side
diff --git a/modules/gui/qt/network/servicesdiscoverymodel.cpp b/modules/gui/qt/network/servicesdiscoverymodel.cpp
new file mode 100644
index 0000000000..46e078156f
--- /dev/null
+++ b/modules/gui/qt/network/servicesdiscoverymodel.cpp
@@ -0,0 +1,264 @@
+/*****************************************************************************
+ * Copyright (C) 2020 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.
+ *****************************************************************************/
+
+#include "servicesdiscoverymodel.hpp"
+#include <vlc_addons.h>
+
+#include "medialibrary/mlhelper.hpp"
+
+#include "playlist/media.hpp"
+#include "playlist/playlist_controller.hpp"
+
+#include <QPixmap>
+
+ServicesDiscoveryModel::ServicesDiscoveryModel( QObject* parent )
+ : QAbstractListModel( parent )
+{
+}
+
+ServicesDiscoveryModel::~ServicesDiscoveryModel()
+{
+ if ( m_manager )
+ {
+ addons_manager_Delete( m_manager );
+ }
+}
+
+QVariant ServicesDiscoveryModel::data( const QModelIndex& index, int role ) const
+{
+ if (!m_ctx)
+ return {};
+ auto idx = index.row();
+ if ( idx < 0 || (size_t)idx >= m_items.size() )
+ return {};
+ const auto& item = m_items[idx];
+ switch ( role )
+ {
+ case Role::SERVICE_NAME:
+ return item.name;
+ case Role::SERVICE_AUTHOR:
+ return item.author;
+ case Role::SERVICE_SUMMARY:
+ return item.summery;
+ case Role::SERVICE_DESCRIPTION:
+ return item.description;
+ case Role::SERVICE_DOWNLOADS:
+ return QVariant::fromValue( item.entry->i_downloads );
+ case Role::SERVICE_SCORE:
+ return item.entry->i_score / 100;
+ case Role::SERVICE_STATE:
+ return item.entry->e_state;
+ case Role::SERVICE_ARTWORK:
+ return item.artworkUrl;
+ default:
+ return {};
+ }
+}
+
+QHash<int, QByteArray> ServicesDiscoveryModel::roleNames() const
+{
+ return {
+ { Role::SERVICE_NAME, "name" },
+ { Role::SERVICE_AUTHOR, "author"},
+ { Role::SERVICE_SUMMARY, "summary" },
+ { Role::SERVICE_DESCRIPTION, "description" },
+ { Role::SERVICE_DOWNLOADS, "downloads" },
+ { Role::SERVICE_SCORE, "score" },
+ { Role::SERVICE_STATE, "state" },
+ { Role::SERVICE_ARTWORK, "artwork" }
+ };
+}
+
+QMap<QString, QVariant> ServicesDiscoveryModel::getDataAt(int idx)
+{
+ QMap<QString, QVariant> dataDict;
+ QHash<int,QByteArray> roles = roleNames();
+ for (auto role: roles.keys()) {
+ dataDict[roles[role]] = data(index(idx), role);
+ }
+ return dataDict;
+}
+
+void ServicesDiscoveryModel::installService(int idx)
+{
+ if ( idx < 0 || idx >= (int)m_items.size() )
+ return;
+
+ addon_uuid_t uuid;
+ memcpy( uuid, m_items[idx].entry->uuid, sizeof( uuid ) );
+ addons_manager_Install( m_manager, uuid );
+}
+
+void ServicesDiscoveryModel::removeService(int idx)
+{
+ if ( idx < 0 || idx >= (int)m_items.size() )
+ return;
+
+ addon_uuid_t uuid;
+ memcpy( uuid, m_items[idx].entry->uuid, sizeof( uuid ) );
+ addons_manager_Remove( m_manager, uuid );
+}
+
+int ServicesDiscoveryModel::rowCount(const QModelIndex& parent) const
+{
+ if ( parent.isValid() )
+ return 0;
+ return getCount();
+}
+
+int ServicesDiscoveryModel::getCount() const
+{
+ assert( m_items.size() < INT32_MAX );
+ return static_cast<int>( m_items.size() );
+}
+
+void ServicesDiscoveryModel::setCtx(QmlMainContext* ctx)
+{
+ if (ctx) {
+ m_ctx = ctx;
+ }
+ if (m_ctx) {
+ initializeManager();
+ }
+ emit ctxChanged();
+}
+
+void ServicesDiscoveryModel::initializeManager()
+{
+ if ( m_manager )
+ addons_manager_Delete( m_manager );
+
+ struct addons_manager_owner owner =
+ {
+ this,
+ addonFoundCallback,
+ addonsDiscoveryEndedCallback,
+ addonChangedCallback,
+ };
+
+ m_manager = addons_manager_New( VLC_OBJECT( m_ctx->getIntf() ), &owner );
+ assert( m_manager );
+
+ m_parsingPending = true;
+ emit parsingPendingChanged();
+ addons_manager_LoadCatalog( m_manager );
+ addons_manager_Gather( m_manager, "repo://" );
+}
+
+void ServicesDiscoveryModel::addonFoundCallback( addons_manager_t *manager,
+ addon_entry_t *entry )
+{
+ if (entry->e_type != ADDON_SERVICE_DISCOVERY)
+ return;
+ ServicesDiscoveryModel *me = (ServicesDiscoveryModel *) manager->owner.sys;
+ QMetaObject::invokeMethod( me, [me, entryPtr = AddonPtr(entry)]()
+ {
+ me->addonFound( std::move( entryPtr ) );
+ }, Qt::QueuedConnection);
+}
+
+void ServicesDiscoveryModel::addonsDiscoveryEndedCallback( addons_manager_t *manager )
+{
+ ServicesDiscoveryModel *me = (ServicesDiscoveryModel *) manager->owner.sys;
+ QMetaObject::invokeMethod( me, [me]()
+ {
+ me->discoveryEnded();
+ }, Qt::QueuedConnection);
+}
+
+void ServicesDiscoveryModel::addonChangedCallback( addons_manager_t *manager,
+ addon_entry_t *entry )
+{
+ if (entry->e_type != ADDON_SERVICE_DISCOVERY)
+ return;
+ ServicesDiscoveryModel *me = (ServicesDiscoveryModel *) manager->owner.sys;
+ QMetaObject::invokeMethod( me, [me, entryPtr = AddonPtr(entry)]()
+ {
+ me->addonChanged( std::move( entryPtr ) );
+ }, Qt::QueuedConnection);
+}
+
+void ServicesDiscoveryModel::addonFound( ServicesDiscoveryModel::AddonPtr addon )
+{
+ beginInsertRows( QModelIndex(), getCount(), getCount() );
+ m_items.emplace_back(addon);
+ endInsertRows();
+ emit countChanged();
+}
+
+void ServicesDiscoveryModel::addonChanged( ServicesDiscoveryModel::AddonPtr addon )
+{
+ for ( int r = 0; r < getCount(); ++r )
+ {
+ if ( memcmp( m_items[r].entry->uuid, addon->uuid, sizeof( addon->uuid ) ) )
+ continue;
+
+ m_items[r] = addon;
+ emit dataChanged( index( r, 0 ), index( r, 0 ) );
+ }
+}
+
+void ServicesDiscoveryModel::discoveryEnded()
+{
+ assert( m_parsingPending );
+ m_parsingPending = false;
+ emit parsingPendingChanged();
+}
+
+ServicesDiscoveryModel::Item::Item( ServicesDiscoveryModel::AddonPtr addon )
+{
+ *this = addon;
+}
+
+ServicesDiscoveryModel::Item &ServicesDiscoveryModel::Item::operator=( ServicesDiscoveryModel::AddonPtr addon )
+{
+ name = qfu( addon->psz_name );
+ summery = qfu( addon->psz_summary ).trimmed();
+ description = qfu( addon->psz_description ).trimmed();
+ author = qfu( addon->psz_author );
+ sourceUrl = QUrl( addon->psz_source_uri );
+ entry = addon;
+
+ if ( addon->psz_image_data ) {
+ QDir dir( config_GetUserDir( VLC_CACHE_DIR ) );
+ dir.mkdir("art");
+ dir.cd("art");
+ dir.mkdir("qt-addon-covers");
+ dir.cd("qt-addon-covers");
+
+ QString id = addons_uuid_to_psz( &addon->uuid );
+ QString filename = QString("addon_thumbnail_%1.png").arg(id);
+ QString absoluteFilePath = dir.absoluteFilePath(filename);
+
+ if ( !QFileInfo::exists( absoluteFilePath )) {
+ QPixmap pixmap;
+ pixmap.loadFromData( QByteArray::fromBase64( QByteArray( addon->psz_image_data ) ),
+ 0,
+ Qt::AutoColor
+ );
+ pixmap.save(absoluteFilePath);
+ }
+ artworkUrl = QUrl::fromLocalFile( absoluteFilePath );
+ }
+ else if ( addon->e_flags & ADDON_BROKEN )
+ artworkUrl = QUrl( ":/addons/broken.svg" );
+ else
+ artworkUrl = QUrl( ":/addons/default.svg" );
+
+ return *this;
+}
diff --git a/modules/gui/qt/network/servicesdiscoverymodel.hpp b/modules/gui/qt/network/servicesdiscoverymodel.hpp
new file mode 100644
index 0000000000..0eb5221bdc
--- /dev/null
+++ b/modules/gui/qt/network/servicesdiscoverymodel.hpp
@@ -0,0 +1,124 @@
+/*****************************************************************************
+ * Copyright (C) 2020 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 MLServicesDiscoveryModel_HPP
+#define MLServicesDiscoveryModel_HPP
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <QAbstractListModel>
+
+#include <vlc_media_library.h>
+#include <vlc_media_source.h>
+#include <vlc_threads.h>
+#include <vlc_addons.h>
+#include <vlc_cxx_helpers.hpp>
+
+#include <util/qml_main_context.hpp>
+
+#include <memory>
+
+class ServicesDiscoveryModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+
+ Q_PROPERTY(QmlMainContext* ctx READ getCtx WRITE setCtx NOTIFY ctxChanged)
+ Q_PROPERTY(bool parsingPending READ getParsingPending NOTIFY parsingPendingChanged)
+ Q_PROPERTY(int count READ getCount NOTIFY countChanged)
+
+ enum State // equivalent to addon_state_t
+ {
+ NOTINSTALLED = 0,
+ INSTALLING,
+ INSTALLED,
+ UNINSTALLING
+ };
+ Q_ENUM(State)
+
+ enum Role {
+ SERVICE_NAME = Qt::UserRole + 1,
+ SERVICE_AUTHOR,
+ SERVICE_SUMMARY,
+ SERVICE_DESCRIPTION,
+ SERVICE_DOWNLOADS,
+ SERVICE_SCORE,
+ SERVICE_STATE,
+ SERVICE_ARTWORK
+ };
+ Q_ENUM(Role);
+
+ explicit ServicesDiscoveryModel(QObject* parent = nullptr);
+ virtual ~ServicesDiscoveryModel() override;
+
+ QVariant data(const QModelIndex& index, int role) const override;
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex& parent) const override;
+
+ void setCtx(QmlMainContext* ctx);
+
+ inline QmlMainContext* getCtx() const { return m_ctx; }
+ inline bool getParsingPending() const { return m_parsingPending; }
+ int getCount() const;
+
+ Q_INVOKABLE QMap<QString, QVariant> getDataAt(int idx);
+ Q_INVOKABLE void installService(int idx);
+ Q_INVOKABLE void removeService(int idx);
+
+signals:
+ void parsingPendingChanged();
+ void countChanged();
+ void ctxChanged();
+
+private:
+ using AddonPtr = vlc_shared_data_ptr_type(addon_entry_t,
+ addon_entry_Hold, addon_entry_Release);
+
+ void initializeManager();
+ static void addonFoundCallback( addons_manager_t *, struct addon_entry_t * );
+ static void addonsDiscoveryEndedCallback( addons_manager_t * );
+ static void addonChangedCallback( addons_manager_t *, struct addon_entry_t * );
+
+ void addonFound( AddonPtr addon );
+ void addonChanged( AddonPtr addon );
+ void discoveryEnded();
+
+ struct Item
+ {
+ QString name;
+ QString summery;
+ QString description;
+ QString author;
+ QUrl sourceUrl;
+ QUrl artworkUrl;
+ AddonPtr entry;
+
+ Item( AddonPtr addon );
+ Item& operator =( AddonPtr addon );
+ };
+
+ std::vector<Item> m_items;
+ QmlMainContext* m_ctx = nullptr;
+ addons_manager_t* m_manager = nullptr;
+ bool m_parsingPending = false;
+};
+
+#endif // MLServicesDiscoveryModel_HPP
More information about the vlc-commits
mailing list