[vlc-devel] [PATCH 08/13] qt: medialib: implement separate data loaders

Romain Vimont rom1v at videolabs.io
Mon Nov 23 17:30:22 CET 2020


When the database queries are executed asynchronously, we must still be
able to delete a list model from the main thread without blocking. As a
consequence, the data loader must be able to outlive the list model
instance; this implies that loading data must not involve calling
methods on the list model itself (fetch() and countTotalElements()).

Nevertheless, the actual data to load depends on the list model (the
content is not the same between a list of videos and a list of albums).

To be able to detach the lifetime of the data loader from the one of the
list model, make the list models provide loader instances on demand,
instead of implementing the loading methods directly.
---
 modules/gui/qt/medialibrary/mlalbummodel.cpp  | 50 +++++++------
 modules/gui/qt/medialibrary/mlalbummodel.hpp  | 12 ++-
 .../gui/qt/medialibrary/mlalbumtrackmodel.cpp | 64 +++++++++-------
 .../gui/qt/medialibrary/mlalbumtrackmodel.hpp | 12 ++-
 modules/gui/qt/medialibrary/mlartistmodel.cpp | 63 +++++++++-------
 modules/gui/qt/medialibrary/mlartistmodel.hpp | 12 ++-
 modules/gui/qt/medialibrary/mlbasemodel.hpp   | 57 +++++++-------
 modules/gui/qt/medialibrary/mlgenremodel.cpp  | 54 +++++++------
 modules/gui/qt/medialibrary/mlgenremodel.hpp  | 12 ++-
 .../gui/qt/medialibrary/mlrecentsmodel.cpp    | 74 ++++++++++--------
 .../gui/qt/medialibrary/mlrecentsmodel.hpp    | 20 ++++-
 .../qt/medialibrary/mlrecentsvideomodel.cpp   | 75 +++++++++++--------
 .../qt/medialibrary/mlrecentsvideomodel.hpp   | 24 +++++-
 modules/gui/qt/medialibrary/mlurlmodel.cpp    | 55 ++++++++------
 modules/gui/qt/medialibrary/mlurlmodel.hpp    | 12 ++-
 modules/gui/qt/medialibrary/mlvideomodel.cpp  | 51 +++++++------
 modules/gui/qt/medialibrary/mlvideomodel.hpp  | 12 ++-
 17 files changed, 404 insertions(+), 255 deletions(-)

diff --git a/modules/gui/qt/medialibrary/mlalbummodel.cpp b/modules/gui/qt/medialibrary/mlalbummodel.cpp
index 6af6079975..23bb3e74d4 100644
--- a/modules/gui/qt/medialibrary/mlalbummodel.cpp
+++ b/modules/gui/qt/medialibrary/mlalbummodel.cpp
@@ -88,23 +88,6 @@ QHash<int, QByteArray> MLAlbumModel::roleNames() const
     };
 }
 
-std::vector<std::unique_ptr<MLAlbum>> MLAlbumModel::fetch(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-
-    ml_unique_ptr<vlc_ml_album_list_t> album_list;
-    if ( m_parent.id <= 0 )
-        album_list.reset( vlc_ml_list_albums(m_ml, &queryParams) );
-    else
-        album_list.reset( vlc_ml_list_albums_of(m_ml, &queryParams, m_parent.type, m_parent.id ) );
-    if ( album_list == nullptr )
-        return {};
-    std::vector<std::unique_ptr<MLAlbum>> res;
-    for( const vlc_ml_album_t& album: ml_range_iterate<vlc_ml_album_t>( album_list ) )
-        res.emplace_back( std::make_unique<MLAlbum>( m_ml, &album ) );
-    return res;
-}
-
 vlc_ml_sorting_criteria_t MLAlbumModel::nameToCriteria(QByteArray name) const
 {
     return M_names_to_criteria.value(name, VLC_ML_SORTING_DEFAULT);
@@ -163,12 +146,37 @@ vlc_ml_sorting_criteria_t MLAlbumModel::roleToCriteria(int role) const
     }
 }
 
-size_t MLAlbumModel::countTotalElements(const MLQueryParams &params) const
+ListCacheLoader<std::unique_ptr<MLAlbum>> *
+MLAlbumModel::createLoader() const
+{
+    return new Loader(*this);
+}
+
+size_t MLAlbumModel::Loader::count() const
 {
-    vlc_ml_query_params_t queryParams = params.toCQueryParams();
-    queryParams.i_offset = 0;
-    queryParams.i_nbResults = 0;
+    MLQueryParams params = getParams();
+    auto queryParams = params.toCQueryParams();
+
     if ( m_parent.id <= 0 )
         return vlc_ml_count_albums(m_ml, &queryParams);
     return vlc_ml_count_albums_of(m_ml, &queryParams, m_parent.type, m_parent.id);
 }
