[vlc-commits] [Git][videolan/vlc][master] 9 commits: qt: add loaded count property in base model

Steve Lhomme (@robUx4) gitlab at videolan.org
Tue Mar 4 08:25:10 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
def2a480 by Prince Gupta at 2025-03-04T08:10:57+00:00
qt: add loaded count property in base model

- - - - -
cede5b60 by Prince Gupta at 2025-03-04T08:10:57+00:00
qt: fix index retrieval from out of cache in ml model

fixes artist loading with large medialibrary

- - - - -
a4b686d5 by Prince Gupta at 2025-03-04T08:10:57+00:00
qt: fix ml test with empty list

- - - - -
fb8b74bb by Prince Gupta at 2025-03-04T08:10:57+00:00
qml: fix find artist index spam on base model chunk loading

- - - - -
6f25bc0d by Prince Gupta at 2025-03-04T08:10:57+00:00
qt: fix warning and typo

- - - - -
e554856e by Prince Gupta at 2025-03-04T08:10:57+00:00
qt: fix misplaced assert

- - - - -
c27e95f2 by Prince Gupta at 2025-03-04T08:10:57+00:00
qt: fix asyncFetchMore in between partial update in list cache

- - - - -
63dea8c7 by Prince Gupta at 2025-03-04T08:10:57+00:00
qt: refactor getIndexFromId of ml model

- - - - -
bb649eb3 by Prince Gupta at 2025-03-04T08:10:57+00:00
qt: add tests for getIndexFromID of ml model

- - - - -


9 changed files:

- modules/gui/qt/medialibrary/mlbasemodel.cpp
- modules/gui/qt/medialibrary/mlbasemodel.hpp
- modules/gui/qt/medialibrary/qml/MusicArtistsAlbums.qml
- modules/gui/qt/tests/test_ml_model.cpp
- modules/gui/qt/util/base_model.cpp
- modules/gui/qt/util/base_model.hpp
- modules/gui/qt/util/base_model_p.hpp
- modules/gui/qt/util/listcache.hpp
- modules/gui/qt/util/listcache.hxx


Changes:

=====================================
modules/gui/qt/medialibrary/mlbasemodel.cpp
=====================================
@@ -98,29 +98,32 @@ public:
     }
 
     //this will load data until id is in the cache, then returns its position
