[vlc-devel] [PATCH 3/7] qt: split MLNetwork model in MLNetworkDeviceModel and MLNetworkMediaModel.

Pierre Lamot pierre at videolabs.io
Mon Oct 21 18:08:55 CEST 2019


  * MLNetworkDeviceModel allows to list available devices
  * MLNetworkMediaModel allows to browse a media tree
---
 modules/gui/qt/Makefile.am                    |  11 +-
 .../mediacenter/mlnetworkdevicemodel.cpp      | 238 ++++++++++++++++++
 .../mediacenter/mlnetworkdevicemodel.hpp      | 122 +++++++++
 ...tworkmodel.cpp => mlnetworkmediamodel.cpp} | 222 +++-------------
 ...tworkmodel.hpp => mlnetworkmediamodel.hpp} |  96 ++-----
 .../mediacenter/mlnetworksourcelistener.cpp   |  51 ++++
 .../mediacenter/mlnetworksourcelistener.hpp   |  78 ++++++
 modules/gui/qt/main_interface.cpp             |   6 +-
 .../mediacenter/MCNetworkBrowseDisplay.qml    |  14 +-
 .../qml/mediacenter/MCNetworkHomeDisplay.qml  |  10 +-
 .../MCNetworksSectionSelectableDM.qml         |   9 +-
 .../qt/qml/mediacenter/NetworkGridItem.qml    |  10 +-
 .../qt/qml/mediacenter/NetworkListItem.qml    |  12 +-
 13 files changed, 596 insertions(+), 283 deletions(-)
 create mode 100644 modules/gui/qt/components/mediacenter/mlnetworkdevicemodel.cpp
 create mode 100644 modules/gui/qt/components/mediacenter/mlnetworkdevicemodel.hpp
 rename modules/gui/qt/components/mediacenter/{mlnetworkmodel.cpp => mlnetworkmediamodel.cpp} (50%)
 rename modules/gui/qt/components/mediacenter/{mlnetworkmodel.hpp => mlnetworkmediamodel.hpp} (62%)
 create mode 100644 modules/gui/qt/components/mediacenter/mlnetworksourcelistener.cpp
 create mode 100644 modules/gui/qt/components/mediacenter/mlnetworksourcelistener.hpp

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 2605d17b0f..deab19fdb1 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -155,8 +155,12 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/components/mediacenter/mlgenre.hpp \
 	gui/qt/components/mediacenter/mlgenremodel.cpp \
 	gui/qt/components/mediacenter/mlgenremodel.hpp \
-	gui/qt/components/mediacenter/mlnetworkmodel.cpp \
-	gui/qt/components/mediacenter/mlnetworkmodel.hpp \
+	gui/qt/components/mediacenter/mlnetworkdevicemodel.cpp \
+	gui/qt/components/mediacenter/mlnetworkdevicemodel.hpp \
+	gui/qt/components/mediacenter/mlnetworkmediamodel.cpp \
+	gui/qt/components/mediacenter/mlnetworkmediamodel.hpp \
+	gui/qt/components/mediacenter/mlnetworksourcelistener.cpp \
+	gui/qt/components/mediacenter/mlnetworksourcelistener.hpp \
 	gui/qt/components/mediacenter/mlvideo.hpp \
 	gui/qt/components/mediacenter/mlvideo.cpp \
 	gui/qt/components/mediacenter/mlvideomodel.cpp \
@@ -294,7 +298,8 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/components/mediacenter/mlartistmodel.moc.cpp \
 	gui/qt/components/mediacenter/mlgenre.moc.cpp \
 	gui/qt/components/mediacenter/mlgenremodel.moc.cpp \
-	gui/qt/components/mediacenter/mlnetworkmodel.moc.cpp \
+	gui/qt/components/mediacenter/mlnetworkdevicemodel.moc.cpp \
+	gui/qt/components/mediacenter/mlnetworkmediamodel.moc.cpp \
 	gui/qt/components/mediacenter/mlvideo.moc.cpp \
 	gui/qt/components/mediacenter/mlvideomodel.moc.cpp \
 	gui/qt/components/mediacenter/mlrecentsvideomodel.moc.cpp \