+
+std::vector<std::unique_ptr<MLAlbum>>
+MLAlbumModel::Loader::load(size_t index, size_t count) const
+{
+    MLQueryParams params = getParams(index, count);
+    auto queryParams = params.toCQueryParams();
+
+    ml_unique_ptr<vlc_ml_album_list_t> album_list;
+    if ( m_parent.id <= 0 )
+        album_list.reset( vlc_ml_list_albums(m_ml, &queryParams) );
+    else
+        album_list.reset( vlc_ml_list_albums_of(m_ml, &queryParams, m_parent.type, m_parent.id ) );
+    if ( album_list == nullptr )
+        return {};
+    std::vector<std::unique_ptr<MLAlbum>> res;
+    for( const vlc_ml_album_t& album: ml_range_iterate<vlc_ml_album_t>( album_list ) )
+        res.emplace_back( std::make_unique<MLAlbum>( m_ml, &album ) );
+    return res;
+}
diff --git a/modules/gui/qt/medialibrary/mlalbummodel.hpp b/modules/gui/qt/medialibrary/mlalbummodel.hpp
index 3aba91def7..9f57d77b78 100644
--- a/modules/gui/qt/medialibrary/mlalbummodel.hpp
+++ b/modules/gui/qt/medialibrary/mlalbummodel.hpp
@@ -57,9 +57,10 @@ public:
     Q_INVOKABLE QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
     Q_INVOKABLE QHash<int, QByteArray> roleNames() const override;
 
+protected:
+    ListCacheLoader<std::unique_ptr<MLAlbum>> *createLoader() const override;
+
 private:
-    std::vector<std::unique_ptr<MLAlbum>> fetch(const MLQueryParams &params) const override;
-    size_t countTotalElements(const MLQueryParams &params) const override;
     vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
     vlc_ml_sorting_criteria_t nameToCriteria(QByteArray name) const override;
     QByteArray criteriaToName(vlc_ml_sorting_criteria_t criteria) const override;
@@ -67,6 +68,13 @@ private:
     void thumbnailUpdated(int idx) override;
 
     static  QHash<QByteArray, vlc_ml_sorting_criteria_t> M_names_to_criteria;
+
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLAlbumModel &model) : BaseLoader(model) {}
+        size_t count() const override;
+        std::vector<std::unique_ptr<MLAlbum>> load(size_t index, size_t count) const override;
+    };
 };
 
 
diff --git a/modules/gui/qt/medialibrary/mlalbumtrackmodel.cpp b/modules/gui/qt/medialibrary/mlalbumtrackmodel.cpp
index e40d069884..fab64930f8 100644
--- a/modules/gui/qt/medialibrary/mlalbumtrackmodel.cpp
+++ b/modules/gui/qt/medialibrary/mlalbumtrackmodel.cpp
@@ -93,34 +93,6 @@ QHash<int, QByteArray> MLAlbumTrackModel::roleNames() const
     };
 }
 
-size_t MLAlbumTrackModel::countTotalElements(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    queryParams.i_offset = 0;
-    queryParams.i_nbResults = 0;
-    if ( m_parent.id <= 0 )
-        return vlc_ml_count_audio_media(m_ml, &queryParams);
-    return vlc_ml_count_media_of(m_ml, &queryParams, m_parent.type, m_parent.id );
-}
-
-std::vector<std::unique_ptr<MLAlbumTrack>> MLAlbumTrackModel::fetch(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-
-    ml_unique_ptr<vlc_ml_media_list_t> media_list;
-
-    if ( m_parent.id <= 0 )
-        media_list.reset( vlc_ml_list_audio_media(m_ml, &queryParams) );
-    else
-        media_list.reset( vlc_ml_list_media_of(m_ml, &queryParams, m_parent.type, m_parent.id ) );
-    if ( media_list == nullptr )
-        return {};
-    std::vector<std::unique_ptr<MLAlbumTrack>> res;
-    for( const vlc_ml_media_t& media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
-        res.emplace_back( std::make_unique<MLAlbumTrack>( m_ml, &media ) );
-    return res;
-}
-
 vlc_ml_sorting_criteria_t MLAlbumTrackModel::roleToCriteria(int role) const
 {
     switch (role) {
@@ -178,3 +150,39 @@ void MLAlbumTrackModel::onVlcMlEvent(const MLEvent &event)
     }
     MLBaseModel::onVlcMlEvent( event );
 }
+
+ListCacheLoader<std::unique_ptr<MLAlbumTrack>> *
+MLAlbumTrackModel::createLoader() const
+{
+    return new Loader(*this);
+}
+
+size_t MLAlbumTrackModel::Loader::count() const
+{
+    MLQueryParams params = getParams();
+    auto queryParams = params.toCQueryParams();
+
+    if ( m_parent.id <= 0 )
+        return vlc_ml_count_audio_media(m_ml, &queryParams);
+    return vlc_ml_count_media_of(m_ml, &queryParams, m_parent.type, m_parent.id );
+}
+
+std::vector<std::unique_ptr<MLAlbumTrack>>
+MLAlbumTrackModel::Loader::load(size_t index, size_t count) const
+{
+    MLQueryParams params = getParams(index, count);
+    auto queryParams = params.toCQueryParams();
+
+    ml_unique_ptr<vlc_ml_media_list_t> media_list;
+
+    if ( m_parent.id <= 0 )
+        media_list.reset( vlc_ml_list_audio_media(m_ml, &queryParams) );
+    else
+        media_list.reset( vlc_ml_list_media_of(m_ml, &queryParams, m_parent.type, m_parent.id ) );
+    if ( media_list == nullptr )
+        return {};
+    std::vector<std::unique_ptr<MLAlbumTrack>> res;
+    for( const vlc_ml_media_t& media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
+        res.emplace_back( std::make_unique<MLAlbumTrack>( m_ml, &media ) );
+    return res;
+}
diff --git a/modules/gui/qt/medialibrary/mlalbumtrackmodel.hpp b/modules/gui/qt/medialibrary/mlalbumtrackmodel.hpp
index 6fe867c319..e494b597cb 100644
--- a/modules/gui/qt/medialibrary/mlalbumtrackmodel.hpp
+++ b/modules/gui/qt/medialibrary/mlalbumtrackmodel.hpp
@@ -57,14 +57,22 @@ public:
     QVariant data(const QModelIndex &index, int role) const override;
     QHash<int, QByteArray> roleNames() const override;
 
+protected:
+    ListCacheLoader<std::unique_ptr<MLAlbumTrack>> *createLoader() const override;
+
 private:
-    std::vector<std::unique_ptr<MLAlbumTrack>> fetch(const MLQueryParams &params) const override;
-    size_t countTotalElements(const MLQueryParams &params) const override;
     vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
     vlc_ml_sorting_criteria_t nameToCriteria(QByteArray name) const override;
     QByteArray criteriaToName(vlc_ml_sorting_criteria_t criteria) const override;
     virtual void onVlcMlEvent( const MLEvent &event ) override;
 
     static QHash<QByteArray, vlc_ml_sorting_criteria_t> M_names_to_criteria;
+
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLAlbumTrackModel &model) : BaseLoader(model) {}
+        size_t count() const override;
+        std::vector<std::unique_ptr<MLAlbumTrack>> load(size_t index, size_t count) const override;
+    };
 };
 #endif // MLTRACKMODEL_HPP