-    void getIndexFromIdImpl(MLItemId id, QJSValue resolve, QJSValue reject) const
+    void getIndexFromIdImpl(MLItemId id, std::function<void (std::optional<int> index)> cb) const
     {
         Q_Q(const MLBaseModel);
 
-        auto jsEngine = qjsEngine(q);
         int index;
         MLItem* item = q->findInCache(id, &index);
+
         if (item) {
-            resolve.call({index});
+            cb(index);
         }
         else
         {
+            unsigned int loaded = q->getLoadedCount();
             int count = q->getCount();
             int limit = q->getLimit();
             //item doesn't exists
-            if ((limit != 0 && count >= limit) || count == getMaximumCount())
-                reject.call(); //not present
+            if ((limit != 0 && count >= limit) || loaded == getMaximumCount()) {
+                cb(std::nullopt);
+            }
             else
             {
                 //load data until we have our item in cache
-                QObject::connect(q, &MLBaseModel::countChanged, q, [this, id, resolve = resolve, reject = reject](){
-                    getIndexFromIdImpl(id, resolve, reject);
+                QObject::connect(q, &MLBaseModel::dataChanged, q, [this, id, cb](){
+                    getIndexFromIdImpl(id, cb);
                 }, Qt::SingleShotConnection);
+
                 if (m_cache)
                     m_cache->fetchMore();
             }
@@ -280,8 +283,10 @@ Q_INVOKABLE QJSValue MLBaseModel::getDataById(MLItemId id)
 
         m_itemLoader->loadItemByIdTask(
             id,
-            [this, resolve=std::move(resolve), reject=std::move(reject)](size_t taksId, MLListCache::ItemType&& item)
+            [this, resolve=std::move(resolve), reject=std::move(reject)](size_t taskId, MLListCache::ItemType&& item)
             {
+                Q_UNUSED(taskId);
+
                 if (!item)
                 {
                     reject.call();
@@ -301,10 +306,25 @@ Q_INVOKABLE QJSValue MLBaseModel::getIndexFromId(MLItemId id)
     Q_D(const MLBaseModel);
 
     auto [p, resolve, reject] = d->makeJSPromise();
-    d->getIndexFromIdImpl(id, resolve, reject);
+    auto cb = [resolve = resolve, reject = reject](std::optional<int> index)
+    {
+        if (index.has_value())
+            resolve.call({*index});
+        else
+            reject.call(); //not present
+    };
+
+    getIndexFromId2(id, cb);
     return p;
 }
 
+void MLBaseModel::getIndexFromId2(MLItemId id, std::function<void (std::optional<int>)> cb)
+{
+    Q_D(const MLBaseModel);
+
+    d->getIndexFromIdImpl(id, cb);
+}
+
 QVariant MLBaseModel::data(const QModelIndex &index, int role) const
 {
     Q_D(const MLBaseModel);


=====================================
modules/gui/qt/medialibrary/mlbasemodel.hpp
=====================================
@@ -86,6 +86,8 @@ public:
      */
     Q_INVOKABLE QJSValue getIndexFromId(MLItemId id);
 
+    void getIndexFromId2(MLItemId id, std::function<void(std::optional<int> index)> cb);
+
 public:
     // properties functions
 


=====================================
modules/gui/qt/medialibrary/qml/MusicArtistsAlbums.qml
=====================================
@@ -44,6 +44,8 @@ FocusScope {
     property int initialAlbumIndex: 0
     property var artistId: undefined
 
+    property var _requestedArtistId: undefined
+
     //behave like a page
     property var pagePrefix: []
 
@@ -110,12 +112,32 @@ FocusScope {
         onLoadingChanged: {
             if (loading)
                 return
-            artistModel.getIndexFromId(root.artistId)
+
+            const defined = v => typeof v !== "undefined"
+
+            if ((root._requestedArtistId === root.artistId)
+                    && defined(root._requestedArtistId))
+                return
+
+            root._requestedArtistId = root.artistId
+            if (!defined(root._requestedArtistId))
+                return
+
+            const thisRequestID = root._requestedArtistId
+            artistModel.getIndexFromId(root._requestedArtistId)
                 .then((row) => {
+                    if ((root._requestedArtistId !== thisRequestID)
+                        || (root._requestedArtistId !== root.artistId))
+                        return
+
                     artistList.currentIndex = row
                     artistList._sidebarInitialyPositioned = true
                 })
                 .catch(() => {
+                    if ((root._requestedArtistId !== thisRequestID)
+                       || (root._requestedArtistId !== root.artistId))
+                    return
+
                     artistList._sidebarInitialyPositioned = true
                 })
         }


=====================================
modules/gui/qt/tests/test_ml_model.cpp
=====================================
@@ -83,6 +83,10 @@ protected:
         std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t* ml, const vlc_ml_query_params_t* queryParams) const override
         {
             VLC_UNUSED(ml);
+
+            if (m_mlTestModel.m_items.empty())
+                return {};
+
             uint32_t offset = queryParams->i_offset;
             uint32_t count = queryParams->i_nbResults;
             size_t maxIndex = std::min(
@@ -163,6 +167,47 @@ private slots:
         m_model->appendRange(1, 1000);
         QTRY_COMPARE_WITH_TIMEOUT(m_model->getCount(), 1000u, 100);
     }
+
+
+    void testGetIndexFromID()
+    {
+        const int low = 1;
+        const int high = 300;
+        m_model->appendRange(low, high);
+
+        const int timeout = 1000;
+
+        // let loading complete, getIndexFromID won't work for 'loading' model
+        QVERIFY(QTest::qWaitFor([this] () { return !m_model->loading(); }, timeout));
+
+        std::optional<int> row = -1;
+        auto cb = [&row](std::optional<int> index)
+        {
+            row = index;
+        };
+
+        // bug of MR6926, here model tries to get index for data outside the initial chunk size
+        m_model->getIndexFromId2(MLItemId{high, VLC_ML_PARENT_UNKNOWN}, cb);
+
+        // NOTE: CI fails with QTRY_COMPARE_WITH_TIMEOUT
+        QVERIFY(QTest::qWaitFor([&row]() { return row != -1; }, timeout));
+        QCOMPARE(row, high - 1);
+
+        // above we must have loaded all the data, following this we should not need to wait for results
+        m_model->getIndexFromId2(MLItemId{low - 1, VLC_ML_PARENT_UNKNOWN}, cb);
+        QCOMPARE(row, std::nullopt);
+
+        m_model->getIndexFromId2(MLItemId{low, VLC_ML_PARENT_UNKNOWN}, cb);
+        QCOMPARE(row, low - 1);
+
+        m_model->getIndexFromId2(MLItemId{high + 1, VLC_ML_PARENT_UNKNOWN}, cb);
+        QCOMPARE(row, std::nullopt);
+
+        const int mid = (low + high) / 2;
+        m_model->getIndexFromId2(MLItemId{mid, VLC_ML_PARENT_UNKNOWN}, cb);
+        QCOMPARE(row, mid - 1);
+    }
+
 private:
     std::unique_ptr<VLCTestingEnv> m_env;
     std::unique_ptr<MediaLib> m_medialib;


=====================================
modules/gui/qt/util/base_model.cpp
=====================================
@@ -196,6 +196,12 @@ unsigned int BaseModel::getCount() const
     return d->getCount();
 }
 
+unsigned int BaseModel::getLoadedCount() const
+{
+    Q_D(const BaseModel);
+    return d->getLoadedCount();
+}
+
 unsigned int BaseModel::getMaximumCount() const
 {
     Q_D(const BaseModel);


=====================================
modules/gui/qt/util/base_model.hpp
=====================================
@@ -76,6 +76,7 @@ public:
     void setOffset(unsigned int offset);
 
     unsigned int getCount() const;
+    unsigned int getLoadedCount() const;
     unsigned int getMaximumCount() const;
 
     virtual bool loading() const;


=====================================
modules/gui/qt/util/base_model_p.hpp
=====================================
@@ -36,6 +36,8 @@ public:
     virtual void resetCache() = 0;
     virtual void invalidateCache() = 0;
     virtual bool loading() const = 0;
+
+    virtual unsigned int getLoadedCount() const = 0;
     virtual unsigned int getCount() const = 0;
     virtual unsigned int getMaximumCount() const = 0;
 
@@ -134,6 +136,7 @@ public:
     const T* item(int signedidx) const;
 
     unsigned int getCount() const override;
+    unsigned int getLoadedCount() const override;
     unsigned int getMaximumCount() const override;
 
     virtual std::unique_ptr<ListCacheLoader<T>> createLoader() const = 0;
@@ -156,6 +159,19 @@ unsigned BaseModelPrivateT<T>::getCount() const
     return static_cast<unsigned>(queryCount);
 }
 
+template<typename T>
+unsigned BaseModelPrivateT<T>::getLoadedCount() const
+{
+    if (!m_cache)
+        return 0;
+
+    ssize_t loadedCount = m_cache->loadedCount();
+    if (loadedCount == ListCache<T>::COUNT_UNINITIALIZED)
+        return 0;
+
+    return static_cast<unsigned>(loadedCount);
+}
+
 template<typename T>
 unsigned BaseModelPrivateT<T>::getMaximumCount() const
 {


=====================================
modules/gui/qt/util/listcache.hpp
=====================================
@@ -222,6 +222,14 @@ public:
      */
     ssize_t maximumCount() const;
 
+    /**
+     * @return underlying list size
+     * COUNT_UNINITIALIZED is returned if the list isn't initialized
+     *
+     * This may be usefull to know loaded size
+     */
+    ssize_t loadedCount() const;
+
     /**
      * Init the list size
      *


=====================================
modules/gui/qt/util/listcache.hxx
=====================================
@@ -390,7 +390,8 @@ void  ListCache<T>::fetchMore()
         return;
 
     m_maxReferedIndex = std::min(cache->loadedCount + m_chunkSize, cache->queryCount);
-    if (!m_appendTask)
+    if (!m_appendTask
+            && cache != m_oldData.get()) // data will be loaded after completing pending updates
         asyncFetchMore();
 }
 
@@ -599,10 +600,10 @@ void ListCache<T>::asyncCountAndLoad()
 template<typename T>
 void ListCache<T>::asyncFetchMore()
 {
+    assert(m_cachedData);
     if (m_maxReferedIndex <= m_cachedData->loadedCount)
         return;
 
-    assert(m_cachedData);
     if (m_appendTask)
         m_loader->cancelTask(m_appendTask);
 
@@ -635,4 +636,11 @@ void ListCache<T>::asyncFetchMore()
         });
 }
 
+
+template<typename T>
+inline ssize_t ListCache<T>::loadedCount() const
+{
+    return m_cachedData ? m_cachedData->loadedCount : COUNT_UNINITIALIZED;
+}
+
 #endif /* LISTCACHE_HXX */



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/aa15eaa7d8051e1223d6eba226857d9dbcf7756f...bb649eb31fe839fd3241e02acd85edba401d69f6

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/aa15eaa7d8051e1223d6eba226857d9dbcf7756f...bb649eb31fe839fd3241e02acd85edba401d69f6
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list