vlc | branch: master | Prince Gupta <guptaprince8...@gmail.com> | Fri Sep 11 17:14:03 2020 +0530| [de193b72860146cf0a8ed8e2a55897ceb9adfe81] | committer: Pierre Lamot
qt: add ServicesDiscoveryModel Signed-off-by: Pierre Lamot <pie...@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 _______________________________________________ vlc-commits mailing list vlc-commits@videolan.org https://mailman.videolan.org/listinfo/vlc-commits