diff --git a/modules/gui/qt/medialibrary/mlartistmodel.cpp b/modules/gui/qt/medialibrary/mlartistmodel.cpp
index e264120cee..7422709096 100644
--- a/modules/gui/qt/medialibrary/mlartistmodel.cpp
+++ b/modules/gui/qt/medialibrary/mlartistmodel.cpp
@@ -67,34 +67,6 @@ QHash<int, QByteArray> MLArtistModel::roleNames() const
     };
 }
 
-std::vector<std::unique_ptr<MLArtist>> MLArtistModel::fetch(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-
-    ml_unique_ptr<vlc_ml_artist_list_t> artist_list;
-    if ( m_parent.id <= 0 )
-        artist_list.reset( vlc_ml_list_artists(m_ml, &queryParams, false) );
-    else
-        artist_list.reset( vlc_ml_list_artist_of(m_ml, &queryParams, m_parent.type, m_parent.id) );
-    if ( artist_list == nullptr )
-        return {};
-    std::vector<std::unique_ptr<MLArtist>> res;
-    for( const vlc_ml_artist_t& artist: ml_range_iterate<vlc_ml_artist_t>( artist_list ) )
-        res.emplace_back( std::make_unique<MLArtist>( &artist ) );
-    return res;
-}
-
-size_t MLArtistModel::countTotalElements(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    queryParams.i_offset = 0;
-    queryParams.i_nbResults = 0;
-
-    if ( m_parent.id <= 0 )
-        return vlc_ml_count_artists(m_ml, &queryParams, false);
-    return vlc_ml_count_artists_of(m_ml, &queryParams, m_parent.type, m_parent.id );
-}
-
 vlc_ml_sorting_criteria_t MLArtistModel::roleToCriteria(int role) const
 {
     switch (role)
@@ -138,3 +110,38 @@ void MLArtistModel::thumbnailUpdated(int idx)
 {
     emit dataChanged(index(idx), index(idx), {ARTIST_COVER});
 }
+
+ListCacheLoader<std::unique_ptr<MLArtist>> *
+MLArtistModel::createLoader() const
+{
+    return new Loader(*this);
+}
+
+size_t MLArtistModel::Loader::count() const
+{
+    MLQueryParams params = getParams();
+    auto queryParams = params.toCQueryParams();
+
+    if ( m_parent.id <= 0 )
+        return vlc_ml_count_artists(m_ml, &queryParams, false);
+    return vlc_ml_count_artists_of(m_ml, &queryParams, m_parent.type, m_parent.id );
+}
+
+std::vector<std::unique_ptr<MLArtist>>
+MLArtistModel::Loader::load(size_t index, size_t count) const
+{
+    MLQueryParams params = getParams(index, count);
+    auto queryParams = params.toCQueryParams();
+
+    ml_unique_ptr<vlc_ml_artist_list_t> artist_list;
+    if ( m_parent.id <= 0 )
+        artist_list.reset( vlc_ml_list_artists(m_ml, &queryParams, false) );
+    else
+        artist_list.reset( vlc_ml_list_artist_of(m_ml, &queryParams, m_parent.type, m_parent.id) );
+    if ( artist_list == nullptr )
+        return {};
+    std::vector<std::unique_ptr<MLArtist>> res;
+    for( const vlc_ml_artist_t& artist: ml_range_iterate<vlc_ml_artist_t>( artist_list ) )
+        res.emplace_back( std::make_unique<MLArtist>( &artist ) );
+    return res;
+}
diff --git a/modules/gui/qt/medialibrary/mlartistmodel.hpp b/modules/gui/qt/medialibrary/mlartistmodel.hpp
index d0fe4d1baa..8926731b87 100644
--- a/modules/gui/qt/medialibrary/mlartistmodel.hpp
+++ b/modules/gui/qt/medialibrary/mlartistmodel.hpp
@@ -48,9 +48,10 @@ public:
     QVariant data(const QModelIndex &index, int role) const override;
     QHash<int, QByteArray> roleNames() const override;
 
+protected:
+    ListCacheLoader<std::unique_ptr<MLArtist>> *createLoader() const override;
+
 private:
-    std::vector<std::unique_ptr<MLArtist>> fetch(const MLQueryParams &params) const override;
-    size_t countTotalElements(const MLQueryParams &params) const override;
     vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
     vlc_ml_sorting_criteria_t nameToCriteria(QByteArray name) const override;
     QByteArray criteriaToName(vlc_ml_sorting_criteria_t criteria) const override;
@@ -58,6 +59,13 @@ private:
     void thumbnailUpdated(int idx) override;
 
     static QHash<QByteArray, vlc_ml_sorting_criteria_t> M_names_to_criteria;
+
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLArtistModel &model) : BaseLoader(model) {}
+        size_t count() const override;
+        std::vector<std::unique_ptr<MLArtist>> load(size_t index, size_t count) const override;
+    };
 };
 
 #endif // MLARTISTMODEL_HPP