diff --git a/modules/gui/qt/components/mediacenter/mlnetworkdevicemodel.cpp b/modules/gui/qt/components/mediacenter/mlnetworkdevicemodel.cpp
new file mode 100644
index 0000000000..e2eb5f9ba2
--- /dev/null
+++ b/modules/gui/qt/components/mediacenter/mlnetworkdevicemodel.cpp
@@ -0,0 +1,238 @@
+/*****************************************************************************
+ * Copyright (C) 2019 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 "mlnetworkdevicemodel.hpp"
+#include "mlnetworkmediamodel.hpp"
+
+#include "components/playlist/media.hpp"
+#include "components/playlist/playlist_controller.hpp"
+
+namespace {
+
+enum Role {
+    NETWORK_NAME = Qt::UserRole + 1,
+    NETWORK_MRL,
+    NETWORK_TYPE,
+    NETWORK_PROTOCOL,
+    NETWORK_SOURCE,
+    NETWORK_TREE,
+};
+
+}
+
+MLNetworkDeviceModel::MLNetworkDeviceModel( QObject* parent )
+    : QAbstractListModel( parent )
+    , m_ml( nullptr )
+{
+}
+
+QVariant MLNetworkDeviceModel::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 NETWORK_NAME:
+            return item.name;
+        case NETWORK_MRL:
+            return item.mainMrl;
+        case NETWORK_TYPE:
+            return item.type;
+        case NETWORK_PROTOCOL:
+            return item.protocol;
+        case NETWORK_SOURCE:
+            return item.mediaSource->description;
+        case NETWORK_TREE:
+            return QVariant::fromValue( NetworkTreeItem(item.mediaSource, item.inputItem.get()) );
+        default:
+            return {};
+    }
+}
+
+QHash<int, QByteArray> MLNetworkDeviceModel::roleNames() const
+{
+    return {
+        { NETWORK_NAME, "name" },
+        { NETWORK_MRL, "mrl" },
+        { NETWORK_TYPE, "type" },
+        { NETWORK_PROTOCOL, "protocol" },
+        { NETWORK_SOURCE, "source" },
+        { NETWORK_TREE, "tree" },
+    };
+}
+
+
+int MLNetworkDeviceModel::rowCount(const QModelIndex& parent) const
+{
+    if ( parent.isValid() )
+        return 0;
+    assert( m_items.size() < INT32_MAX );
+    return static_cast<int>( m_items.size() );
+}
+
+
+void MLNetworkDeviceModel::setCtx(QmlMainContext* ctx)
+{
+    if (ctx) {
+        m_ctx = ctx;
+        m_ml = vlc_ml_instance_get( m_ctx->getIntf() );
+    }
+    if (m_ctx && m_sdSource != CAT_UNDEFINED) {
+        initializeMediaSources();
+    }
+    emit ctxChanged();
+}
+
+void MLNetworkDeviceModel::setSdSource(SDCatType s)
+{
+    m_sdSource = s;
+    if (m_ctx && m_sdSource != CAT_UNDEFINED) {
+        initializeMediaSources();
+    }
+    emit sdSourceChanged();
+}
+
+bool MLNetworkDeviceModel::initializeMediaSources()
+{
+    auto libvlc = vlc_object_instance(m_ctx->getIntf());
+
+    m_listeners.clear();
+    if (!m_items.empty()) {
+        beginResetModel();
+        m_items.clear();
+        endResetModel();
+    }
+
+    auto provider = vlc_media_source_provider_Get( libvlc );
+
+    using SourceMetaPtr = std::unique_ptr<vlc_media_source_meta_list_t,
+                                          decltype( &vlc_media_source_meta_list_Delete )>;
+
+    SourceMetaPtr providerList( vlc_media_source_provider_List( provider, static_cast<services_discovery_category_e>(m_sdSource) ),
+                                &vlc_media_source_meta_list_Delete );
+    if ( providerList == nullptr )
+        return false;
+
+    auto nbProviders = vlc_media_source_meta_list_Count( providerList.get() );
+
+    for ( auto i = 0u; i < nbProviders; ++i )
+    {
+        auto meta = vlc_media_source_meta_list_Get( providerList.get(), i );
+        auto mediaSource = vlc_media_source_provider_GetMediaSource( provider,
+                                                                     meta->name );
+        if ( mediaSource == nullptr )
+            continue;
+        std::unique_ptr<MLNetworkSourceListener> l{ new MLNetworkSourceListener(
+                        MediaSourcePtr{ mediaSource, false }, this ) };
+        if ( l->listener == nullptr )
+            return false;
+        m_listeners.push_back( std::move( l ) );
+    }
+    return m_listeners.empty() == false;
+}
+
+
+void MLNetworkDeviceModel::onItemCleared( MediaSourcePtr mediaSource, input_item_node_t* node )
+{
+    refreshDeviceList( std::move( mediaSource), node->pp_children, node->i_children, true );
+}
+
+void MLNetworkDeviceModel::onItemAdded( MediaSourcePtr mediaSource, input_item_node_t* parent,
+                                  input_item_node_t *const children[],
+                                  size_t count )
+{
+    refreshDeviceList( std::move( mediaSource ), children, count, false );
+}
+
+void MLNetworkDeviceModel::onItemRemoved( MediaSourcePtr,
+                                    input_item_node_t *const children[],
+                                    size_t count )
+{
+    for ( auto i = 0u; i < count; ++i )
+    {
+        input_item_t* p_item = children[i]->p_item;
+        input_item_Hold( p_item );
+        QMetaObject::invokeMethod(this, [this, p_item]() {
+            QUrl itemUri = QUrl::fromEncoded(p_item->psz_uri);
+            auto it = std::find_if( begin( m_items ), end( m_items ), [p_item, itemUri](const Item& i) {
+                return QString::compare( qfu(p_item->psz_name), i.name, Qt::CaseInsensitive ) == 0 &&
+                    itemUri.scheme() == i.mainMrl.scheme();
+            });
+            if ( it == end( m_items ) )
+            {
+                input_item_Release( p_item );
+                return;
+            }
+            auto mrlIt = std::find_if( begin( (*it).mrls ), end( (*it).mrls),
+                                       [itemUri]( const QUrl& mrl ) {
+                return mrl == itemUri;
+            });
+            input_item_Release( p_item );
+            if ( mrlIt == end( (*it).mrls ) )
+                return;
+            (*it).mrls.erase( mrlIt );
+            if ( (*it).mrls.empty() == false )
+                return;
+            auto idx = std::distance( begin( m_items ), it );
+            beginRemoveRows({}, idx, idx );
+            m_items.erase( it );
+            endRemoveRows();
+        });
+    }
+}
+
+void MLNetworkDeviceModel::refreshDeviceList( MediaSourcePtr mediaSource, input_item_node_t* const children[], size_t count, bool clear )
+{
+    if ( clear == true )
+    {
+        QMetaObject::invokeMethod(this, [this, mediaSource]() {
+            beginResetModel();
+            m_items.erase(std::remove_if(m_items.begin(), m_items.end(), [mediaSource](const Item& value) {
+                return value.mediaSource == mediaSource;
+            }), m_items.end());
+            endResetModel();
+        });
+    }
+    for ( auto i = 0u; i < count; ++i )
+    {
+        Item item;
+        item.mainMrl = QUrl::fromEncoded( QByteArray{ children[i]->p_item->psz_uri }.append( '/' ) );
+        item.name = qfu(children[i]->p_item->psz_name);
+        item.mrls.push_back( item.mainMrl );
+        item.type = static_cast<ItemType>( children[i]->p_item->i_type );
+        item.protocol = item.mainMrl.scheme();
+        item.mediaSource = mediaSource;
+        item.inputItem = InputItemPtr(children[i]->p_item);
+
+        QMetaObject::invokeMethod(this, [this, item]() mutable {
+            auto it = std::find_if( begin( m_items ), end( m_items ), [&item](const Item& i) {
+                return QString::compare(item.name , i.name, Qt::CaseInsensitive ) == 0 &&
+                        item.mainMrl.scheme() == i.mainMrl.scheme();
+            });
+            if ( it != end( m_items ) )
+                return;
+            beginInsertRows( {}, m_items.size(), m_items.size() );
+            m_items.push_back( std::move( item ) );
+            endInsertRows();
+        });
+    }
+}
diff --git a/modules/gui/qt/components/mediacenter/mlnetworkdevicemodel.hpp b/modules/gui/qt/components/mediacenter/mlnetworkdevicemodel.hpp
new file mode 100644
index 0000000000..a1d354c3a3
--- /dev/null
+++ b/modules/gui/qt/components/mediacenter/mlnetworkdevicemodel.hpp
@@ -0,0 +1,122 @@
+/*****************************************************************************
+ * Copyright (C) 2019 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 MLNETWORKDEVICEMODEL_HPP
+#define MLNETWORKDEVICEMODEL_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_cxx_helpers.hpp>
+
+#include <components/qml_main_context.hpp>
+#include "mlnetworksourcelistener.hpp"
+
+#include <memory>
+
+class MLNetworkDeviceModel : public QAbstractListModel, public MLNetworkSourceListener::SourceListenerCb
+{
+    Q_OBJECT
+public:
+    enum ItemType{
+        // qt version of input_item_type_e
+        TYPE_UNKNOWN = ITEM_TYPE_UNKNOWN,
+        TYPE_FILE,
+        TYPE_DIRECTORY,
+        TYPE_DISC,
+        TYPE_CARD,
+        TYPE_STREAM,
+        TYPE_PLAYLIST,
+        TYPE_NODE,
+    };
+    Q_ENUM( ItemType )
+
+    enum SDCatType{
+        // qt version of input_item_type_e
+        CAT_UNDEFINED = 0,
+        CAT_DEVICES = SD_CAT_DEVICES,
+        CAT_LAN,
+        CAT_INTERNET,
+        CAT_MYCOMPUTER,
+    };
+    Q_ENUM( SDCatType )
+
+
+    Q_PROPERTY(QmlMainContext* ctx READ getCtx WRITE setCtx NOTIFY ctxChanged)
+    Q_PROPERTY(SDCatType sd_source READ getSdSource WRITE setSdSource NOTIFY sdSourceChanged)
+
+
+public:
+    MLNetworkDeviceModel( QObject* parent = nullptr );
+
+    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);
+    void setSdSource(SDCatType s);
+
+    inline QmlMainContext* getCtx() { return m_ctx; }
+    inline SDCatType getSdSource() { return m_sdSource; }
+
+signals:
+    void ctxChanged();
+    void sdSourceChanged();
+
+private:
+    using MediaSourcePtr = vlc_shared_data_ptr_type(vlc_media_source_t,
+                                    vlc_media_source_Hold, vlc_media_source_Release);
+
+    using InputItemPtr = vlc_shared_data_ptr_type(input_item_t,
+                                                  input_item_Hold,
+                                                  input_item_Release);
+
+    struct Item
+    {
+        QString name;
+        QUrl mainMrl;
+        std::vector<QUrl> mrls;
+        QString protocol;
+        ItemType type;
+        MediaSourcePtr mediaSource;
+        InputItemPtr inputItem;
+    };
+
+    bool initializeMediaSources();
+    void onItemCleared( MediaSourcePtr mediaSource, input_item_node_t* node ) override;
+    void onItemAdded( MediaSourcePtr mediaSource, input_item_node_t* parent, input_item_node_t *const children[], size_t count ) override;
+    void onItemRemoved( MediaSourcePtr mediaSource, input_item_node_t *const children[], size_t count ) override;
+
+    void refreshDeviceList(MediaSourcePtr mediaSource, input_item_node_t* const children[], size_t count , bool clear);
+
+private:
+    std::vector<Item> m_items;
+    QmlMainContext* m_ctx = nullptr;
+    vlc_medialibrary_t* m_ml = nullptr;
+    SDCatType m_sdSource = CAT_UNDEFINED;
+
+    std::vector<std::unique_ptr<MLNetworkSourceListener>> m_listeners;
+};
+
+#endif // MLNETWORKDEVICEMODEL_HPP
diff --git a/modules/gui/qt/components/mediacenter/mlnetworkmodel.cpp b/modules/gui/qt/components/mediacenter/mlnetworkmediamodel.cpp
similarity index 50%
rename from modules/gui/qt/components/mediacenter/mlnetworkmodel.cpp
rename to modules/gui/qt/components/mediacenter/mlnetworkmediamodel.cpp
index a12fb90983..53b9cab2d8 100644
--- a/modules/gui/qt/components/mediacenter/mlnetworkmodel.cpp
+++ b/modules/gui/qt/components/mediacenter/mlnetworkmediamodel.cpp
@@ -4,11 +4,11 @@
  * 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.
+ * (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
+ * 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
@@ -16,10 +16,13 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
-#include "mlnetworkmodel.hpp"
+#include "mlnetworkmediamodel.hpp"
 
 #include "mlhelper.hpp"
 
+#include "components/playlist/media.hpp"
+#include "components/playlist/playlist_controller.hpp"
+
 namespace {
 
 enum Role {
@@ -35,13 +38,13 @@ enum Role {
 
 }
 
-MLNetworkModel::MLNetworkModel( QObject* parent )
+MLNetworkMediaModel::MLNetworkMediaModel( QObject* parent )
     : QAbstractListModel( parent )
     , m_ml( nullptr )
 {
 }
 
-QVariant MLNetworkModel::data( const QModelIndex& index, int role ) const
+QVariant MLNetworkMediaModel::data( const QModelIndex& index, int role ) const
 {
     if (!m_ctx)
         return {};
@@ -72,7 +75,7 @@ QVariant MLNetworkModel::data( const QModelIndex& index, int role ) const
     }
 }
 
-QHash<int, QByteArray> MLNetworkModel::roleNames() const
+QHash<int, QByteArray> MLNetworkMediaModel::roleNames() const
 {
     return {
         { NETWORK_NAME, "name" },
@@ -86,7 +89,7 @@ QHash<int, QByteArray> MLNetworkModel::roleNames() const
     };
 }
 
-int MLNetworkModel::rowCount(const QModelIndex& parent) const
+int MLNetworkMediaModel::rowCount(const QModelIndex& parent) const
 {
     if ( parent.isValid() )
         return 0;
@@ -94,13 +97,14 @@ int MLNetworkModel::rowCount(const QModelIndex& parent) const
     return static_cast<int>( m_items.size() );
 }
 
-Qt::ItemFlags MLNetworkModel::flags( const QModelIndex& idx ) const
+Qt::ItemFlags MLNetworkMediaModel::flags( const QModelIndex& idx ) const
 {
     return QAbstractListModel::flags( idx ) | Qt::ItemIsEditable;
 }
 
-bool MLNetworkModel::setData( const QModelIndex& idx, const QVariant& value, int role )
+bool MLNetworkMediaModel::setData( const QModelIndex& idx, const QVariant& value, int role )
 {
+    printf("MLNetworkMediaModel::setData `\n");
     if (!m_ml)
         return false;
 
@@ -109,6 +113,7 @@ bool MLNetworkModel::setData( const QModelIndex& idx, const QVariant& value, int
     auto enabled = value.toBool();
     if ( m_items[idx.row()].indexed == enabled )
         return  false;
+    printf("MLNetworkMediaModel::setData change indexed`\n");
     int res;
     if ( enabled )
         res = vlc_ml_add_folder( m_ml, qtu( m_items[idx.row()].mainMrl.toString( QUrl::None ) ) );
@@ -119,7 +124,8 @@ bool MLNetworkModel::setData( const QModelIndex& idx, const QVariant& value, int
     return res == VLC_SUCCESS;
 }
 
-void MLNetworkModel::setIndexed(bool indexed)
+
+void MLNetworkMediaModel::setIndexed(bool indexed)
 {
     if (indexed == m_indexed || !m_canBeIndexed)
         return;
@@ -135,7 +141,7 @@ void MLNetworkModel::setIndexed(bool indexed)
     }
 }
 
-void MLNetworkModel::setCtx(QmlMainContext* ctx)
+void MLNetworkMediaModel::setCtx(QmlMainContext* ctx)
 {
     if (ctx) {
         m_ctx = ctx;
@@ -147,7 +153,7 @@ void MLNetworkModel::setCtx(QmlMainContext* ctx)
     emit ctxChanged();
 }
 
-void MLNetworkModel::setTree(QVariant parentTree)
+void MLNetworkMediaModel::setTree(QVariant parentTree)
 {
     if (parentTree.canConvert<NetworkTreeItem>())
         m_treeItem = parentTree.value<NetworkTreeItem>();
@@ -159,74 +165,23 @@ void MLNetworkModel::setTree(QVariant parentTree)
     }
     emit treeChanged();
 }
-void MLNetworkModel::setIsOnProviderList(bool b)
-{
-    m_isOnProviderList = b;
-
-    emit isOnProviderListChanged();
-}
-void MLNetworkModel::setSdSource(QString s)
-{
-    beginResetModel();
-    m_sdSource = s;
-    emit sdSourceChanged();
-    endResetModel();
-}
 
-bool MLNetworkModel::initializeMediaSources()
+bool MLNetworkMediaModel::initializeMediaSources()
 {
     auto libvlc = vlc_object_instance(m_ctx->getIntf());
 
-    m_listeners.clear();
+    m_listener.reset();
     if (!m_items.empty()) {
         beginResetModel();
         m_items.clear();
         endResetModel();
     }
 
-    if ( m_treeItem.media == nullptr )
-    {
-        // If there's no target media, we're handling device discovery
-        setIsOnProviderList( true );
-        auto provider = vlc_media_source_provider_Get( libvlc );
-
-        using SourceMetaPtr = std::unique_ptr<vlc_media_source_meta_list_t,
-                                              decltype( &vlc_media_source_meta_list_Delete )>;
-
-        services_discovery_category_e cat = SD_CAT_LAN;
-        if ( m_sdSource == "SD_CAT_DEVICES" )
-            cat = SD_CAT_DEVICES;
-        else if ( m_sdSource == "SD_CAT_MYCOMPUTER" )
-            cat = SD_CAT_MYCOMPUTER;
-        else if ( m_sdSource == "SD_CAT_INTERNET" )
-            cat = SD_CAT_INTERNET;
-
-        SourceMetaPtr providerList( vlc_media_source_provider_List( provider, cat ),
-                                    &vlc_media_source_meta_list_Delete );
-        if ( providerList == nullptr )
-            return false;
-
-        auto nbProviders = vlc_media_source_meta_list_Count( providerList.get() );
+    if (!m_treeItem)
+        return false;
 
-        for ( auto i = 0u; i < nbProviders; ++i )
-        {
-            auto meta = vlc_media_source_meta_list_Get( providerList.get(), i );
-            auto mediaSource = vlc_media_source_provider_GetMediaSource( provider,
-                                                                         meta->name );
-            if ( mediaSource == nullptr )
-                continue;
-            std::unique_ptr<SourceListener> l{ new SourceListener(
-                            MediaSourcePtr{ mediaSource, false }, this ) };
-            if ( l->listener == nullptr )
-                return false;
-            m_listeners.push_back( std::move( l ) );
-        }
-        return m_listeners.empty() == false;
-    }
-    // Otherwise, we're listing content from a device or folder
-    setIsOnProviderList( false );
     auto tree = m_treeItem.source->tree;
-    std::unique_ptr<SourceListener> l{ new SourceListener( m_treeItem.source, this ) };
+    std::unique_ptr<MLNetworkSourceListener> l{ new MLNetworkSourceListener( m_treeItem.source, this ) };
     if ( l->listener == nullptr )
         return false;
 
@@ -246,38 +201,32 @@ bool MLNetworkModel::initializeMediaSources()
         emit isIndexedChanged();
     }
 
+
     vlc_media_tree_Preparse( tree, libvlc, m_treeItem.media.get() );
-    m_listeners.push_back( std::move( l ) );
+    m_listener = std::move( l );
 
     return true;
 }
 
-void MLNetworkModel::onItemCleared( MediaSourcePtr mediaSource, input_item_node_t* node )
+void MLNetworkMediaModel::onItemCleared( MediaSourcePtr mediaSource, input_item_node_t* node )
 {
-    if ( m_treeItem.media != nullptr )
-    {
-        input_item_node_t *res;
-        input_item_node_t *parent;
-        if ( vlc_media_tree_Find( m_treeItem.source->tree, m_treeItem.media.get(),
-                                          &res, &parent ) == false )
-            return;
-        refreshMediaList( std::move( mediaSource ), res->pp_children, res->i_children, true );
-    }
-    else
-        refreshDeviceList( std::move( mediaSource), node->pp_children, node->i_children, true );
+    input_item_node_t *res;
+    input_item_node_t *parent;
+    if ( vlc_media_tree_Find( m_treeItem.source->tree, m_treeItem.media.get(),
+                                      &res, &parent ) == false )
+        return;
+    refreshMediaList( std::move( mediaSource ), res->pp_children, res->i_children, true );
 }
 
-void MLNetworkModel::onItemAdded( MediaSourcePtr mediaSource, input_item_node_t* parent,
+void MLNetworkMediaModel::onItemAdded( MediaSourcePtr mediaSource, input_item_node_t* parent,
                                   input_item_node_t *const children[],
                                   size_t count )
 {
-    if ( m_treeItem.media == nullptr )
-        refreshDeviceList( std::move( mediaSource ), children, count, false );
-    else if ( parent->p_item == m_treeItem.media.get() )
+    if ( parent->p_item == m_treeItem.media.get() )
         refreshMediaList( std::move( mediaSource ), children, count, false );
 }
 
-void MLNetworkModel::onItemRemoved( MediaSourcePtr,
+void MLNetworkMediaModel::onItemRemoved( MediaSourcePtr,
                                     input_item_node_t *const children[],
                                     size_t count )
 {
@@ -285,7 +234,7 @@ void MLNetworkModel::onItemRemoved( MediaSourcePtr,
     {
         input_item_t* p_item = children[i]->p_item;
         input_item_Hold( p_item );
-        callAsync([this, p_item]() {
+        QMetaObject::invokeMethod(this, [this, p_item]() {
             QUrl itemUri = QUrl::fromEncoded(p_item->psz_uri);
             auto it = std::find_if( begin( m_items ), end( m_items ), [p_item, itemUri](const Item& i) {
                 return QString::compare( qfu(p_item->psz_name), i.name, Qt::CaseInsensitive ) == 0 &&
@@ -314,7 +263,7 @@ void MLNetworkModel::onItemRemoved( MediaSourcePtr,
     }
 }
 
-void MLNetworkModel::refreshMediaList( MediaSourcePtr mediaSource,
+void MLNetworkMediaModel::refreshMediaList( MediaSourcePtr mediaSource,
                                        input_item_node_t* const children[], size_t count,
                                        bool clear )
 {
@@ -340,10 +289,10 @@ void MLNetworkModel::refreshMediaList( MediaSourcePtr mediaSource,
                                     &item.indexed ) != VLC_SUCCESS )
                 item.indexed = false;
         }
-        item.tree = NetworkTreeItem( mediaSource, it, m_treeItem.media.get() );
+        item.tree = NetworkTreeItem( mediaSource, it );
         items->push_back( std::move( item ) );
     }
-    callAsync([this, clear, items]() {
+    QMetaObject::invokeMethod(this, [this, clear, items]() {
         std::unique_ptr<std::vector<Item>> itemsPtr{ items };
         if ( clear == true )
         {
@@ -360,97 +309,8 @@ void MLNetworkModel::refreshMediaList( MediaSourcePtr mediaSource,
     });
 }
 
-void MLNetworkModel::refreshDeviceList( MediaSourcePtr mediaSource, input_item_node_t* const children[], size_t count, bool clear )
-{
-    if ( clear == true )
-    {
-        callAsync([this, mediaSource]() {
-            beginResetModel();
-            m_items.erase(std::remove_if(m_items.begin(), m_items.end(), [mediaSource](const Item& value) {
-                return value.mediaSource == mediaSource;
-            }), m_items.end());
-            endResetModel();
-        });
-    }
-    for ( auto i = 0u; i < count; ++i )
-    {
-        Item item;
-        item.mainMrl = QUrl::fromEncoded( QByteArray{ children[i]->p_item->psz_uri }.append( '/' ) );
-        item.name = qfu(children[i]->p_item->psz_name);
-        item.mrls.push_back( item.mainMrl );
-        item.indexed = false;
-        item.type = static_cast<ItemType>( children[i]->p_item->i_type );
-        item.canBeIndexed = canBeIndexed( item.mainMrl , item.type );
-        item.protocol = item.mainMrl.scheme();
-        item.tree = NetworkTreeItem{ mediaSource,
-                                     children[i]->p_item,
-                                     nullptr };
-        item.mediaSource = mediaSource;
-
-        callAsync([this, item]() mutable {
-            auto it = std::find_if( begin( m_items ), end( m_items ), [&item](const Item& i) {
-                return QString::compare(item.name , i.name, Qt::CaseInsensitive ) == 0 &&
-                        item.mainMrl.scheme() == i.mainMrl.scheme();
-            });
-            if ( it != end( m_items ) )
-                return;
-            if ( item.canBeIndexed == true )
-            {
-                if ( vlc_ml_is_indexed( m_ml, qtu( item.mainMrl.toString( QUrl::None ) ),
-                                        &item.indexed ) != VLC_SUCCESS )
-                    item.indexed = false;
-            }
-            beginInsertRows( {}, m_items.size(), m_items.size() );
-            m_items.push_back( std::move( item ) );
-            endInsertRows();
-        });
-    }
-}
-
-MLNetworkModel::SourceListener::SourceListener(MediaSourcePtr s, MLNetworkModel* m)
-    : source( s )
-    , listener( nullptr, [s]( vlc_media_tree_listener_id* l ) {
-            vlc_media_tree_RemoveListener( s->tree, l );
-        } )
-    , model( m )
-{
-    static const vlc_media_tree_callbacks cbs {
-        &SourceListener::onItemCleared,
-        &SourceListener::onItemAdded,
-        &SourceListener::onItemRemoved
-    };
-    auto l = vlc_media_tree_AddListener( s->tree, &cbs, this, true );
-    if ( l == nullptr )
-        return;
-    listener.reset( l );
-}
-
-void MLNetworkModel::SourceListener::onItemCleared( vlc_media_tree_t*, input_item_node_t* node,
-                                                    void* userdata)
-{
-    auto* self = static_cast<SourceListener*>( userdata );
-    self->model->onItemCleared( self->source, node );
-}
-
-void MLNetworkModel::SourceListener::onItemAdded( vlc_media_tree_t *, input_item_node_t * parent,
-                                  input_item_node_t *const children[], size_t count,
-                                  void *userdata )
+bool MLNetworkMediaModel::canBeIndexed(const QUrl& url , ItemType itemType )
 {
-    auto* self = static_cast<SourceListener*>( userdata );
-    auto source = self->source;
-    auto model = self->model;
-    model->onItemAdded( source, parent, children, count );
+    return static_cast<input_item_type_e>(itemType) != ITEM_TYPE_FILE && (url.scheme() == "smb" || url.scheme() == "ftp");
 }
 
-void MLNetworkModel::SourceListener::onItemRemoved( vlc_media_tree_t *, input_item_node_t *,
-                                    input_item_node_t *const children[], size_t count,
-                                    void *userdata )
-{
-    auto* self = static_cast<SourceListener*>( userdata );
-    self->model->onItemRemoved( self->source, children, count );
-}
-
-bool MLNetworkModel::canBeIndexed(const QUrl& url , ItemType itemType )
-{
-    return itemType != ITEM_TYPE_FILE && (url.scheme() == "smb" || url.scheme() == "ftp");
-}
diff --git a/modules/gui/qt/components/mediacenter/mlnetworkmodel.hpp b/modules/gui/qt/components/mediacenter/mlnetworkmediamodel.hpp
similarity index 62%
rename from modules/gui/qt/components/mediacenter/mlnetworkmodel.hpp
rename to modules/gui/qt/components/mediacenter/mlnetworkmediamodel.hpp
index 6e26605dde..e7d84ba326 100644
--- a/modules/gui/qt/components/mediacenter/mlnetworkmodel.hpp
+++ b/modules/gui/qt/components/mediacenter/mlnetworkmediamodel.hpp
@@ -4,11 +4,11 @@
  * 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.
+ * (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
+ * 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
@@ -16,8 +16,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
-#ifndef MLNETWORKMODEL_HPP
-#define MLNETWORKMODEL_HPP
+#ifndef MLNETWORKMEDIAMODEL_HPP
+#define MLNETWORKMEDIAMODEL_HPP
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -31,6 +31,7 @@
 #include <vlc_cxx_helpers.hpp>
 
 #include <components/qml_main_context.hpp>
+#include "mlnetworksourcelistener.hpp"
 
 #include <memory>
 
@@ -45,24 +46,28 @@ class NetworkTreeItem
 {
     Q_GADGET
 public:
-    NetworkTreeItem() : source(nullptr), media(nullptr), parent(nullptr) {}
-    NetworkTreeItem( MediaSourcePtr s, input_item_t* m, input_item_t* p )
+    NetworkTreeItem() : source(nullptr), media(nullptr) {}
+    NetworkTreeItem( MediaSourcePtr s, input_item_t* m )
         : source( std::move( s ) )
         , media( m )
-        , parent( p )
     {
     }
+
     NetworkTreeItem( const NetworkTreeItem& ) = default;
     NetworkTreeItem& operator=( const NetworkTreeItem& ) = default;
     NetworkTreeItem( NetworkTreeItem&& ) = default;
     NetworkTreeItem& operator=( NetworkTreeItem&& ) = default;
 
+    operator bool() const
+    {
+        return source.get() != nullptr;
+    }
+
     MediaSourcePtr source;
     InputItemPtr media;
-    InputItemPtr parent;
 };
 
-class MLNetworkModel : public QAbstractListModel
+class MLNetworkMediaModel : public QAbstractListModel, public MLNetworkSourceListener::SourceListenerCb
 {
     Q_OBJECT
 
@@ -78,12 +83,10 @@ public:
         TYPE_PLAYLIST,
         TYPE_NODE,
     };
-    Q_ENUM( ItemType );
+    Q_ENUM( ItemType )
 
     Q_PROPERTY(QmlMainContext* ctx READ getCtx WRITE setCtx NOTIFY ctxChanged)
     Q_PROPERTY(QVariant tree READ getTree WRITE setTree NOTIFY treeChanged)
-    Q_PROPERTY(bool is_on_provider_list READ getIsOnProviderList WRITE setIsOnProviderList NOTIFY isOnProviderListChanged)
-    Q_PROPERTY(QString sd_source READ getSdSource WRITE setSdSource NOTIFY sdSourceChanged)
 
     Q_PROPERTY(QString name READ getName NOTIFY nameChanged)
     Q_PROPERTY(QUrl url READ getUrl NOTIFY urlChanged)
@@ -91,8 +94,9 @@ public:
     Q_PROPERTY(bool indexed READ isIndexed WRITE setIndexed NOTIFY isIndexedChanged)
     Q_PROPERTY(bool canBeIndexed READ canBeIndexed NOTIFY canBeIndexedChanged)
 
-    explicit MLNetworkModel(QObject* parent = nullptr);
-    MLNetworkModel( QmlMainContext* ctx, QString parentMrl, QObject* parent = nullptr );
+
+    explicit MLNetworkMediaModel(QObject* parent = nullptr);
+    MLNetworkMediaModel( QmlMainContext* ctx, QString parentMrl, QObject* parent = nullptr );
 
     QVariant data(const QModelIndex& index, int role) const override;
     QHash<int, QByteArray> roleNames() const override;
@@ -101,12 +105,12 @@ public:
     Qt::ItemFlags flags( const QModelIndex& idx ) const override;
     bool setData( const QModelIndex& idx,const QVariant& value, int role ) override;
 
-
     void setIndexed(bool indexed);
     void setCtx(QmlMainContext* ctx);
     void setTree(QVariant tree);
-    void setIsOnProviderList(bool b);
-    void setSdSource(QString s);
+
+    inline QmlMainContext* getCtx() const { return m_ctx; }
+    inline QVariant getTree() const { return QVariant::fromValue( m_treeItem); }
 
     inline QString getName() const { return m_name; }
     inline QUrl getUrl() const { return m_url; }
@@ -114,11 +118,6 @@ public:
     inline bool isIndexed() const { return m_indexed; }
     inline bool canBeIndexed() const { return m_canBeIndexed; }
 
-    inline QmlMainContext* getCtx() const { return m_ctx; }
-    inline QVariant getTree() const { return QVariant::fromValue( m_treeItem); }
-    inline bool getIsOnProviderList() const { return m_isOnProviderList; }
-    inline QString getSdSource() const { return m_sdSource; }
-
 signals:
     void nameChanged();
     void urlChanged();
@@ -145,57 +144,17 @@ private:
         MediaSourcePtr mediaSource;
     };
 
-    ///call function @a fun on object thread
-    template <typename Fun>
-    void callAsync(Fun&& fun)
-    {
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
-        QMetaObject::invokeMethod(this, std::forward<Fun>(fun), Qt::QueuedConnection, nullptr);
-#else
-        QObject src;
-        QObject::connect(&src, &QObject::destroyed, q, std::forward<Fun>(fun), Qt::QueuedConnection);
-#endif
-    }
 
     bool initializeMediaSources();
-    void onItemCleared( MediaSourcePtr mediaSource, input_item_node_t* node );
-    void onItemAdded( MediaSourcePtr mediaSource, input_item_node_t* parent, input_item_node_t *const children[], size_t count );
-    void onItemRemoved( MediaSourcePtr mediaSource, input_item_node_t *const children[], size_t count );
+    void onItemCleared( MediaSourcePtr mediaSource, input_item_node_t* node ) override;
+    void onItemAdded( MediaSourcePtr mediaSource, input_item_node_t* parent, input_item_node_t *const children[], size_t count ) override;
+    void onItemRemoved( MediaSourcePtr mediaSource, input_item_node_t *const children[], size_t count ) override;
 
     void refreshMediaList(MediaSourcePtr s, input_item_node_t* const children[], size_t count , bool clear);
-    void refreshDeviceList(MediaSourcePtr mediaSource, input_item_node_t* const children[], size_t count , bool clear);
 
     static bool canBeIndexed(const QUrl& url , ItemType itemType );
 
 private:
-    struct SourceListener
-    {
-        using ListenerPtr = std::unique_ptr<vlc_media_tree_listener_id,
-                                            std::function<void(vlc_media_tree_listener_id*)>>;
-
-        SourceListener( MediaSourcePtr s, MLNetworkModel* m );
-        SourceListener() : source( nullptr ), listener( nullptr ), model( nullptr ){}
-
-        SourceListener( SourceListener&& ) = default;
-        SourceListener& operator=( SourceListener&& ) = default;
-
-        SourceListener( const SourceListener& ) = delete;
-        SourceListener& operator=( const SourceListener& ) = delete;
-
-        static void onItemCleared( vlc_media_tree_t* tree, input_item_node_t* node,
-                                   void* userdata );
-        static void onItemAdded( vlc_media_tree_t *tree, input_item_node_t *node,
-                                 input_item_node_t *const children[], size_t count,
-                                 void *userdata );
-        static void onItemRemoved( vlc_media_tree_t *tree, input_item_node_t *node,
-                                   input_item_node_t *const children[], size_t count,
-                                   void *userdata );
-
-        MediaSourcePtr source;
-        ListenerPtr listener;
-        MLNetworkModel *model;
-    };
-
     //properties of the current node
     QString m_name;
     QUrl m_url;
@@ -203,14 +162,13 @@ private:
     bool m_indexed = false;
     bool m_canBeIndexed  = false;
 
+
     std::vector<Item> m_items;
     QmlMainContext* m_ctx = nullptr;
     vlc_medialibrary_t* m_ml;
     bool m_hasTree = false;
     NetworkTreeItem m_treeItem;
-    bool m_isOnProviderList;
-    QString m_sdSource;
-    std::vector<std::unique_ptr<SourceListener>> m_listeners;
+    std::unique_ptr<MLNetworkSourceListener> m_listener;
 };
 
-#endif // MLNETWORKMODEL_HPP
+#endif // MLNETWORKMEDIAMODEL_HPP
diff --git a/modules/gui/qt/components/mediacenter/mlnetworksourcelistener.cpp b/modules/gui/qt/components/mediacenter/mlnetworksourcelistener.cpp
new file mode 100644
index 0000000000..c7fcbb618d
--- /dev/null
+++ b/modules/gui/qt/components/mediacenter/mlnetworksourcelistener.cpp
@@ -0,0 +1,51 @@
+#include "mlnetworksourcelistener.hpp"
+
+MLNetworkSourceListener::SourceListenerCb::~SourceListenerCb()
+{
+}
+
+MLNetworkSourceListener::MLNetworkSourceListener(MediaSourcePtr s, SourceListenerCb* m)
+    : source( s )
+    , listener( nullptr, [s]( vlc_media_tree_listener_id* l ) {
+            vlc_media_tree_RemoveListener( s->tree, l );
+        } )
+    , cb( m )
+{
+    static const vlc_media_tree_callbacks cbs {
+        &MLNetworkSourceListener::onItemCleared,
+        &MLNetworkSourceListener::onItemAdded,
+        &MLNetworkSourceListener::onItemRemoved
+    };
+    auto l = vlc_media_tree_AddListener( s->tree, &cbs, this, true );
+    if ( l == nullptr )
+        return;
+    listener.reset( l );
+}
+
+MLNetworkSourceListener::MLNetworkSourceListener()
+{
+}
+
+void MLNetworkSourceListener::onItemCleared( vlc_media_tree_t*, input_item_node_t* node,
+                                                    void* userdata)
+{
+    auto* self = static_cast<MLNetworkSourceListener*>( userdata );
+    self->cb->onItemCleared( self->source, node );
+}
+
+void MLNetworkSourceListener::onItemAdded( vlc_media_tree_t *, input_item_node_t * parent,
+                                  input_item_node_t *const children[], size_t count,
+                                  void *userdata )
+{
+    auto* self = static_cast<MLNetworkSourceListener*>( userdata );
+    auto source = self->source;
+    self->cb->onItemAdded( self->source, parent, children, count );
+}
+
+void MLNetworkSourceListener::onItemRemoved( vlc_media_tree_t *, input_item_node_t *,
+                                    input_item_node_t *const children[], size_t count,
+                                    void *userdata )
+{
+    auto* self = static_cast<MLNetworkSourceListener*>( userdata );
+    self->cb->onItemRemoved( self->source, children, count );
+}
diff --git a/modules/gui/qt/components/mediacenter/mlnetworksourcelistener.hpp b/modules/gui/qt/components/mediacenter/mlnetworksourcelistener.hpp
new file mode 100644
index 0000000000..210b91d76a
--- /dev/null
+++ b/modules/gui/qt/components/mediacenter/mlnetworksourcelistener.hpp
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * Copyright (C) 2019 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 MLNETWORKSOURCELISTENER_HPP
+#define MLNETWORKSOURCELISTENER_HPP
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <vlc_media_library.h>
+#include <vlc_media_source.h>
+#include <vlc_threads.h>
+#include <vlc_cxx_helpers.hpp>
+
+#include <components/qml_main_context.hpp>
+
+#include <memory>
+#include <functional>
+
+class MLNetworkSourceListener
+{
+public:
+    using MediaSourcePtr = vlc_shared_data_ptr_type(vlc_media_source_t,
+                                    vlc_media_source_Hold, vlc_media_source_Release);
+
+    using ListenerPtr = std::unique_ptr<vlc_media_tree_listener_id,
+                                        std::function<void(vlc_media_tree_listener_id*)>>;
+
+    class SourceListenerCb
+    {
+    public:
+        virtual ~SourceListenerCb();
+
+        virtual void onItemCleared( MediaSourcePtr mediaSource, input_item_node_t* node ) = 0;
+        virtual void onItemAdded( MediaSourcePtr mediaSource, input_item_node_t* parent, input_item_node_t *const children[], size_t count ) = 0;
+        virtual void onItemRemoved( MediaSourcePtr mediaSource, input_item_node_t *const children[], size_t count ) = 0;
+    };
+
+public:
+    MLNetworkSourceListener( MediaSourcePtr s, SourceListenerCb* m );
+    MLNetworkSourceListener();
+
+    MLNetworkSourceListener( MLNetworkSourceListener&& ) = default;
+    MLNetworkSourceListener& operator=( MLNetworkSourceListener&& ) = default;
+
+    MLNetworkSourceListener( const MLNetworkSourceListener& ) = delete;
+    MLNetworkSourceListener& operator=( const MLNetworkSourceListener& ) = delete;
+
+    static void onItemCleared( vlc_media_tree_t* tree, input_item_node_t* node,
+                               void* userdata );
+    static void onItemAdded( vlc_media_tree_t *tree, input_item_node_t *node,
+                             input_item_node_t *const children[], size_t count,
+                             void *userdata );
+    static void onItemRemoved( vlc_media_tree_t *tree, input_item_node_t *node,
+                               input_item_node_t *const children[], size_t count,
+                               void *userdata );
+
+    MediaSourcePtr source;
+    ListenerPtr listener = nullptr;
+    SourceListenerCb *cb = nullptr;
+};
+#endif // MLNETWORKSOURCELISTENER_HPP
diff --git a/modules/gui/qt/main_interface.cpp b/modules/gui/qt/main_interface.cpp
index 8f87d26c85..0c16f7a18d 100644
--- a/modules/gui/qt/main_interface.cpp
+++ b/modules/gui/qt/main_interface.cpp
@@ -51,7 +51,8 @@
 #include "components/mediacenter/mlgenremodel.hpp"
 #include "components/mediacenter/mlvideomodel.hpp"
 #include "components/mediacenter/mlrecentsvideomodel.hpp"
-#include "components/mediacenter/mlnetworkmodel.hpp"
+#include "components/mediacenter/mlnetworkmediamodel.hpp"
+#include "components/mediacenter/mlnetworkdevicemodel.hpp"
 #include "components/recent_media_model.hpp"
 #include "components/settings.hpp"
 
@@ -346,7 +347,8 @@ void MainInterface::createMainWidget( QSettings * )
         qmlRegisterType<MLVideoModel>( "org.videolan.medialib", 0, 1, "MLVideoModel" );
         qmlRegisterType<MLRecentsVideoModel>( "org.videolan.medialib", 0, 1, "MLRecentsVideoModel" );
         qRegisterMetaType<NetworkTreeItem>();
-        qmlRegisterType<MLNetworkModel>( "org.videolan.medialib", 0, 1, "MLNetworkModel");
+        qmlRegisterType<MLNetworkMediaModel>( "org.videolan.medialib", 0, 1, "MLNetworkMediaModel");
+        qmlRegisterType<MLNetworkDeviceModel>( "org.videolan.medialib", 0, 1, "MLNetworkDeviceModel");
 
         //expose base object, they aren't instanciable from QML side
         qmlRegisterType<MLAlbum>();
diff --git a/modules/gui/qt/qml/mediacenter/MCNetworkBrowseDisplay.qml b/modules/gui/qt/qml/mediacenter/MCNetworkBrowseDisplay.qml
index 7ae6ac4025..c8881c86e7 100644
--- a/modules/gui/qt/qml/mediacenter/MCNetworkBrowseDisplay.qml
+++ b/modules/gui/qt/qml/mediacenter/MCNetworkBrowseDisplay.qml
@@ -39,7 +39,7 @@ Utils.NavigableFocusScope {
         }
     }
 
-    MLNetworkModel {
+    MLNetworkMediaModel {
         id: providerModel
         ctx: mainctx
         tree: undefined
@@ -148,15 +148,15 @@ Utils.NavigableFocusScope {
 
                 image: {
                     switch (model.type){
-                    case MLNetworkModel.TYPE_DISC:
+                    case MLNetworkMediaModel.TYPE_DISC:
                         return  "qrc:///type/disc.svg"
-                    case MLNetworkModel.TYPE_CARD:
+                    case MLNetworkMediaModel.TYPE_CARD:
                         return  "qrc:///type/capture-card.svg"
-                    case MLNetworkModel.TYPE_STREAM:
+                    case MLNetworkMediaModel.TYPE_STREAM:
                         return  "qrc:///type/stream.svg"
-                    case MLNetworkModel.TYPE_PLAYLIST:
+                    case MLNetworkMediaModel.TYPE_PLAYLIST:
                         return  "qrc:///type/playlist.svg"
-                    case MLNetworkModel.TYPE_FILE:
+                    case MLNetworkMediaModel.TYPE_FILE:
                         return  "qrc:///type/file_black.svg"
                     default:
                         return "qrc:///type/directory_black.svg"
@@ -173,7 +173,7 @@ Utils.NavigableFocusScope {
                 }
 
                 onItemDoubleClicked: {
-                    if (model.type === MLNetworkModel.TYPE_NODE || model.type === MLNetworkModel.TYPE_DIRECTORY)
+                    if (model.type === MLNetworkMediaModel.TYPE_NODE || model.type === MLNetworkMediaModel.TYPE_DIRECTORY)
                         history.push( ["mc", "network", { tree: model.tree } ], History.Go)
                     else
                         medialib.addAndPlay( model.mrl )
diff --git a/modules/gui/qt/qml/mediacenter/MCNetworkHomeDisplay.qml b/modules/gui/qt/qml/mediacenter/MCNetworkHomeDisplay.qml
index 4aa07c7703..8ce438b23f 100644
--- a/modules/gui/qt/qml/mediacenter/MCNetworkHomeDisplay.qml
+++ b/modules/gui/qt/qml/mediacenter/MCNetworkHomeDisplay.qml
@@ -39,21 +39,19 @@ Utils.NavigableFocusScope {
 
     MCNetworksSectionSelectableDM{
         id: machineDM
-        model: MLNetworkModel {
+        model: MLNetworkDeviceModel {
             id: machineModel
             ctx: mainctx
-            tree: undefined
-            sd_source: "SD_CAT_DEVICES"
+            sd_source: MLNetworkDeviceModel.CAT_DEVICES
         }
     }
 
     MCNetworksSectionSelectableDM{
         id: lanDM
-        model: MLNetworkModel {
+        model: MLNetworkDeviceModel {
             id: lanModel
             ctx: mainctx
-            tree: undefined
-            sd_source: "SD_CAT_LAN"
+            sd_source: MLNetworkDeviceModel.CAT_LAN
         }
     }
 
diff --git a/modules/gui/qt/qml/mediacenter/MCNetworksSectionSelectableDM.qml b/modules/gui/qt/qml/mediacenter/MCNetworksSectionSelectableDM.qml
index ece2cb227b..72eca97391 100644
--- a/modules/gui/qt/qml/mediacenter/MCNetworksSectionSelectableDM.qml
+++ b/modules/gui/qt/qml/mediacenter/MCNetworksSectionSelectableDM.qml
@@ -45,7 +45,7 @@ Utils.SelectableDelegateModel {
             }
 
             onItemDoubleClicked: {
-                if (model.type === MLNetworkModel.TYPE_NODE || model.type === MLNetworkModel.TYPE_DIRECTORY)
+                if (model.type === MLNetworkMediaModel.TYPE_NODE || model.type === MLNetworkMediaModel.TYPE_DIRECTORY)
                     history.push( ["mc", "network", { tree: model.tree } ], History.Go)
                 else
                     medialib.addAndPlay( model.mrl )
@@ -70,7 +70,7 @@ Utils.SelectableDelegateModel {
             }
 
             onItemDoubleClicked: {
-                if (model.type === MLNetworkModel.TYPE_NODE || model.type === MLNetworkModel.TYPE_DIRECTORY)
+                if (model.type === MLNetworkMediaModel.TYPE_NODE || model.type === MLNetworkMediaModel.TYPE_DIRECTORY)
                     history.push( ["mc", "network", { tree: model.tree } ], History.Go)
                 else
                     medialib.addAndPlay( model.mrl )
@@ -120,8 +120,9 @@ Utils.SelectableDelegateModel {
         if ( delegateModel.selectedGroup.count > 1 ) {
             playSelection()
         } else {
-            if (delegateModel.items.get(index).model.type === MLNetworkModel.TYPE_DIRECTORY
-                    || delegateModel.items.get(index).model.type === MLNetworkModel.TYPE_NODE)  {
+            if (delegateModel.items.get(index).model.type === MLNetworkMediaModel.TYPE_DIRECTORY
+                    || delegateModel.items.get(index).model.type === MLNetworkMediaModel.TYPE_NODE)  {
+                console.log("push network tree", delegateModel.items.get(index).model.tree)
                 history.push(["mc", "network", { tree: delegateModel.items.get(index).model.tree }], History.Go);
             } else {
                 playSelection()
diff --git a/modules/gui/qt/qml/mediacenter/NetworkGridItem.qml b/modules/gui/qt/qml/mediacenter/NetworkGridItem.qml
index a185406d5a..55b1164ded 100644
--- a/modules/gui/qt/qml/mediacenter/NetworkGridItem.qml
+++ b/modules/gui/qt/qml/mediacenter/NetworkGridItem.qml
@@ -32,15 +32,15 @@ Utils.GridItem {
     pictureHeight: VLCStyle.network_normal
     image: {
         switch (model.type){
-        case MLNetworkModel.TYPE_DISC:
+        case MLNetworkMediaModel.TYPE_DISC:
             return  "qrc:///type/disc.svg"
-        case MLNetworkModel.TYPE_CARD:
+        case MLNetworkMediaModel.TYPE_CARD:
             return  "qrc:///type/capture-card.svg"
-        case MLNetworkModel.TYPE_STREAM:
+        case MLNetworkMediaModel.TYPE_STREAM:
             return  "qrc:///type/stream.svg"
-        case MLNetworkModel.TYPE_PLAYLIST:
+        case MLNetworkMediaModel.TYPE_PLAYLIST:
             return  "qrc:///type/playlist.svg"
-        case MLNetworkModel.TYPE_FILE:
+        case MLNetworkMediaModel.TYPE_FILE:
             return  "qrc:///type/file_black.svg"
         default:
             return "qrc:///type/directory_black.svg"
diff --git a/modules/gui/qt/qml/mediacenter/NetworkListItem.qml b/modules/gui/qt/qml/mediacenter/NetworkListItem.qml
index f0ecc2c418..bba0332491 100644
--- a/modules/gui/qt/qml/mediacenter/NetworkListItem.qml
+++ b/modules/gui/qt/qml/mediacenter/NetworkListItem.qml
@@ -40,15 +40,15 @@ Utils.ListItem {
         fillMode: Image.PreserveAspectFit
         source: {
             switch (model.type) {
-            case MLNetworkModel.TYPE_DISC:
+            case MLNetworkMediaModel.TYPE_DISC:
                 return  "qrc:///type/disc.svg"
-            case MLNetworkModel.TYPE_CARD:
+            case MLNetworkMediaModel.TYPE_CARD:
                 return  "qrc:///type/capture-card.svg"
-            case MLNetworkModel.TYPE_STREAM:
+            case MLNetworkMediaModel.TYPE_STREAM:
                 return  "qrc:///type/stream.svg"
-            case MLNetworkModel.TYPE_PLAYLIST:
+            case MLNetworkMediaModel.TYPE_PLAYLIST:
                 return  "qrc:///type/playlist.svg"
-            case MLNetworkModel.TYPE_FILE:
+            case MLNetworkMediaModel.TYPE_FILE:
                 return  "qrc:///type/file_black.svg"
             default:
                 return "qrc:///type/directory_black.svg"
@@ -57,7 +57,7 @@ Utils.ListItem {
     }
     line1: model.name || qsTr("Unknown share")
     line2: model.mrl
-    imageText: (model.type !== MLNetworkModel.TYPE_DIRECTORY && model.type !== MLNetworkModel.TYPE_NODE) ? model.protocol : ""
+    imageText: (model.type !== MLNetworkMediaModel.TYPE_DIRECTORY && model.type !== MLNetworkMediaModel.TYPE_NODE) ? model.protocol : ""
 
     showContextButton: true
 
-- 
2.17.1



More information about the vlc-devel mailing list