diff --git a/modules/gui/qt/medialibrary/mlbasemodel.hpp b/modules/gui/qt/medialibrary/mlbasemodel.hpp
index 00911c5547..fdd98f5626 100644
--- a/modules/gui/qt/medialibrary/mlbasemodel.hpp
+++ b/modules/gui/qt/medialibrary/mlbasemodel.hpp
@@ -216,7 +216,7 @@ public:
         if (m_cache)
             return;
 
-        auto loader = new Loader(*this);
+        auto loader = createLoader();
         m_cache.reset(new ListCache<std::unique_ptr<T>>(loader));
         connect(&*m_cache, &ListCacheBase::localDataChanged,
                 this, &MLBaseModel::onLocalDataChanged);
@@ -226,6 +226,8 @@ public:
     }
 
 protected:
+    virtual ListCacheLoader<std::unique_ptr<T>> *createLoader() const = 0;
+
     void invalidateCache()
     {
         m_cache.reset();
@@ -287,48 +289,41 @@ protected:
         MLBaseModel::onVlcMlEvent( event );
     }
 
-private:
-    virtual size_t countTotalElements(const MLQueryParams &params) const = 0;
-    virtual std::vector<std::unique_ptr<T>> fetch(const MLQueryParams &params) const = 0;
-    virtual void thumbnailUpdated( int ) {}
-
     /* Data loader for the cache */
-    struct Loader : public ListCacheLoader<std::unique_ptr<T>>
+    struct BaseLoader : public ListCacheLoader<std::unique_ptr<T>>
     {
-        Loader(const MLSlidingWindowModel &model)
-            : m_model(model)
-            , m_searchPattern(model.m_search_pattern)
-            , m_sort(model.m_sort)
-            , m_sort_desc(model.m_sort_desc)
+        BaseLoader(vlc_medialibrary_t *ml, MLParentId parent, QString searchPattern,
+                   vlc_ml_sorting_criteria_t sort, bool sort_desc)
+            : m_ml(ml)
+            , m_parent(parent)
+            , m_searchPattern(searchPattern)
+            , m_sort(sort)
+            , m_sort_desc(sort_desc)
         {
         }
 
-        size_t count() const override;
-        std::vector<std::unique_ptr<T>> load(size_t index, size_t count) const override;
+        BaseLoader(const MLSlidingWindowModel<T> &model)
+            : BaseLoader(model.m_ml, model.m_parent, model.m_search_pattern, model.m_sort, model.m_sort_desc)
+        {
+        }
+
+        MLQueryParams getParams(size_t index = 0, size_t count = 0) const
+        {
+            return { m_searchPattern.toUtf8(), m_sort, m_sort_desc, index, count };
+        }
 
-    private:
-        const MLSlidingWindowModel &m_model;
+    protected:
+        vlc_medialibrary_t *m_ml;
+        MLParentId m_parent;
         QString m_searchPattern;
         vlc_ml_sorting_criteria_t m_sort;
         bool m_sort_desc;
     };
 
+private:
+    virtual void thumbnailUpdated( int ) {}
+
     mutable std::unique_ptr<ListCache<std::unique_ptr<T>>> m_cache;
 };
 
-template <typename T>
-size_t MLSlidingWindowModel<T>::Loader::count() const
-{
-    MLQueryParams params{ m_searchPattern.toUtf8(), m_sort, m_sort_desc };
-    return m_model.countTotalElements(params);
-}
-
-template <typename T>
-std::vector<std::unique_ptr<T>>
-MLSlidingWindowModel<T>::Loader::load(size_t index, size_t count) const
-{
-    MLQueryParams params{ m_searchPattern.toUtf8(), m_sort, m_sort_desc, index, count };
-    return m_model.fetch(params);
-}
-
 #endif // MLBASEMODEL_HPP
diff --git a/modules/gui/qt/medialibrary/mlgenremodel.cpp b/modules/gui/qt/medialibrary/mlgenremodel.cpp
index 9ae0186761..b9c562a5f8 100644
--- a/modules/gui/qt/medialibrary/mlgenremodel.cpp
+++ b/modules/gui/qt/medialibrary/mlgenremodel.cpp
@@ -68,28 +68,6 @@ QHash<int, QByteArray> MLGenreModel::roleNames() const
     };
 }
 
-std::vector<std::unique_ptr<MLGenre>> MLGenreModel::fetch(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    ml_unique_ptr<vlc_ml_genre_list_t> genre_list(
-        vlc_ml_list_genres(m_ml, &queryParams)
-    );
-    if ( genre_list == nullptr )
-        return {};
-    std::vector<std::unique_ptr<MLGenre>> res;
-    for( const vlc_ml_genre_t& genre: ml_range_iterate<vlc_ml_genre_t>( genre_list ) )
-        res.emplace_back( std::make_unique<MLGenre>( m_ml, &genre ) );
-    return res;
-}
-
-size_t MLGenreModel::countTotalElements(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    queryParams.i_offset = 0;
-    queryParams.i_nbResults = 0;
-    return vlc_ml_count_genres( m_ml, &queryParams );
-}
-
 void MLGenreModel::onVlcMlEvent(const MLEvent &event)
 {
     switch (event.i_type)
@@ -123,3 +101,35 @@ vlc_ml_sorting_criteria_t MLGenreModel::nameToCriteria(QByteArray name) const
 {
     return M_names_to_criteria.value(name, VLC_ML_SORTING_DEFAULT);
 }
+
+ListCacheLoader<std::unique_ptr<MLGenre>> *
+MLGenreModel::createLoader() const
+{
+    return new Loader(*this);
+}
+
+size_t MLGenreModel::Loader::count() const
+{
+    MLQueryParams params = getParams();
+    auto queryParams = params.toCQueryParams();
+
+    return vlc_ml_count_genres( m_ml, &queryParams );
+}
+
+std::vector<std::unique_ptr<MLGenre>>
+MLGenreModel::Loader::load(size_t index, size_t count) const
+{
+    MLQueryParams params = getParams(index, count);
+    auto queryParams = params.toCQueryParams();
+
+    ml_unique_ptr<vlc_ml_genre_list_t> genre_list(
+        vlc_ml_list_genres(m_ml, &queryParams)
+    );
+    if ( genre_list == nullptr )
+        return {};
+    std::vector<std::unique_ptr<MLGenre>> res;
+    for( const vlc_ml_genre_t& genre: ml_range_iterate<vlc_ml_genre_t>( genre_list ) )
+        res.emplace_back( std::make_unique<MLGenre>( m_ml, &genre ) );
+    return res;
+
+}
diff --git a/modules/gui/qt/medialibrary/mlgenremodel.hpp b/modules/gui/qt/medialibrary/mlgenremodel.hpp
index 33dc8c89db..10e0ae8daa 100644
--- a/modules/gui/qt/medialibrary/mlgenremodel.hpp
+++ b/modules/gui/qt/medialibrary/mlgenremodel.hpp
@@ -51,9 +51,10 @@ public:
     QHash<int, QByteArray> roleNames() const override;
     QVariant data(const QModelIndex &index, int role) const override;
 
+protected:
+    ListCacheLoader<std::unique_ptr<MLGenre>> *createLoader() const override;
+
 private:
-    std::vector<std::unique_ptr<MLGenre>> fetch(const MLQueryParams &params) const override;
-    size_t countTotalElements(const MLQueryParams &params) const override;
     void onVlcMlEvent(const MLEvent &event) override;
     void thumbnailUpdated(int idx) override;
     vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
@@ -61,6 +62,13 @@ private:
 
 
     static QHash<QByteArray, vlc_ml_sorting_criteria_t> M_names_to_criteria;
+
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLGenreModel &model) : BaseLoader(model) {}
+        size_t count() const override;
+        std::vector<std::unique_ptr<MLGenre>> load(size_t index, size_t count) const override;
+    };
 };
 
 
diff --git a/modules/gui/qt/medialibrary/mlrecentsmodel.cpp b/modules/gui/qt/medialibrary/mlrecentsmodel.cpp
index 8bd538d20c..7d4b5cc7e2 100644
--- a/modules/gui/qt/medialibrary/mlrecentsmodel.cpp
+++ b/modules/gui/qt/medialibrary/mlrecentsmodel.cpp
@@ -79,38 +79,6 @@ void MLRecentsModel::clearHistory()
     vlc_ml_clear_history(m_ml);
 }
 
-std::vector<std::unique_ptr<MLRecentMedia> > MLRecentsModel::fetch(const MLQueryParams &params) const
-{
-    std::vector<std::unique_ptr<MLRecentMedia>> res;
-    auto queryParams = params.toCQueryParams();
-    if (m_numberOfItemsToShow >= 0)
-    {
-        if (queryParams.i_offset <= static_cast<uint32_t>(m_numberOfItemsToShow))
-           queryParams.i_nbResults = static_cast<uint32_t>(m_numberOfItemsToShow) - queryParams.i_offset;
-        else
-            return res;
-    }
-
-    ml_unique_ptr<vlc_ml_media_list_t> media_list{ vlc_ml_list_history(
-                m_ml, &queryParams ) };
-    if ( media_list == nullptr )
-        return {};
-    for( vlc_ml_media_t &media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
-        res.emplace_back( std::make_unique<MLRecentMedia>( &media ) );
-    return res;
-}
-
-size_t MLRecentsModel::countTotalElements(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    queryParams.i_offset = 0;
-    queryParams.i_nbResults = m_numberOfItemsToShow;
-    size_t realCount = vlc_ml_count_history( m_ml, &queryParams );
-    if (m_numberOfItemsToShow >= 0)
-        return std::min( realCount, static_cast<size_t>(m_numberOfItemsToShow) );
-    return realCount;
-}
-
 void MLRecentsModel::onVlcMlEvent( const MLEvent &event )
 {
     switch ( event.i_type )
@@ -127,7 +95,49 @@ void MLRecentsModel::onVlcMlEvent( const MLEvent &event )
 }
 void MLRecentsModel::setNumberOfItemsToShow( int n ){
     m_numberOfItemsToShow = n;
+    invalidateCache();
 }
 int MLRecentsModel::getNumberOfItemsToShow() const {
     return m_numberOfItemsToShow;
 }
+
+ListCacheLoader<std::unique_ptr<MLRecentMedia>> *
+MLRecentsModel::createLoader() const
+{
+    return new Loader(*this, m_numberOfItemsToShow);
+}
+
+size_t MLRecentsModel::Loader::count() const
+{
+    MLQueryParams params = getParams();
+    auto queryParams = params.toCQueryParams();
+
+    size_t realCount = vlc_ml_count_history( m_ml, &queryParams );
+    if (m_numberOfItemsToShow >= 0)
+        return std::min( realCount, static_cast<size_t>(m_numberOfItemsToShow) );
+    return realCount;
+}
+
+std::vector<std::unique_ptr<MLRecentMedia>>
+MLRecentsModel::Loader::load(size_t index, size_t count) const
+{
+    MLQueryParams params = getParams(index, count);
+    auto queryParams = params.toCQueryParams();
+
+    std::vector<std::unique_ptr<MLRecentMedia>> res;
+    if (m_numberOfItemsToShow >= 0)
+    {
+        if (queryParams.i_offset <= static_cast<uint32_t>(m_numberOfItemsToShow))
+           queryParams.i_nbResults = static_cast<uint32_t>(m_numberOfItemsToShow) - queryParams.i_offset;
+        else
+            return res;
+    }
+
+    ml_unique_ptr<vlc_ml_media_list_t> media_list{ vlc_ml_list_history(
+                m_ml, &queryParams ) };
+    if ( media_list == nullptr )
+        return {};
+    for( vlc_ml_media_t &media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
+        res.emplace_back( std::make_unique<MLRecentMedia>( &media ) );
+    return res;
+}
diff --git a/modules/gui/qt/medialibrary/mlrecentsmodel.hpp b/modules/gui/qt/medialibrary/mlrecentsmodel.hpp
index 1cd2e17abe..b58ebea042 100644
--- a/modules/gui/qt/medialibrary/mlrecentsmodel.hpp
+++ b/modules/gui/qt/medialibrary/mlrecentsmodel.hpp
@@ -75,9 +75,10 @@ public:
     void setNumberOfItemsToShow(int);
     int getNumberOfItemsToShow() const;
 
+protected:
+    ListCacheLoader<std::unique_ptr<MLRecentMedia>> *createLoader() const override;
+
 private:
-    std::vector<std::unique_ptr<MLRecentMedia>> fetch(const MLQueryParams &params) const override;
-    size_t countTotalElements(const MLQueryParams &params) const override;
     vlc_ml_sorting_criteria_t roleToCriteria( int /* role */ ) const override{
         return VLC_ML_SORTING_DEFAULT;
     }
@@ -85,6 +86,21 @@ private:
         return VLC_ML_SORTING_DEFAULT;
     }
     virtual void onVlcMlEvent( const MLEvent &event ) override;
+
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLRecentsModel &model, int numberOfItemsToShow)
+            : BaseLoader(model)
+            , m_numberOfItemsToShow(numberOfItemsToShow)
+        {
+        }
+
+        size_t count() const override;
+        std::vector<std::unique_ptr<MLRecentMedia>> load(size_t index, size_t count) const override;
+
+    private:
+        int m_numberOfItemsToShow;
+    };
 };
 
 #endif // ML_RECENTS_MODEL_H
diff --git a/modules/gui/qt/medialibrary/mlrecentsvideomodel.cpp b/modules/gui/qt/medialibrary/mlrecentsvideomodel.cpp
index 643c351515..8324affa33 100644
--- a/modules/gui/qt/medialibrary/mlrecentsvideomodel.cpp
+++ b/modules/gui/qt/medialibrary/mlrecentsvideomodel.cpp
@@ -92,37 +92,6 @@ QHash<int, QByteArray> MLRecentsVideoModel::roleNames() const
     };
 }
 
-std::vector<std::unique_ptr<MLVideo> > MLRecentsVideoModel::fetch(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    ml_unique_ptr<vlc_ml_media_list_t> media_list{ vlc_ml_list_history(
-                m_ml, &queryParams ) };
-    if ( media_list == nullptr )
-        return {};
-    std::vector<std::unique_ptr<MLVideo>> res;
-    m_video_count = 0;
-    for( vlc_ml_media_t &media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
-        if( media.i_type == VLC_ML_MEDIA_TYPE_VIDEO )
-        {
-            m_video_count++;
-            res.emplace_back( std::make_unique<MLVideo>( m_ml, &media ) );
-        }
-    return res;
-}
-
-size_t MLRecentsVideoModel::countTotalElements(const MLQueryParams &params) const
-{
-    // FIXME: countTotalElements() may not depend on fetch(), since the call to
-    // fetch() depends on countTotalElements()
-
-    (void) params;
-
-    if(numberOfItemsToShow == -1){
-        return m_video_count;
-    }
-    return std::min(m_video_count,numberOfItemsToShow);
-}
-
 void MLRecentsVideoModel::onVlcMlEvent( const MLEvent &event )
 {
     switch ( event.i_type )
@@ -139,7 +108,51 @@ void MLRecentsVideoModel::onVlcMlEvent( const MLEvent &event )
 }
 void MLRecentsVideoModel::setNumberOfItemsToShow( int n ){
     numberOfItemsToShow = n;
+    invalidateCache();
 }
 int MLRecentsVideoModel::getNumberOfItemsToShow(){
     return numberOfItemsToShow;
 }
+
+ListCacheLoader<std::unique_ptr<MLVideo>> *
+MLRecentsVideoModel::createLoader() const
+{
+    return new Loader(*this, numberOfItemsToShow);
+}
+
+size_t MLRecentsVideoModel::Loader::count() const
+{
+    MLQueryParams params = getParams();
+    auto queryParams = params.toCQueryParams();
+
+    // FIXME: count() may not depend on load(), since the call to load()
+    // depends on count()
+
+    (void) queryParams;
+
+    if (m_numberOfItemsToShow == -1) {
+        return m_video_count;
+    }
+    return std::min(m_video_count, m_numberOfItemsToShow);
+}
+
+std::vector<std::unique_ptr<MLVideo>>
+MLRecentsVideoModel::Loader::load(size_t index, size_t count) const
+{
+    MLQueryParams params = getParams(index, count);
+    auto queryParams = params.toCQueryParams();
+
+    ml_unique_ptr<vlc_ml_media_list_t> media_list{ vlc_ml_list_history(
+                m_ml, &queryParams ) };
+    if ( media_list == nullptr )
+        return {};
+    std::vector<std::unique_ptr<MLVideo>> res;
+    m_video_count = 0;
+    for( vlc_ml_media_t &media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
+        if( media.i_type == VLC_ML_MEDIA_TYPE_VIDEO )
+        {
+            m_video_count++;
+            res.emplace_back( std::make_unique<MLVideo>( m_ml, &media ) );
+        }
+    return res;
+}
diff --git a/modules/gui/qt/medialibrary/mlrecentsvideomodel.hpp b/modules/gui/qt/medialibrary/mlrecentsvideomodel.hpp
index 9a68459d42..64525ec0aa 100644
--- a/modules/gui/qt/medialibrary/mlrecentsvideomodel.hpp
+++ b/modules/gui/qt/medialibrary/mlrecentsvideomodel.hpp
@@ -44,9 +44,10 @@ public:
     QHash<int, QByteArray> roleNames() const override;
     int numberOfItemsToShow = 10;
 
+protected:
+    ListCacheLoader<std::unique_ptr<MLVideo>> *createLoader() const override;
+
 private:
-    std::vector<std::unique_ptr<MLVideo>> fetch(const MLQueryParams &params) const override;
-    size_t countTotalElements(const MLQueryParams &params) const override;
     vlc_ml_sorting_criteria_t roleToCriteria( int /* role */ ) const override{
         return VLC_ML_SORTING_DEFAULT;
     }
@@ -56,7 +57,24 @@ private:
     virtual void onVlcMlEvent( const MLEvent &event ) override;
     void setNumberOfItemsToShow(int);
     int getNumberOfItemsToShow();
-    mutable int m_video_count;
+
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLRecentsVideoModel &model, int numberOfItemsToShow)
+            : BaseLoader(model)
+            , m_numberOfItemsToShow(numberOfItemsToShow)
+        {
+        }
+
+        size_t count() const override;
+        std::vector<std::unique_ptr<MLVideo>> load(size_t index, size_t count) const override;
+
+    private:
+        int m_numberOfItemsToShow;
+        // FIXME: count() may not depend on load(), since the call to load()
+        // depends on count()
+        mutable int m_video_count;
+    };
 };
 
 #endif // ML_RECENTS_VIDEO_MODEL_H
diff --git a/modules/gui/qt/medialibrary/mlurlmodel.cpp b/modules/gui/qt/medialibrary/mlurlmodel.cpp
index eb2f219d4b..662842ffa6 100644
--- a/modules/gui/qt/medialibrary/mlurlmodel.cpp
+++ b/modules/gui/qt/medialibrary/mlurlmodel.cpp
@@ -72,30 +72,6 @@ void MLUrlModel::addAndPlay( const QString &url )
     });
 }
 
-size_t MLUrlModel::countTotalElements(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    queryParams.i_offset = 0;
-    queryParams.i_nbResults = 0;
-    auto s = vlc_ml_count_stream_history( m_ml, &queryParams );
-    return s;
-}
-
-std::vector<std::unique_ptr<MLUrl>> MLUrlModel::fetch(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-
-    ml_unique_ptr<vlc_ml_media_list_t> media_list;
-    media_list.reset( vlc_ml_list_stream_history(m_ml, &queryParams) );
-    if ( media_list == nullptr )
-        return {};
-
-    std::vector<std::unique_ptr<MLUrl>> res;
-    for( const vlc_ml_media_t& media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
-        res.emplace_back( std::make_unique<MLUrl>( &media ) );
-    return res;
-}
-
 vlc_ml_sorting_criteria_t MLUrlModel::roleToCriteria(int role) const
 {
     switch (role) {
@@ -149,3 +125,34 @@ QString MLUrl::getLastPlayedDate() const
 MLUrl *MLUrl::clone() const {
     return new MLUrl( *this );
 }
+
+ListCacheLoader<std::unique_ptr<MLUrl>> *
+MLUrlModel::createLoader() const
+{
+    return new Loader(*this);
+}
+
+size_t MLUrlModel::Loader::count() const
+{
+    MLQueryParams params = getParams();
+    auto queryParams = params.toCQueryParams();
+
+    return vlc_ml_count_stream_history( m_ml, &queryParams );
+}
+
+std::vector<std::unique_ptr<MLUrl>>
+MLUrlModel::Loader::load(size_t index, size_t count) const
+{
+    MLQueryParams params = getParams(index, count);
+    auto queryParams = params.toCQueryParams();
+
+    ml_unique_ptr<vlc_ml_media_list_t> media_list;
+    media_list.reset( vlc_ml_list_stream_history(m_ml, &queryParams) );
+    if ( media_list == nullptr )
+        return {};
+
+    std::vector<std::unique_ptr<MLUrl>> res;
+    for( const vlc_ml_media_t& media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
+        res.emplace_back( std::make_unique<MLUrl>( &media ) );
+    return res;
+}
diff --git a/modules/gui/qt/medialibrary/mlurlmodel.hpp b/modules/gui/qt/medialibrary/mlurlmodel.hpp
index 1828f15c89..b647d49234 100644
--- a/modules/gui/qt/medialibrary/mlurlmodel.hpp
+++ b/modules/gui/qt/medialibrary/mlurlmodel.hpp
@@ -69,11 +69,19 @@ public:
 
     Q_INVOKABLE void addAndPlay( const QString& url );
 
+protected:
+    ListCacheLoader<std::unique_ptr<MLUrl>> *createLoader() const override;
+
 private:
-    std::vector<std::unique_ptr<MLUrl>> fetch(const MLQueryParams &params) const override;
-    size_t countTotalElements(const MLQueryParams &params) const override;
     vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
     virtual void onVlcMlEvent( const MLEvent &event ) override;
+
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLUrlModel &model) : BaseLoader(model) {}
+        size_t count() const override;
+        std::vector<std::unique_ptr<MLUrl>> load(size_t index, size_t count) const override;
+    };
 };
 
 #endif // MLURLMODEL_H
diff --git a/modules/gui/qt/medialibrary/mlvideomodel.cpp b/modules/gui/qt/medialibrary/mlvideomodel.cpp
index 1e861d2714..27e1414e4b 100644
--- a/modules/gui/qt/medialibrary/mlvideomodel.cpp
+++ b/modules/gui/qt/medialibrary/mlvideomodel.cpp
@@ -92,27 +92,6 @@ QHash<int, QByteArray> MLVideoModel::roleNames() const
     };
 }
 
-std::vector<std::unique_ptr<MLVideo> > MLVideoModel::fetch(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    ml_unique_ptr<vlc_ml_media_list_t> media_list{ vlc_ml_list_video_media(
-                m_ml, &queryParams ) };
-    if ( media_list == nullptr )
-        return {};
-    std::vector<std::unique_ptr<MLVideo>> res;
-    for( vlc_ml_media_t &media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
-        res.emplace_back( std::make_unique<MLVideo>(m_ml, &media) );
-    return res;
-}
-
-size_t MLVideoModel::countTotalElements(const MLQueryParams &params) const
-{
-    auto queryParams = params.toCQueryParams();
-    queryParams.i_offset = 0;
-    queryParams.i_nbResults = 0;
-    return vlc_ml_count_video_media(m_ml, &queryParams);
-}
-
 vlc_ml_sorting_criteria_t MLVideoModel::roleToCriteria(int role) const
 {
     switch(role)
@@ -157,3 +136,33 @@ void MLVideoModel::thumbnailUpdated(int idx)
 {
     emit dataChanged(index(idx), index(idx), {VIDEO_THUMBNAIL});
 }
+
+ListCacheLoader<std::unique_ptr<MLVideo>> *
+MLVideoModel::createLoader() const
+{
+    return new Loader(*this);
+}
+
+size_t MLVideoModel::Loader::count() const
+{
+    MLQueryParams params = getParams();
+    auto queryParams = params.toCQueryParams();
+
+    return vlc_ml_count_video_media(m_ml, &queryParams);
+}
+
+std::vector<std::unique_ptr<MLVideo>>
+MLVideoModel::Loader::load(size_t index, size_t count) const
+{
+    MLQueryParams params = getParams(index, count);
+    auto queryParams = params.toCQueryParams();
+
+    ml_unique_ptr<vlc_ml_media_list_t> media_list{ vlc_ml_list_video_media(
+                m_ml, &queryParams ) };
+    if ( media_list == nullptr )
+        return {};
+    std::vector<std::unique_ptr<MLVideo>> res;
+    for( vlc_ml_media_t &media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
+        res.emplace_back( std::make_unique<MLVideo>(m_ml, &media) );
+    return res;
+}
diff --git a/modules/gui/qt/medialibrary/mlvideomodel.hpp b/modules/gui/qt/medialibrary/mlvideomodel.hpp
index 2d60df10b6..2f3fda89fa 100644
--- a/modules/gui/qt/medialibrary/mlvideomodel.hpp
+++ b/modules/gui/qt/medialibrary/mlvideomodel.hpp
@@ -62,9 +62,10 @@ public:
     QVariant data(const QModelIndex& index, int role) const override;
     QHash<int, QByteArray> roleNames() const override;
 
+protected:
+    ListCacheLoader<std::unique_ptr<MLVideo>> *createLoader() const override;
+
 private:
-    std::vector<std::unique_ptr<MLVideo>> fetch(const MLQueryParams &params) const override;
-    size_t countTotalElements(const MLQueryParams &params) const override;
     vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
     vlc_ml_sorting_criteria_t nameToCriteria(QByteArray name) const override;
     virtual void onVlcMlEvent( const MLEvent &event ) override;
@@ -72,6 +73,13 @@ private:
 
     static QHash<QByteArray, vlc_ml_sorting_criteria_t> M_names_to_criteria;
     QByteArray criteriaToName(vlc_ml_sorting_criteria_t criteria) const override;
+
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLVideoModel &model) : BaseLoader(model) {}
+        size_t count() const override;
+        std::vector<std::unique_ptr<MLVideo>> load(size_t index, size_t count) const override;
+    };
 };
 
 #endif // MCVIDEOMODEL_H
-- 
2.29.2



More information about the vlc-devel mailing list