[vlc-commits] [Git][videolan/vlc][master] 15 commits: medialibrary: Add video(s) list and count functions

Hugo Beauzée-Luyssen (@chouquette) gitlab at videolan.org
Sun Feb 20 15:40:10 UTC 2022



Hugo Beauzée-Luyssen pushed to branch master at VideoLAN / VLC


Commits:
a4e5e0ce by Pierre Lamot at 2022-02-20T14:18:57+00:00
medialibrary: Add video(s) list and count functions

Co-authored-by: Benjamin Arnaud <benjamin.arnaud at videolabs.io>

- - - - -
7866b1be by Pierre Lamot at 2022-02-20T14:18:57+00:00
medialibrary: Add audio(s) list and count functions

Co-authored-by: Benjamin Arnaud <benjamin.arnaud at videolabs.io>

- - - - -
6f2b25fa by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
medialibrary: Udpate folder support

- - - - -
5fa9fb97 by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt/covergenerator: Add folder support

- - - - -
21c57e14 by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt/mlhelper: Create the 'getVideoListCover' function

- - - - -
1066a20b by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt/mlvideomodel: Switch to list video(s) functions

- - - - -
4f02618c by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt/mlgrouplistmodel: Inherit from MLVideoModel

- - - - -
c536126d by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt: Rename MLGroupListModel to MLVideoGroupsModel

- - - - -
fd3245c2 by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt: Create MLFolder

- - - - -
877355ec by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt: Create MLVideoFoldersModel

- - - - -
724981ad by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt: Rename GroupListContextMenu to VideoGroupsContextMenu

- - - - -
1af0a999 by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt/qml_menu_wrapper: Add 'Group by folder' to SortMenuVideo

- - - - -
02dca7ec by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qt/qml_menu_wrapper: Create VideoFoldersContextMenu

- - - - -
712c7715 by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qml/VideoAllSubDisplay: Add folder support

- - - - -
6fb0c5f3 by Benjamin Arnaud at 2022-02-20T14:18:57+00:00
qml/MediaGroupDisplay: Fix the 'initialTitle' property

- - - - -


23 changed files:

- include/vlc_media_library.h
- modules/gui/qt/Makefile.am
- modules/gui/qt/maininterface/mainui.cpp
- modules/gui/qt/medialibrary/mlbasemodel.hpp
- + modules/gui/qt/medialibrary/mlfolder.cpp
- + modules/gui/qt/medialibrary/mlfolder.hpp
- modules/gui/qt/medialibrary/mlhelper.cpp
- modules/gui/qt/medialibrary/mlhelper.hpp
- modules/gui/qt/medialibrary/mlqmltypes.hpp
- + modules/gui/qt/medialibrary/mlvideofoldersmodel.cpp
- + modules/gui/qt/medialibrary/mlvideofoldersmodel.hpp
- modules/gui/qt/medialibrary/mlgrouplistmodel.cpp → modules/gui/qt/medialibrary/mlvideogroupsmodel.cpp
- modules/gui/qt/medialibrary/mlgrouplistmodel.hpp → modules/gui/qt/medialibrary/mlvideogroupsmodel.hpp
- modules/gui/qt/medialibrary/mlvideomodel.cpp
- modules/gui/qt/medialibrary/qml/MediaGroupDisplay.qml
- modules/gui/qt/medialibrary/qml/VideoAllSubDisplay.qml
- modules/gui/qt/menus/qml_menu_wrapper.cpp
- modules/gui/qt/menus/qml_menu_wrapper.hpp
- modules/gui/qt/util/covergenerator.cpp
- modules/misc/medialibrary/entities.cpp
- modules/misc/medialibrary/medialibrary.cpp
- modules/misc/medialibrary/medialibrary.h
- po/POTFILES.in


Changes:

=====================================
include/vlc_media_library.h
=====================================
@@ -334,9 +334,11 @@ typedef struct vlc_ml_playlist_list_t
 
 typedef struct vlc_ml_folder_t
 {
-    int64_t i_id; /**< This folder's MRL. Will be NULL if b_present is false */
-    char* psz_mrl; /**< This folder's MRL. Will be NULL if b_present is false */
-    bool b_present; /**< The presence state for this folder. */
+    int64_t i_id; /**< The folder's MRL. Will be NULL if b_present is false */
+    char* psz_name; /**< The folder's name */
+    char* psz_mrl; /**< The folder's MRL. Will be NULL if b_present is false */
+    unsigned int i_nb_media; /**< The media count */
+    bool b_present; /**< The folder's presence state */
     bool b_banned; /**< Will be true if the user required this folder to be excluded */
 } vlc_ml_folder_t;
 
@@ -443,6 +445,8 @@ enum vlc_ml_list_queries
     VLC_ML_COUNT_ENTRY_POINTS,    /**< arg1 bool: list_banned; arg2 (out): size_t*                                      */
     VLC_ML_LIST_FOLDERS,          /**< arg1 (out): vlc_ml_folder_list_t**                                               */
     VLC_ML_COUNT_FOLDERS,         /**< arg1 (out): size_t*                                                              */
+    VLC_ML_LIST_FOLDERS_BY_TYPE,  /**< arg1 vlc_ml_media_type_t: the media type. arg2 (out): vlc_ml_media_list_t**      */
+    VLC_ML_COUNT_FOLDERS_BY_TYPE, /**< arg1 vlc_ml_media_type_t: the media type. arg2 (out): vlc_ml_media_list_t**      */
 
     /* Album specific listings */
     VLC_ML_LIST_ALBUM_TRACKS,     /**< arg1: The album id. arg2 (out): vlc_ml_media_list_t**                            */
@@ -484,12 +488,16 @@ enum vlc_ml_list_queries
     /* Folder specific listings */
     VLC_ML_LIST_SUBFOLDERS,       /**< arg1: parent id; arg2 (out): vlc_ml_folder_list_t**                              */
     VLC_ML_COUNT_SUBFOLDERS,      /**< arg1: parent id; arg2 (out): size_t*                                             */
-    VLC_ML_LIST_FOLDER_MEDIAS,    /**< arg1: folder id; arg2 (out): vlc_ml_media_list_t**                               */
-    VLC_ML_COUNT_FOLDER_MEDIAS,   /**< arg1: folder id; arg2 (out): size_t*                                             */
+    VLC_ML_LIST_FOLDER_MEDIA,     /**< arg1: folder id; arg2 (out): vlc_ml_media_list_t**                               */
+    VLC_ML_COUNT_FOLDER_MEDIA,    /**< arg1: folder id; arg2 (out): size_t*                                             */
 
     /* Children entities listing */
     VLC_ML_LIST_MEDIA_OF,         /**< arg1: parent entity type; arg2: parent entity id; arg3(out): ml_media_list_t**   */
     VLC_ML_COUNT_MEDIA_OF,        /**< arg1: parent entity type; arg2: parent entity id; arg3(out): size_t*             */
+    VLC_ML_LIST_VIDEO_OF,         /**< arg1: parent entity type; arg2: parent entity id; arg3(out): size_t*             */
+    VLC_ML_COUNT_VIDEO_OF,        /**< arg1: parent entity type; arg2: parent entity id; arg3(out): size_t*             */
+    VLC_ML_LIST_AUDIO_OF,         /**< arg1: parent entity type; arg2: parent entity id; arg3(out): size_t*             */
+    VLC_ML_COUNT_AUDIO_OF,        /**< arg1: parent entity type; arg2: parent entity id; arg3(out): size_t*             */
     VLC_ML_LIST_ARTISTS_OF,       /**< arg1: parent entity type; arg2: parent entity id; arg3(out): ml_artist_list_t**  */
     VLC_ML_COUNT_ARTISTS_OF,      /**< arg1: parent entity type; arg2: parent entity id; arg3(out): size_t*             */
     VLC_ML_LIST_ALBUMS_OF,        /**< arg1: parent entity type; arg2: parent entity id; arg3(out): ml_album_list_t**   */
@@ -504,6 +512,7 @@ enum vlc_ml_parent_type
     VLC_ML_PARENT_SHOW,
     VLC_ML_PARENT_GENRE,
     VLC_ML_PARENT_GROUP,
+    VLC_ML_PARENT_FOLDER,
     VLC_ML_PARENT_PLAYLIST,
 };
 
@@ -1209,6 +1218,66 @@ static inline size_t vlc_ml_count_media_of( vlc_medialibrary_t* p_ml, const vlc_
     return res;
 }
 
+static inline vlc_ml_media_list_t* vlc_ml_list_video_of( vlc_medialibrary_t* p_ml,
+                                                         const vlc_ml_query_params_t* params,
+                                                         int i_parent_type, int64_t i_parent_id )
+{
+    vlc_assert( p_ml != NULL );
+
+    vlc_ml_media_list_t* res;
+
+    if ( vlc_ml_list( p_ml, VLC_ML_LIST_VIDEO_OF,
+                      params, i_parent_type, i_parent_id, &res ) != VLC_SUCCESS )
+        return NULL;
+
+    return res;
+}
+
+static inline size_t vlc_ml_count_video_of( vlc_medialibrary_t* p_ml,
+                                            const vlc_ml_query_params_t* params,
+                                            int i_parent_type, int64_t i_parent_id )
+{
+    vlc_assert( p_ml != NULL );
+
+    size_t res;
+
+    if ( vlc_ml_list( p_ml, VLC_ML_COUNT_VIDEO_OF,
+                      params, i_parent_type, i_parent_id, &res ) != VLC_SUCCESS )
+        return 0;
+
+    return res;
+}
+
+static inline vlc_ml_media_list_t* vlc_ml_list_audio_of( vlc_medialibrary_t* p_ml,
+                                                         const vlc_ml_query_params_t* params,
+                                                         int i_parent_type, int64_t i_parent_id )
+{
+    vlc_assert( p_ml != NULL );
+
+    vlc_ml_media_list_t* res;
+
+    if ( vlc_ml_list( p_ml, VLC_ML_LIST_AUDIO_OF,
+                      params, i_parent_type, i_parent_id, &res ) != VLC_SUCCESS )
+        return NULL;
+
+    return res;
+}
+
+static inline size_t vlc_ml_count_audio_of( vlc_medialibrary_t* p_ml,
+                                            const vlc_ml_query_params_t* params,
+                                            int i_parent_type, int64_t i_parent_id )
+{
+    vlc_assert( p_ml != NULL );
+
+    size_t res;
+
+    if ( vlc_ml_list( p_ml, VLC_ML_COUNT_AUDIO_OF,
+                      params, i_parent_type, i_parent_id, &res ) != VLC_SUCCESS )
+        return 0;
+
+    return res;
+}
+
 static inline vlc_ml_artist_list_t* vlc_ml_list_artist_of( vlc_medialibrary_t* p_ml, const vlc_ml_query_params_t* params, int i_parent_type, int64_t i_parent_id )
 {
     vlc_assert( p_ml != NULL );
@@ -1693,6 +1762,93 @@ static inline size_t vlc_ml_count_banned_entry_points( vlc_medialibrary_t* p_ml,
     return res;
 }
 
+// Folders
+
+static inline vlc_ml_folder_list_t * vlc_ml_list_folders(vlc_medialibrary_t * p_ml,
+                                                         const vlc_ml_query_params_t * params)
+{
+    vlc_assert(p_ml != NULL);
+
+    vlc_ml_folder_list_t * res;
+
+    if (vlc_ml_list(p_ml, VLC_ML_LIST_FOLDERS, params, &res) != VLC_SUCCESS)
+        return NULL;
+
+    return res;
+}
+
+static inline size_t vlc_ml_count_folders(vlc_medialibrary_t * p_ml,
+                                          const vlc_ml_query_params_t * params)
+{
+    vlc_assert(p_ml != NULL);
+
+    size_t count;
+
+    if (vlc_ml_list(p_ml, VLC_ML_COUNT_FOLDERS, params, &count) != VLC_SUCCESS)
+        return 0;
+
+    return count;
+}
+
+static inline
+vlc_ml_folder_list_t * vlc_ml_list_folders_by_type(vlc_medialibrary_t * p_ml,
+                                                   const vlc_ml_query_params_t * params,
+                                                   vlc_ml_media_type_t type)
+{
+    vlc_assert(p_ml != NULL);
+
+    vlc_ml_folder_list_t * res;
+
+    if (vlc_ml_list(p_ml, VLC_ML_LIST_FOLDERS_BY_TYPE, params, (int) type, &res) != VLC_SUCCESS)
+        return NULL;
+
+    return res;
+}
+
+static inline size_t vlc_ml_count_folders_by_type(vlc_medialibrary_t * p_ml,
+                                                  const vlc_ml_query_params_t * params,
+                                                  vlc_ml_media_type_t type)
+{
+    vlc_assert(p_ml != NULL);
+
+    size_t count;
+
+    if (vlc_ml_list(p_ml, VLC_ML_COUNT_FOLDERS_BY_TYPE, params, (int) type, &count) != VLC_SUCCESS)
+        return 0;
+
+    return count;
+}
+
+// Folder Media
+
+static inline vlc_ml_media_list_t * vlc_ml_list_folder_media(vlc_medialibrary_t * p_ml,
+                                                             const vlc_ml_query_params_t * params,
+                                                             int64_t i_folder_id)
+{
+    vlc_assert(p_ml != NULL);
+
+    vlc_ml_media_list_t * res;
+
+    if (vlc_ml_list(p_ml, VLC_ML_LIST_FOLDER_MEDIA, params, i_folder_id, &res) != VLC_SUCCESS)
+        return NULL;
+
+    return res;
+}
+
+static inline size_t vlc_ml_count_folder_media(vlc_medialibrary_t * p_ml,
+                                               const vlc_ml_query_params_t * params,
+                                               int64_t i_folder_id)
+{
+    vlc_assert(p_ml != NULL);
+
+    size_t count;
+
+    if (vlc_ml_list(p_ml, VLC_ML_COUNT_FOLDER_MEDIA, params, i_folder_id, &count) != VLC_SUCCESS)
+        return 0;
+
+    return count;
+}
+
 #ifdef __cplusplus
 }
 #endif /* C++ */


=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -162,6 +162,8 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/medialibrary/mlbookmarkmodel.cpp \
 	gui/qt/medialibrary/mlbookmarkmodel.hpp \
 	gui/qt/medialibrary/mlevent.hpp \
+	gui/qt/medialibrary/mlfolder.cpp \
+	gui/qt/medialibrary/mlfolder.hpp \
 	gui/qt/medialibrary/mlfoldersmodel.cpp \
 	gui/qt/medialibrary/mlfoldersmodel.hpp \
 	gui/qt/medialibrary/mlgenre.cpp \
@@ -170,8 +172,6 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/medialibrary/mlgenremodel.hpp \
 	gui/qt/medialibrary/mlgroup.cpp \
 	gui/qt/medialibrary/mlgroup.hpp \
-	gui/qt/medialibrary/mlgrouplistmodel.cpp \
-	gui/qt/medialibrary/mlgrouplistmodel.hpp \
 	gui/qt/medialibrary/mlhelper.cpp \
 	gui/qt/medialibrary/mlhelper.hpp \
 	gui/qt/medialibrary/mlitemcover.cpp \
@@ -191,6 +191,10 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/medialibrary/mlurlmodel.hpp \
 	gui/qt/medialibrary/mlvideo.cpp \
 	gui/qt/medialibrary/mlvideo.hpp \
+	gui/qt/medialibrary/mlvideofoldersmodel.cpp \
+	gui/qt/medialibrary/mlvideofoldersmodel.hpp \
+	gui/qt/medialibrary/mlvideogroupsmodel.cpp \
+	gui/qt/medialibrary/mlvideogroupsmodel.hpp \
 	gui/qt/medialibrary/mlvideomodel.cpp \
 	gui/qt/medialibrary/mlvideomodel.hpp \
 	gui/qt/medialibrary/mlplaylist.cpp \
@@ -391,7 +395,6 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/medialibrary/mlbasemodel.moc.cpp \
 	gui/qt/medialibrary/mlfoldersmodel.moc.cpp \
 	gui/qt/medialibrary/mlgenremodel.moc.cpp \
-	gui/qt/medialibrary/mlgrouplistmodel.moc.cpp \
 	gui/qt/medialibrary/mllistcache.moc.cpp \
 	gui/qt/medialibrary/mlthreadpool.moc.cpp \
 	gui/qt/medialibrary/mlqmltypes.moc.cpp \
@@ -402,6 +405,8 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/medialibrary/mlvideomodel.moc.cpp \
 	gui/qt/medialibrary/mlplaylistlistmodel.moc.cpp \
 	gui/qt/medialibrary/mlplaylistmodel.moc.cpp \
+	gui/qt/medialibrary/mlvideofoldersmodel.moc.cpp \
+	gui/qt/medialibrary/mlvideogroupsmodel.moc.cpp \
 	gui/qt/menus/custom_menus.moc.cpp \
 	gui/qt/menus/qml_menu_wrapper.moc.cpp \
 	gui/qt/menus/menus.moc.cpp \


=====================================
modules/gui/qt/maininterface/mainui.cpp
=====================================
@@ -13,7 +13,8 @@
 #include "medialibrary/mlrecentsmodel.hpp"
 #include "medialibrary/mlrecentsvideomodel.hpp"
 #include "medialibrary/mlfoldersmodel.hpp"
-#include "medialibrary/mlgrouplistmodel.hpp"
+#include "medialibrary/mlvideogroupsmodel.hpp"
+#include "medialibrary/mlvideofoldersmodel.hpp"
 #include "medialibrary/mlplaylistlistmodel.hpp"
 #include "medialibrary/mlplaylistmodel.hpp"
 #include "medialibrary/mlplaylist.hpp"
@@ -324,7 +325,8 @@ void MainUI::registerQMLTypes()
         qmlRegisterType<MLUrlModel>( uri, versionMajor, versionMinor, "MLUrlModel" );
         qmlRegisterType<MLVideoModel>( uri, versionMajor, versionMinor, "MLVideoModel" );
         qmlRegisterType<MLRecentsVideoModel>( uri, versionMajor, versionMinor, "MLRecentsVideoModel" );
-        qmlRegisterType<MLGroupListModel>( uri, versionMajor, versionMinor, "MLGroupListModel" );
+        qmlRegisterType<MLVideoGroupsModel>( uri, versionMajor, versionMinor, "MLVideoGroupsModel" );
+        qmlRegisterType<MLVideoFoldersModel>( uri, versionMajor, versionMinor, "MLVideoFoldersModel" );
         qmlRegisterType<MLPlaylistListModel>( uri, versionMajor, versionMinor, "MLPlaylistListModel" );
         qmlRegisterType<MLPlaylistModel>( uri, versionMajor, versionMinor, "MLPlaylistModel" );
 
@@ -347,7 +349,8 @@ void MainUI::registerQMLTypes()
         qmlRegisterType<AlbumTrackContextMenu>( uri, versionMajor, versionMinor, "AlbumTrackContextMenu" );
         qmlRegisterType<URLContextMenu>( uri, versionMajor, versionMinor, "URLContextMenu" );
         qmlRegisterType<VideoContextMenu>( uri, versionMajor, versionMinor, "VideoContextMenu" );
-        qmlRegisterType<GroupListContextMenu>( uri, versionMajor, versionMinor, "GroupListContextMenu" );
+        qmlRegisterType<VideoGroupsContextMenu>( uri, versionMajor, versionMinor, "VideoGroupsContextMenu" );
+        qmlRegisterType<VideoFoldersContextMenu>( uri, versionMajor, versionMinor, "VideoFoldersContextMenu" );
         qmlRegisterType<PlaylistListContextMenu>( uri, versionMajor, versionMinor, "PlaylistListContextMenu" );
         qmlRegisterType<PlaylistMediaContextMenu>( uri, versionMajor, versionMinor, "PlaylistMediaContextMenu" );
 


=====================================
modules/gui/qt/medialibrary/mlbasemodel.hpp
=====================================
@@ -36,8 +36,10 @@
 #include "mlqueryparams.hpp"
 #include "util/listcacheloader.hpp"
 
+// Fordward declarations
 class MLListCache;
 class MediaLib;
+class MLItemCover;
 
 class MLBaseModel : public QAbstractListModel
 {
@@ -172,6 +174,9 @@ protected:
     bool m_need_reset = false;
 
     mutable std::unique_ptr<MLListCache> m_cache;
+
+private: // Friends
+    friend QString getVideoListCover(const MLBaseModel*, MLItemCover*, int, int, int);
 };
 
 #endif // MLBASEMODEL_HPP


=====================================
modules/gui/qt/medialibrary/mlfolder.cpp
=====================================
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee at omega.gg>
+ *
+ * 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 "mlfolder.hpp"
+
+// Ctor / dtor
+
+MLFolder::MLFolder(const vlc_ml_folder_t * data)
+    : MLItemCover(MLItemId(data->i_id, VLC_ML_PARENT_FOLDER))
+    , m_present(data->b_present)
+    , m_banned(data->b_banned)
+    , m_title(data->psz_name)
+    , m_mrl(data->psz_mrl)
+    , m_duration(0) // FIXME: We should have a duration field in vlc_ml_folder_t.
+    , m_count(data->i_nb_media) {}
+
+// Interface
+
+bool MLFolder::isPresent() const
+{
+    return m_present;
+}
+
+bool MLFolder::isBanned() const
+{
+    return m_banned;
+}
+
+QString MLFolder::getTitle() const
+{
+    return m_title;
+}
+
+QString MLFolder::getMRL() const
+{
+    return m_mrl;
+}
+
+int64_t MLFolder::getDuration() const
+{
+    return m_duration;
+}
+
+unsigned int MLFolder::getCount() const
+{
+    return m_count;
+}


=====================================
modules/gui/qt/medialibrary/mlfolder.hpp
=====================================
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee at omega.gg>
+ *
+ * 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 MLFOLDER_HPP
+#define MLFOLDER_HPP
+
+// MediaLibrary includes
+#include "mlitemcover.hpp"
+
+class MLFolder : public MLItemCover
+{
+public:
+    MLFolder(const vlc_ml_folder_t * data);
+
+public: // Interface
+    bool isPresent() const;
+    bool isBanned() const;
+
+    QString getTitle() const;
+
+    QString getMRL() const;
+
+    int64_t getDuration() const;
+
+    unsigned int getCount() const;
+
+private:
+    bool m_present;
+    bool m_banned;
+
+    QString m_title;
+
+    QString m_mrl;
+
+    int64_t m_duration;
+
+    unsigned int m_count;
+};
+
+#endif


=====================================
modules/gui/qt/medialibrary/mlhelper.cpp
=====================================
@@ -18,6 +18,10 @@
 
 #include "mlhelper.hpp"
 
+// MediaLibrary includes
+#include "mlbasemodel.hpp"
+#include "mlitemcover.hpp"
+
 QString MsToString( int64_t time , bool doShort )
 {
     if (time < 0)
@@ -42,3 +46,59 @@ QString MsToString( int64_t time , bool doShort )
                 .arg(sec, 2, 10, QChar('0'));
 
 }
+
+QString getVideoListCover( const MLBaseModel* model, MLItemCover* item, int width, int height,
+                           int role )
+{
+    QString cover = item->getCover();
+
+    // NOTE: Making sure we're not already generating a cover.
+    if (cover.isNull() == false || item->hasGenerator())
+        return cover;
+
+    MLItemId itemId = item->getId();
+
+    struct Context { QString cover; };
+
+    item->setGenerator(true);
+
+    model->ml()->runOnMLThread<Context>(model,
+    //ML thread
+    [itemId, width, height]
+    (vlc_medialibrary_t * ml, Context & ctx)
+    {
+        CoverGenerator generator{ ml, itemId };
+
+        generator.setSize(QSize(width, height));
+
+        generator.setDefaultThumbnail(":/noart_videoCover.svg");
+
+        if (generator.cachedFileAvailable())
+            ctx.cover = generator.cachedFileURL();
+        else
+            ctx.cover = generator.execute();
+    },
+    //UI Thread
+    [model, itemId, role]
+    (quint64, Context & ctx)
+    {
+        int row;
+
+        // NOTE: We want to avoid calling 'MLBaseModel::item' for performance issues.
+        auto item = static_cast<MLItemCover *>(model->findInCache(itemId, &row));
+
+        if (!item)
+            return;
+
+        item->setCover(ctx.cover);
+
+        item->setGenerator(false);
+
+        QModelIndex modelIndex = model->index(row);
+
+        //we're running in a callback
+        emit const_cast<MLBaseModel *>(model)->dataChanged(modelIndex, modelIndex, { role });
+    });
+
+    return cover;
+}


=====================================
modules/gui/qt/medialibrary/mlhelper.hpp
=====================================
@@ -28,6 +28,10 @@
 #include "vlc_media_library.h"
 #include <QString>
 
+// Forward declarations
+class MLBaseModel;
+class MLItemCover;
+
 template<typename T>
 class MLDeleter
 {
@@ -73,4 +77,7 @@ MLListRange<T> ml_range_iterate(L& list)
 
 QString MsToString( int64_t time, bool doShort = false );
 
+QString getVideoListCover( const MLBaseModel* model, MLItemCover* item, int width, int height,
+                           int role );
+
 #endif // MLHELPER_HPP


=====================================
modules/gui/qt/medialibrary/mlqmltypes.hpp
=====================================
@@ -53,6 +53,7 @@ public:
             ML_PARENT_TYPE_CASE(VLC_ML_PARENT_SHOW);
             ML_PARENT_TYPE_CASE(VLC_ML_PARENT_GENRE);
             ML_PARENT_TYPE_CASE(VLC_ML_PARENT_GROUP);
+            ML_PARENT_TYPE_CASE(VLC_ML_PARENT_FOLDER);
             ML_PARENT_TYPE_CASE(VLC_ML_PARENT_PLAYLIST);
         default:
             return QString("UNKNOWN - %2").arg(id);


=====================================
modules/gui/qt/medialibrary/mlvideofoldersmodel.cpp
=====================================
@@ -0,0 +1,169 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee at omega.gg>
+ *
+ * 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.
+ *****************************************************************************/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "mlvideofoldersmodel.hpp"
+
+// VLC includes
+#include <vlc_media_library.h>
+
+// Util includes
+#include "util/covergenerator.hpp"
+
+// MediaLibrary includes
+#include "mlhelper.hpp"
+#include "mlfolder.hpp"
+
+// Static variables
+
+// NOTE: We multiply by 2 to cover most dpi settings.
+static const int MLVIDEOFOLDERSMODEL_COVER_WIDTH  = 512 * 2; // 16 / 10 ratio
+static const int MLVIDEOFOLDERSMODEL_COVER_HEIGHT = 320 * 2;
+
+static const QHash<QByteArray, vlc_ml_sorting_criteria_t> criterias =
+{
+    { "title",    VLC_ML_SORTING_ALPHA    },
+    { "duration", VLC_ML_SORTING_DURATION }
+};
+
+// Ctor / dtor
+
+/* explicit */ MLVideoFoldersModel::MLVideoFoldersModel(QObject * parent) : MLBaseModel(parent) {}
+
+// MLBaseModel reimplementation
+
+QHash<int, QByteArray> MLVideoFoldersModel::roleNames() const /* override */
+{
+    return {
+        { FOLDER_ID, "id" },
+        { FOLDER_IS_NEW, "isNew" },
+        { FOLDER_TITLE, "title" },
+        { FOLDER_THUMBNAIL, "thumbnail" },
+        { FOLDER_DURATION, "duration" },
+        { FOLDER_PROGRESS, "progress" },
+        { FOLDER_COUNT, "count"},
+    };
+}
+
+// Protected MLVideoModel implementation
+
+QVariant MLVideoFoldersModel::itemRoleData(MLItem * item, const int role) const /* override */
+{
+    if (item == nullptr)
+        return QVariant();
+
+    MLFolder * folder = static_cast<MLFolder *> (item);
+
+    switch (role)
+    {
+        // NOTE: This is the condition for QWidget view(s).
+        case Qt::DisplayRole:
+            return QVariant::fromValue(folder->getTitle());
+        // NOTE: These are the conditions for QML view(s).
+        case FOLDER_ID:
+            return QVariant::fromValue(folder->getId());
+        case FOLDER_TITLE:
+            return QVariant::fromValue(folder->getTitle());
+        case FOLDER_THUMBNAIL:
+            return getVideoListCover(this, folder, MLVIDEOFOLDERSMODEL_COVER_WIDTH,
+                                     MLVIDEOFOLDERSMODEL_COVER_HEIGHT, FOLDER_THUMBNAIL);
+        case FOLDER_DURATION:
+            return QVariant::fromValue(folder->getDuration());
+        case FOLDER_COUNT:
+            return QVariant::fromValue(folder->getCount());
+        default:
+            return QVariant();
+    }
+}
+
+vlc_ml_sorting_criteria_t MLVideoFoldersModel::roleToCriteria(int role) const /* override */
+{
+    switch (role)
+    {
+        case FOLDER_TITLE:
+            return VLC_ML_SORTING_ALPHA;
+        case FOLDER_DURATION:
+            return VLC_ML_SORTING_DURATION;
+        default:
+            return VLC_ML_SORTING_DEFAULT;
+    }
+}
+
+vlc_ml_sorting_criteria_t MLVideoFoldersModel::nameToCriteria(QByteArray name) const /* override */
+{
+    return criterias.value(name, VLC_ML_SORTING_DEFAULT);
+}
+
+QByteArray MLVideoFoldersModel::criteriaToName(vlc_ml_sorting_criteria_t criteria) const
+/* override */
+{
+    return criterias.key(criteria, "");
+}
+
+ListCacheLoader<std::unique_ptr<MLItem>> * MLVideoFoldersModel::createLoader() const /* override */
+{
+    return new Loader(*this);
+}
+
+// Protected MLBaseModel reimplementation
+
+void MLVideoFoldersModel::onVlcMlEvent(const MLEvent & event) /* override */
+{
+    // FIXME: Add support for folder events once the MediaLibrary supports them.
+
+    MLBaseModel::onVlcMlEvent(event);
+}
+
+// Loader
+
+MLVideoFoldersModel::Loader::Loader(const MLVideoFoldersModel & model)
+    : MLBaseModel::BaseLoader(model) {}
+
+size_t MLVideoFoldersModel::Loader::count(vlc_medialibrary_t * ml) const /* override */
+{
+    vlc_ml_query_params_t params = getParams().toCQueryParams();
+
+    return vlc_ml_count_folders_by_type(ml, &params, VLC_ML_MEDIA_TYPE_VIDEO);
+}
+
+std::vector<std::unique_ptr<MLItem>>
+MLVideoFoldersModel::Loader::load(vlc_medialibrary_t * ml,
+                                  size_t index, size_t count) const /* override */
+{
+    vlc_ml_query_params_t params = getParams(index, count).toCQueryParams();
+
+    ml_unique_ptr<vlc_ml_folder_list_t> list(vlc_ml_list_folders_by_type(ml, &params,
+                                                                         VLC_ML_MEDIA_TYPE_VIDEO));
+
+    if (list == nullptr)
+        return {};
+
+    std::vector<std::unique_ptr<MLItem>> result;
+
+    for (const vlc_ml_folder_t & folder : ml_range_iterate<vlc_ml_folder_t>(list))
+    {
+        result.emplace_back(std::make_unique<MLFolder>(&folder));
+    }
+
+    return result;
+}


=====================================
modules/gui/qt/medialibrary/mlvideofoldersmodel.hpp
=====================================
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee at omega.gg>
+ *
+ * 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 MLVIDEOFOLDERSMODEL_HPP
+#define MLVIDEOFOLDERSMODEL_HPP
+
+// MediaLibrary includes
+#include "mlvideomodel.hpp"
+
+// Forward declarations
+class MLFolder;
+
+class MLVideoFoldersModel : public MLBaseModel
+{
+    Q_OBJECT
+
+public:
+    enum Roles
+    {
+        FOLDER_ID = Qt::UserRole + 1,
+        FOLDER_IS_NEW,
+        FOLDER_TITLE,
+        FOLDER_THUMBNAIL,
+        FOLDER_DURATION,
+        FOLDER_PROGRESS,
+        FOLDER_COUNT
+    };
+
+public:
+    explicit MLVideoFoldersModel(QObject * parent = nullptr);
+
+public: // MLBaseModel reimplementation
+    QHash<int, QByteArray> roleNames() const override;
+
+protected:
+    QVariant itemRoleData(MLItem *item, int role = Qt::DisplayRole) 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;
+
+    ListCacheLoader<std::unique_ptr<MLItem>> * createLoader() const override;
+
+protected: // MLBaseModel reimplementation
+    void onVlcMlEvent(const MLEvent & event) override;
+
+private:
+    struct Loader : public BaseLoader
+    {
+        Loader(const MLVideoFoldersModel & model);
+
+        size_t count(vlc_medialibrary_t * ml) const override;
+
+        std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t * ml,
+                                                  size_t index, size_t count) const override;
+    };
+};
+
+#endif // MLVIDEOFOLDERSMODEL_HPP


=====================================
modules/gui/qt/medialibrary/mlgrouplistmodel.cpp → modules/gui/qt/medialibrary/mlvideogroupsmodel.cpp
=====================================
@@ -22,7 +22,7 @@
 # include "config.h"
 #endif
 
-#include "mlgrouplistmodel.hpp"
+#include "mlvideogroupsmodel.hpp"
 
 // VLC includes
 #include <vlc_media_library.h>
@@ -39,8 +39,8 @@
 // Static variables
 
 // NOTE: We multiply by 2 to cover most dpi settings.
-static const int MLGROUPLISTMODEL_COVER_WIDTH  = 512 * 2; // 16 / 10 ratio
-static const int MLGROUPLISTMODEL_COVER_HEIGHT = 320 * 2;
+static const int MLVIDEOGROUPSMODEL_COVER_WIDTH  = 512 * 2; // 16 / 10 ratio
+static const int MLVIDEOGROUPSMODEL_COVER_HEIGHT = 320 * 2;
 
 static const QHash<QByteArray, vlc_ml_sorting_criteria_t> criterias =
 {
@@ -50,43 +50,29 @@ static const QHash<QByteArray, vlc_ml_sorting_criteria_t> criterias =
 };
 
 //=================================================================================================
-// MLGroupListModel
+// MLVideoGroupsModel
 //=================================================================================================
 
-/* explicit */ MLGroupListModel::MLGroupListModel(QObject * parent)
-    : MLBaseModel(parent) {}
+/* explicit */ MLVideoGroupsModel::MLVideoGroupsModel(QObject * parent) : MLVideoModel(parent) {}
 
 //-------------------------------------------------------------------------------------------------
-// QAbstractItemModel implementation
+// MLVideoModel reimplementation
 //-------------------------------------------------------------------------------------------------
 
-QHash<int, QByteArray> MLGroupListModel::roleNames() const /* override */
+QHash<int, QByteArray> MLVideoGroupsModel::roleNames() const /* override */
 {
-    return
-    {
-        { GROUP_IS_VIDEO,           "isVideo"            },
-        { GROUP_ID,                 "id"                 },
-        { GROUP_TITLE,              "title"              },
-        { GROUP_THUMBNAIL,          "thumbnail"          },
-        { GROUP_DURATION,           "duration"           },
-        { GROUP_DATE,               "date"               },
-        { GROUP_COUNT,              "count"              },
-        // NOTE: Media specific.
-        { GROUP_IS_NEW,             "isNew"              },
-        { GROUP_FILENAME,           "fileName"           },
-        { GROUP_PROGRESS,           "progress"           },
-        { GROUP_PLAYCOUNT,          "playcount"          },
-        { GROUP_RESOLUTION,         "resolution_name"    },
-        { GROUP_CHANNEL,            "channel"            },
-        { GROUP_MRL,                "mrl"                },
-        { GROUP_MRL_DISPLAY,        "display_mrl"        },
-        { GROUP_VIDEO_TRACK,        "videoDesc"          },
-        { GROUP_AUDIO_TRACK,        "audioDesc"          },
-        { GROUP_TITLE_FIRST_SYMBOL, "title_first_symbol" }
-    };
+    QHash<int, QByteArray> hash = MLVideoModel::roleNames();
+
+    hash.insert(GROUP_IS_VIDEO, "isVideo");
+    hash.insert(GROUP_DATE,     "date");
+    hash.insert(GROUP_COUNT,    "count");
+
+    return hash;
 }
 
-QVariant MLGroupListModel::itemRoleData(MLItem *item, const int role) const /* override */
+// Protected MLVideoModel implementation
+
+QVariant MLVideoGroupsModel::itemRoleData(MLItem * item, const int role) const /* override */
 {
     if (item == nullptr)
         return QVariant();
@@ -101,16 +87,17 @@ QVariant MLGroupListModel::itemRoleData(MLItem *item, const int role) const /* o
             case Qt::DisplayRole:
                 return QVariant::fromValue(group->getTitle());
             // NOTE: These are the conditions for QML view(s).
-            case GROUP_IS_VIDEO:
-                return false;
-            case GROUP_ID:
+            case VIDEO_ID:
                 return QVariant::fromValue(group->getId());
-            case GROUP_TITLE:
+            case VIDEO_TITLE:
                 return QVariant::fromValue(group->getTitle());
-            case GROUP_THUMBNAIL:
-                return getCover(group);
-            case GROUP_DURATION:
+            case VIDEO_THUMBNAIL:
+                return getVideoListCover(this, group, MLVIDEOGROUPSMODEL_COVER_WIDTH,
+                                         MLVIDEOGROUPSMODEL_COVER_HEIGHT, VIDEO_THUMBNAIL);
+            case VIDEO_DURATION:
                 return QVariant::fromValue(group->getDuration());
+            case GROUP_IS_VIDEO:
+                return false;
             case GROUP_DATE:
                 return QVariant::fromValue(group->getDate());
             case GROUP_COUNT:
@@ -129,66 +116,24 @@ QVariant MLGroupListModel::itemRoleData(MLItem *item, const int role) const /* o
                 return QVariant::fromValue(video->getTitle());
             case GROUP_IS_VIDEO:
                 return true;
-            case GROUP_ID:
-                return QVariant::fromValue(video->getId());
-            case GROUP_TITLE:
-                return QVariant::fromValue(video->getTitle());
-            case GROUP_THUMBNAIL:
-            {
-                vlc_ml_thumbnail_status_t status;
-                const QString thumbnail = video->getThumbnail(&status);
-                if (status == VLC_ML_THUMBNAIL_STATUS_MISSING || status == VLC_ML_THUMBNAIL_STATUS_FAILURE)
-                {
-                    generateVideoThumbnail(item->getId().id);
-                }
-                return QVariant::fromValue( thumbnail );
-            }
-            case GROUP_DURATION:
-                return QVariant::fromValue(video->getDuration());
             case GROUP_DATE:
                 return QVariant();
             case GROUP_COUNT:
                 return 1;
             // NOTE: Media specific.
-            case GROUP_IS_NEW:
-                return QVariant::fromValue(video->isNew());
-            case GROUP_FILENAME:
-                return QVariant::fromValue(video->getFileName());
-            case GROUP_PROGRESS:
-                return QVariant::fromValue(video->getProgress());
-            case GROUP_PLAYCOUNT:
-                return QVariant::fromValue(video->getPlayCount());
-            case GROUP_RESOLUTION:
-                return QVariant::fromValue(video->getResolutionName());
-            case GROUP_CHANNEL:
-                return QVariant::fromValue(video->getChannel());
-            case GROUP_MRL:
-                return QVariant::fromValue(video->getMRL());
-            case GROUP_MRL_DISPLAY:
-                return QVariant::fromValue(video->getDisplayMRL());
-            case GROUP_VIDEO_TRACK:
-                return QVariant::fromValue(video->getVideoDesc());
-            case GROUP_AUDIO_TRACK:
-                return QVariant::fromValue(video->getAudioDesc());
-            case GROUP_TITLE_FIRST_SYMBOL:
-                return QVariant::fromValue(getFirstSymbol(video->getTitle()));
             default:
-                return QVariant();
+                return MLVideoModel::itemRoleData(item, role);
         }
     }
 }
 
-//-------------------------------------------------------------------------------------------------
-// Protected MLBaseModel implementation
-//-------------------------------------------------------------------------------------------------
-
-vlc_ml_sorting_criteria_t MLGroupListModel::roleToCriteria(int role) const /* override */
+vlc_ml_sorting_criteria_t MLVideoGroupsModel::roleToCriteria(int role) const /* override */
 {
     switch (role)
     {
-        case GROUP_TITLE:
+        case VIDEO_TITLE:
             return VLC_ML_SORTING_ALPHA;
-        case GROUP_DURATION:
+        case VIDEO_DURATION:
             return VLC_ML_SORTING_DURATION;
         case GROUP_DATE:
             return VLC_ML_SORTING_INSERTIONDATE;
@@ -197,93 +142,23 @@ vlc_ml_sorting_criteria_t MLGroupListModel::roleToCriteria(int role) const /* ov
     }
 }
 
-vlc_ml_sorting_criteria_t MLGroupListModel::nameToCriteria(QByteArray name) const /* override */
+vlc_ml_sorting_criteria_t MLVideoGroupsModel::nameToCriteria(QByteArray name) const /* override */
 {
     return criterias.value(name, VLC_ML_SORTING_DEFAULT);
 }
 
-QByteArray MLGroupListModel::criteriaToName(vlc_ml_sorting_criteria_t criteria) const
+QByteArray MLVideoGroupsModel::criteriaToName(vlc_ml_sorting_criteria_t criteria) const
 /* override */
 {
     return criterias.key(criteria, "");
 }
 
-//-------------------------------------------------------------------------------------------------
-
-ListCacheLoader<std::unique_ptr<MLItem>> * MLGroupListModel::createLoader() const /* override */
+ListCacheLoader<std::unique_ptr<MLItem>> * MLVideoGroupsModel::createLoader() const /* override */
 {
     return new Loader(*this);
 }
 
-//-------------------------------------------------------------------------------------------------
-// Private functions
-//-------------------------------------------------------------------------------------------------
-
-QString MLGroupListModel::getCover(MLGroup * group) const
-{
-    QString cover = group->getCover();
-
-    // NOTE: Making sure we're not already generating a cover.
-    if (cover.isNull() == false || group->hasGenerator())
-        return cover;
-
-    MLItemId groupId = group->getId();
-    struct Context{
-        QString cover;
-    };
-    group->setGenerator(true);
-    m_mediaLib->runOnMLThread<Context>(this,
-    //ML thread
-    [groupId]
-    (vlc_medialibrary_t* ml, Context& ctx){
-        CoverGenerator generator{ml, groupId};
-
-        generator.setSize(QSize(MLGROUPLISTMODEL_COVER_WIDTH,
-                                 MLGROUPLISTMODEL_COVER_HEIGHT));
-
-        generator.setDefaultThumbnail(":/noart_videoCover.svg");
-
-        if (generator.cachedFileAvailable())
-            ctx.cover = generator.cachedFileURL();
-        else
-            ctx.cover = generator.execute();
-    },
-    //UI Thread
-    [this, groupId]
-    (quint64, Context& ctx)
-    {
-        int row;
-        // NOTE: We want to avoid calling 'MLBaseModel::item' for performance issues.
-        auto group = static_cast<MLGroup*>(findInCache(groupId, &row));
-        if (!group)
-            return;
-
-        group->setCover(ctx.cover);
-        group->setGenerator(false);
-
-        QModelIndex modelIndex =this->index(row);
-        //we're running in a callback
-        emit const_cast<MLGroupListModel*>(this)->dataChanged(modelIndex, modelIndex, { GROUP_THUMBNAIL });
-    });
-
-    return cover;
-}
-
-
-void MLGroupListModel::generateVideoThumbnail(uint64_t id) const
-{
-    m_mediaLib->runOnMLThread(this,
-    //ML thread
-    [id](vlc_medialibrary_t* ml){
-        vlc_ml_media_generate_thumbnail(ml, id, VLC_ML_THUMBNAIL_SMALL, 512, 320, .15 );
-    });
-}
-
-//-------------------------------------------------------------------------------------------------
-// Private MLBaseModel reimplementation
-//-------------------------------------------------------------------------------------------------
-
-void MLGroupListModel::onVlcMlEvent(const MLEvent & event) /* override */
+void MLVideoGroupsModel::onVlcMlEvent(const MLEvent & event) /* override */
 {
     int type = event.i_type;
 
@@ -306,22 +181,14 @@ void MLGroupListModel::onVlcMlEvent(const MLEvent & event) /* override */
     MLBaseModel::onVlcMlEvent(event);
 }
 
-void MLGroupListModel::thumbnailUpdated(const QModelIndex& idx, MLItem* mlitem, const QString& mrl, vlc_ml_thumbnail_status_t status) /* override */
-{
-    auto videoItem = static_cast<MLVideo*>(mlitem);
-    videoItem->setThumbnail(status, mrl);
-    emit dataChanged(idx, idx, { GROUP_THUMBNAIL });
-}
-
-
 //=================================================================================================
 // Loader
 //=================================================================================================
 
-MLGroupListModel::Loader::Loader(const MLGroupListModel & model)
+MLVideoGroupsModel::Loader::Loader(const MLVideoGroupsModel & model)
     : MLBaseModel::BaseLoader(model) {}
 
-size_t MLGroupListModel::Loader::count(vlc_medialibrary_t* ml) const /* override */
+size_t MLVideoGroupsModel::Loader::count(vlc_medialibrary_t* ml) const /* override */
 {
     vlc_ml_query_params_t params = getParams().toCQueryParams();
 
@@ -329,7 +196,8 @@ size_t MLGroupListModel::Loader::count(vlc_medialibrary_t* ml) const /* override
 }
 
 std::vector<std::unique_ptr<MLItem>>
-MLGroupListModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count) const /* override */
+MLVideoGroupsModel::Loader::load(vlc_medialibrary_t* ml,
+                                 size_t index, size_t count) const /* override */
 {
     vlc_ml_query_params_t params = getParams(index, count).toCQueryParams();
 


=====================================
modules/gui/qt/medialibrary/mlgrouplistmodel.hpp → modules/gui/qt/medialibrary/mlvideogroupsmodel.hpp
=====================================
@@ -18,53 +18,35 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
-#ifndef MLGROUPLISTMODEL_HPP
-#define MLGROUPLISTMODEL_HPP
+#ifndef MLVIDEOGROUPSMODEL_HPP
+#define MLVIDEOGROUPSMODEL_HPP
 
 // MediaLibrary includes
-#include "mlbasemodel.hpp"
+#include "mlvideomodel.hpp"
 
 // Forward declarations
-struct vlc_medialibrary_t;
 class MLGroup;
 
-class MLGroupListModel : public MLBaseModel
+class MLVideoGroupsModel : public MLVideoModel
 {
     Q_OBJECT
 
 public:
     enum Roles
     {
-        GROUP_IS_VIDEO = Qt::UserRole + 1,
-        GROUP_ID,
-        GROUP_TITLE,
-        GROUP_THUMBNAIL,
-        GROUP_DURATION,
+        // NOTE: Group specific.
+        GROUP_IS_VIDEO = VIDEO_TITLE_FIRST_SYMBOL + 1,
         GROUP_DATE,
-        GROUP_COUNT,
-        // NOTE: Media specific.
-        GROUP_IS_NEW,
-        GROUP_FILENAME,
-        GROUP_PROGRESS,
-        GROUP_PLAYCOUNT,
-        GROUP_RESOLUTION,
-        GROUP_CHANNEL,
-        GROUP_MRL,
-        GROUP_MRL_DISPLAY,
-        GROUP_VIDEO_TRACK,
-        GROUP_AUDIO_TRACK,
-
-        GROUP_TITLE_FIRST_SYMBOL
+        GROUP_COUNT
     };
 
 public:
-    explicit MLGroupListModel(QObject * parent = nullptr);
+    explicit MLVideoGroupsModel(QObject * parent = nullptr);
 
-
-public: // QAbstractItemModel implementation
+public: // MLVideoModel reimplementation
     QHash<int, QByteArray> roleNames() const override;
 
-protected: // MLBaseModel implementation
+protected: // MLVideoModel reimplementation
     QVariant itemRoleData(MLItem *item, int role = Qt::DisplayRole) const override;
 
     vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
@@ -75,20 +57,12 @@ protected: // MLBaseModel implementation
 
     ListCacheLoader<std::unique_ptr<MLItem>> * createLoader() const override;
 
-    void thumbnailUpdated(const QModelIndex& idx, MLItem* mlitem, const QString& mrl, vlc_ml_thumbnail_status_t status) override;
-
-private: // Functions
-    QString getCover(MLGroup * group) const;
-
-private: // MLBaseModel implementation
     void onVlcMlEvent(const MLEvent & event) override;
 
-    void generateVideoThumbnail(uint64_t id) const;
-
 private:
-    struct Loader : public MLBaseModel::BaseLoader
+    struct Loader : public BaseLoader
     {
-        Loader(const MLGroupListModel & model);
+        Loader(const MLVideoGroupsModel & model);
 
         size_t count(vlc_medialibrary_t* ml) const override;
 
@@ -96,4 +70,4 @@ private:
     };
 };
 
-#endif // MLGROUPLISTMODEL_HPP
+#endif // MLVIDEOGROUPSMODEL_HPP


=====================================
modules/gui/qt/medialibrary/mlvideomodel.cpp
=====================================
@@ -188,7 +188,7 @@ size_t MLVideoModel::Loader::count(vlc_medialibrary_t* ml) const /* override */
     if (id <= 0)
         return vlc_ml_count_video_media(ml, &params);
     else
-        return vlc_ml_count_media_of(ml, &params, m_parent.type, id);
+        return vlc_ml_count_video_of(ml, &params, m_parent.type, id);
 }
 
 std::vector<std::unique_ptr<MLItem>>
@@ -203,7 +203,7 @@ MLVideoModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count) c
     if (id <= 0)
         list.reset(vlc_ml_list_video_media(ml, &params));
     else
-        list.reset(vlc_ml_list_media_of(ml, &params, m_parent.type, id));
+        list.reset(vlc_ml_list_video_of(ml, &params, m_parent.type, id));
 
     if (list == nullptr)
         return {};


=====================================
modules/gui/qt/medialibrary/qml/MediaGroupDisplay.qml
=====================================
@@ -32,7 +32,7 @@ VideoAll {
 
     property int      initialIndex: 0
     property MLItemId initialId
-    property string   initialName
+    property string   initialTitle
 
     // Aliases
 


=====================================
modules/gui/qt/medialibrary/qml/VideoAllSubDisplay.qml
=====================================
@@ -40,8 +40,16 @@ VideoAll {
 
     // Private
 
-    property var _meta: (MainCtx.grouping === MainCtx.GROUPING_NONE) ? metaVideo
-                                                                     : metaGroup
+    property var _meta: {
+        var grouping = MainCtx.grouping;
+
+        if (grouping === MainCtx.GROUPING_NONE)
+            return metaVideo
+        else if (grouping === MainCtx.GROUPING_NAME)
+            return metaGroup
+        else if (grouping === MainCtx.GROUPING_FOLDER)
+            return metaFolder
+    }
 
     // Signals
 
@@ -114,9 +122,9 @@ VideoAll {
     QtObject {
         id: metaGroup
 
-        property var model: MLGroupListModel { ml: MediaLib }
+        property var model: MLVideoGroupsModel { ml: MediaLib }
 
-        property var contextMenu: GroupListContextMenu { model: metaGroup.model }
+        property var contextMenu: VideoGroupsContextMenu { model: metaGroup.model }
 
         function onAction(indexes) {
             var index = indexes[0]
@@ -153,6 +161,32 @@ VideoAll {
         }
     }
 
+    QtObject {
+        id: metaFolder
+
+        property var model: MLVideoFoldersModel { ml: MediaLib }
+
+        property var contextMenu: VideoFoldersContextMenu { model: metaFolder.model }
+
+        function onAction(indexes) {
+            var index = indexes[0]
+
+            root.showList(model.getDataAt(index), Qt.TabFocusReason)
+        }
+
+        function onDoubleClick(object) {
+            root.showList(object, Qt.MouseFocusReason)
+        }
+
+        function onLabelGrid(object) {
+            return root.getLabelGroup(object, I18n.qtr("%1 Videos"))
+        }
+
+        function onLabelList(object) {
+            return root.getLabelGroup(object, I18n.qtr("%1"))
+        }
+    }
+
     // Children
 
     MLRecentsVideoModel {


=====================================
modules/gui/qt/menus/qml_menu_wrapper.cpp
=====================================
@@ -19,7 +19,8 @@
 #include "menus.hpp"
 #include "medialibrary/medialib.hpp"
 #include "medialibrary/mlvideomodel.hpp"
-#include "medialibrary/mlgrouplistmodel.hpp"
+#include "medialibrary/mlvideogroupsmodel.hpp"
+#include "medialibrary/mlvideofoldersmodel.hpp"
 #include "medialibrary/mlplaylistlistmodel.hpp"
 #include "medialibrary/mlplaylistmodel.hpp"
 #include "medialibrary/mlalbummodel.hpp"
@@ -162,6 +163,7 @@ void SortMenuVideo::onPopup(QMenu * menu) /* override */
     {
         { N_("Do not group videos"), MainCtx::GROUPING_NONE },
         { N_("Group by name"), MainCtx::GROUPING_NAME },
+        { N_("Group by folder"), MainCtx::GROUPING_FOLDER },
     };
 
     QActionGroup * group = new QActionGroup(this);
@@ -542,18 +544,19 @@ void VideoContextMenu::popup(const QModelIndexList& selected, QPoint pos, QVaria
 }
 
 //=================================================================================================
-// GroupListContextMenu
+// VideoGroupsContextMenu
 //=================================================================================================
 
-GroupListContextMenu::GroupListContextMenu(QObject * parent) : QObject(parent) {}
+VideoGroupsContextMenu::VideoGroupsContextMenu(QObject * parent) : QObject(parent) {}
 
-GroupListContextMenu::~GroupListContextMenu() /* override */
+VideoGroupsContextMenu::~VideoGroupsContextMenu() /* override */
 {
     if (m_menu)
         delete m_menu;
 }
 
-void GroupListContextMenu::popup(const QModelIndexList & selected, QPoint pos, QVariantMap options)
+void VideoGroupsContextMenu::popup(const QModelIndexList & selected, QPoint pos,
+                                   QVariantMap options)
 {
     if (m_model == nullptr)
         return;
@@ -564,7 +567,7 @@ void GroupListContextMenu::popup(const QModelIndexList & selected, QPoint pos, Q
     QVariantList ids;
 
     for (const QModelIndex & index : selected)
-        ids.push_back(m_model->data(index, MLGroupListModel::GROUP_ID));
+        ids.push_back(m_model->data(index, MLVideoModel::VIDEO_ID));
 
     m_menu = new QMenu();
 
@@ -605,7 +608,7 @@ void GroupListContextMenu::popup(const QModelIndexList & selected, QPoint pos, Q
     // NOTE: At the moment informations are only available for single video(s).
     if (selected.count() == 1
         &&
-        m_model->data(selected.first(), MLGroupListModel::GROUP_IS_VIDEO) == true
+        m_model->data(selected.first(), MLVideoGroupsModel::GROUP_IS_VIDEO) == true
         &&
         options.contains("information") && options["information"].type() == QVariant::Int)
     {
@@ -619,16 +622,79 @@ void GroupListContextMenu::popup(const QModelIndexList & selected, QPoint pos, Q
 
 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
         connect(mapper, &QSignalMapper::mappedInt,
-                this, &GroupListContextMenu::showMediaInformation);
+                this, &VideoGroupsContextMenu::showMediaInformation);
 #else
         connect(mapper, QOverload<int>::of(&QSignalMapper::mapped),
-                this, &GroupListContextMenu::showMediaInformation);
+                this, &VideoGroupsContextMenu::showMediaInformation);
 #endif
     }
 
     m_menu->popup(pos);
 }
 
+// VideoFoldersContextMenu
+
+VideoFoldersContextMenu::VideoFoldersContextMenu(QObject * parent) : QObject(parent) {}
+
+VideoFoldersContextMenu::~VideoFoldersContextMenu() /* override */
+{
+    if (m_menu)
+        delete m_menu;
+}
+
+void VideoFoldersContextMenu::popup(const QModelIndexList & selected, QPoint pos,
+                                    QVariantMap options)
+{
+    if (m_model == nullptr)
+        return;
+
+    if (m_menu)
+        delete m_menu;
+
+    QVariantList ids;
+
+    for (const QModelIndex & index : selected)
+        ids.push_back(m_model->data(index, MLVideoFoldersModel::FOLDER_ID));
+
+    m_menu = new QMenu();
+
+    MediaLib * ml = m_model->ml();
+
+    QAction * action = m_menu->addAction(qtr("Add and play"));
+
+    connect(action, &QAction::triggered, [ml, ids, options]()
+    {
+        ml->addAndPlay(ids, options["player-options"].toStringList());
+    });
+
+    action = m_menu->addAction(qtr("Enqueue"));
+
+    connect(action, &QAction::triggered, [ml, ids]()
+    {
+        ml->addToPlaylist(ids);
+    });
+
+    action = m_menu->addAction(qtr("Add to playlist"));
+
+    connect(action, &QAction::triggered, [ids]()
+    {
+        DialogsProvider::getInstance()->playlistsDialog(ids);
+    });
+
+    action = m_menu->addAction(qtr("Play as audio"));
+
+    connect(action, &QAction::triggered, [ml, ids, options]()
+    {
+        QStringList list = options["player-options"].toStringList();
+
+        list.prepend(":no-video");
+
+        ml->addAndPlay(ids, list);
+    });
+
+    m_menu->popup(pos);
+}
+
 //=================================================================================================
 // PlaylistListContextMenu
 //=================================================================================================


=====================================
modules/gui/qt/menus/qml_menu_wrapper.hpp
=====================================
@@ -33,8 +33,8 @@ class MLArtistModel;
 class MLAlbumTrackModel;
 class MLUrlModel;
 class MLVideoModel;
-class MLGroupListModel;
-class MLGroupModel;
+class MLVideoGroupsModel;
+class MLVideoFoldersModel;
 class MLPlaylistListModel;
 class MLPlaylistModel;
 class NetworkDeviceModel;
@@ -278,15 +278,15 @@ private:
 // Groups
 //-------------------------------------------------------------------------------------------------
 
-class GroupListContextMenu : public QObject {
+class VideoGroupsContextMenu : public QObject {
     Q_OBJECT
 
-    SIMPLE_MENU_PROPERTY(MLGroupListModel *, model, nullptr)
+    SIMPLE_MENU_PROPERTY(MLVideoGroupsModel *, model, nullptr)
 
 public:
-    GroupListContextMenu(QObject * parent = nullptr);
+    VideoGroupsContextMenu(QObject * parent = nullptr);
 
-    ~GroupListContextMenu() /* override */;
+    ~VideoGroupsContextMenu(); /* override */
 
 public slots:
     void popup(const QModelIndexList & selected, QPoint pos, QVariantMap options = {});
@@ -298,6 +298,29 @@ private:
     QMenu * m_menu = nullptr;
 };
 
+// Folders
+
+class VideoFoldersContextMenu : public QObject {
+    Q_OBJECT
+
+    SIMPLE_MENU_PROPERTY(MLVideoFoldersModel *, model, nullptr)
+
+public:
+    VideoFoldersContextMenu(QObject * parent = nullptr);
+
+    ~VideoFoldersContextMenu(); /* override */
+
+public slots:
+    void popup(const QModelIndexList & selected, QPoint pos, QVariantMap options = {});
+
+signals:
+    // FIXME: This signal is required for VideoAll Connections.
+    void showMediaInformation(int index);
+
+private:
+    QMenu * m_menu = nullptr;
+};
+
 //-------------------------------------------------------------------------------------------------
 
 class PlaylistListContextMenu : public QObject {


=====================================
modules/gui/qt/util/covergenerator.cpp
=====================================
@@ -357,6 +357,8 @@ QString CoverGenerator::getPrefix(vlc_ml_parent_type type) const
             return "genre";
         case VLC_ML_PARENT_GROUP:
             return "group";
+        case VLC_ML_PARENT_FOLDER:
+            return "folder";
         case VLC_ML_PARENT_PLAYLIST:
             return "playlist";
         default:


=====================================
modules/misc/medialibrary/entities.cpp
=====================================
@@ -455,18 +455,28 @@ bool Convert( const medialibrary::IPlaylist* input, vlc_ml_playlist_t& output )
 bool Convert( const medialibrary::IFolder* input, vlc_ml_folder_t& output )
 {
     output.i_id = input->id();
+
+    output.i_nb_media = input->nbMedia();
+
     output.b_banned = input->isBanned();
+
+    if ( strdup_helper( input->name(), output.psz_name ) == false )
+        return false;
+
     try
     {
-        if ( strdup_helper( input->mrl(), output.psz_mrl ) == false )
+        if (strdup_helper(input->mrl(), output.psz_mrl) == false)
             return false;
+
         output.b_present = true;
     }
     catch ( const medialibrary::fs::errors::DeviceRemoved& )
     {
         output.psz_mrl = nullptr;
+
         output.b_present = false;
     }
+
     return true;
 }
 


=====================================
modules/misc/medialibrary/medialibrary.cpp
=====================================
@@ -740,10 +740,39 @@ int MediaLibrary::List( int listQuery, const vlc_ml_query_params_t* params, va_l
         psz_pattern = params->psz_pattern;
         paramsPtr = &p;
     }
+
+    medialibrary::IMedia::Type type = medialibrary::IMedia::Type::Unknown;
+
+    switch ( listQuery )
+    {
+        case VLC_ML_LIST_MEDIA_OF:
+        case VLC_ML_COUNT_MEDIA_OF:
+            // N/A default value
+            break;
+        case VLC_ML_LIST_VIDEO_OF:
+        case VLC_ML_COUNT_VIDEO_OF:
+            type = medialibrary::IMedia::Type::Video;
+            break;
+        case VLC_ML_LIST_AUDIO_OF:
+        case VLC_ML_COUNT_AUDIO_OF:
+            type = medialibrary::IMedia::Type::Audio;
+            break;
+        case VLC_ML_LIST_FOLDERS_BY_TYPE:
+        case VLC_ML_COUNT_FOLDERS_BY_TYPE:
+            type = static_cast<medialibrary::IMedia::Type>(va_arg(args, int));
+            break;
+        default:
+            break;
+    }
+
     switch ( listQuery )
     {
         case VLC_ML_LIST_MEDIA_OF:
         case VLC_ML_COUNT_MEDIA_OF:
+        case VLC_ML_LIST_VIDEO_OF:
+        case VLC_ML_COUNT_VIDEO_OF:
+        case VLC_ML_LIST_AUDIO_OF:
+        case VLC_ML_COUNT_AUDIO_OF:
         case VLC_ML_LIST_ARTISTS_OF:
         case VLC_ML_COUNT_ARTISTS_OF:
         case VLC_ML_LIST_ALBUMS_OF:
@@ -755,6 +784,7 @@ int MediaLibrary::List( int listQuery, const vlc_ml_query_params_t* params, va_l
         default:
             break;
     }
+
     switch( listQuery )
     {
         case VLC_ML_LIST_ALBUM_TRACKS:
@@ -971,7 +1001,14 @@ int MediaLibrary::List( int listQuery, const vlc_ml_query_params_t* params, va_l
         case VLC_ML_COUNT_GROUPS:
         case VLC_ML_LIST_GROUP_MEDIA:
         case VLC_ML_COUNT_GROUP_MEDIA:
-            return listGroup( listQuery, paramsPtr, psz_pattern, nbItems, offset, args );
+            return listGroup( listQuery, paramsPtr, type, psz_pattern, nbItems, offset, args );
+        case VLC_ML_LIST_FOLDERS:
+        case VLC_ML_COUNT_FOLDERS:
+        case VLC_ML_LIST_FOLDERS_BY_TYPE:
+        case VLC_ML_COUNT_FOLDERS_BY_TYPE:
+        case VLC_ML_LIST_FOLDER_MEDIA:
+        case VLC_ML_COUNT_FOLDER_MEDIA:
+            return listFolder( listQuery, paramsPtr, type, psz_pattern, nbItems, offset, args );
         case VLC_ML_LIST_PLAYLIST_MEDIA:
         case VLC_ML_COUNT_PLAYLIST_MEDIA:
         case VLC_ML_LIST_PLAYLISTS:
@@ -1067,42 +1104,6 @@ int MediaLibrary::List( int listQuery, const vlc_ml_query_params_t* params, va_l
             *( va_arg( args, size_t* ) ) = query == nullptr ? 0 : query->count();
             break;
         }
-        case VLC_ML_LIST_FOLDERS:
-        {
-            const auto query = m_ml->folders( medialibrary::IMedia::Type::Unknown, paramsPtr );
-            if ( query == nullptr )
-                return VLC_EGENERIC;
-            auto* res = ml_convert_list<vlc_ml_folder_list_t, vlc_ml_folder_t>( query->all() );
-            *( va_arg( args, vlc_ml_folder_list_t** ) ) = res;
-            break;
-        }
-        case VLC_ML_COUNT_FOLDERS:
-        {
-            const auto query = m_ml->folders( medialibrary::IMedia::Type::Unknown, paramsPtr );
-            *( va_arg( args, size_t* ) ) = query == nullptr ? 0 : query->count();
-            break;
-        }
-        case VLC_ML_LIST_FOLDER_MEDIAS:
-        {
-            const auto folder = m_ml->folder( va_arg( args, int64_t ) );
-            if ( folder == nullptr )
-                return VLC_EGENERIC;
-            const auto query = folder->media( medialibrary::IMedia::Type::Unknown, paramsPtr );
-            if ( query == nullptr )
-                return VLC_EGENERIC;
-            auto* res = ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>( query->all() );
-            *( va_arg( args, vlc_ml_media_list_t** ) ) = res;
-            break;
-        }
-        case VLC_ML_COUNT_FOLDER_MEDIAS:
-        {
-            const auto folder = m_ml->folder( va_arg( args, int64_t ) );
-            if ( folder == nullptr )
-                return VLC_EGENERIC;
-            const auto query = folder->media( medialibrary::IMedia::Type::Unknown, paramsPtr );
-            *( va_arg( args, size_t* ) ) = query == nullptr ? 0 : query->count();
-            break;
-        }
     }
     return VLC_SUCCESS;
 }
@@ -1506,6 +1507,8 @@ int MediaLibrary::filterListChildrenQuery( int query, int parentType )
     switch( query )
     {
         case VLC_ML_LIST_MEDIA_OF:
+        case VLC_ML_LIST_VIDEO_OF:
+        case VLC_ML_LIST_AUDIO_OF:
             switch ( parentType )
             {
                 case VLC_ML_PARENT_ALBUM:
@@ -1518,12 +1521,16 @@ int MediaLibrary::filterListChildrenQuery( int query, int parentType )
                     return VLC_ML_LIST_GENRE_TRACKS;
                 case VLC_ML_PARENT_GROUP:
                     return VLC_ML_LIST_GROUP_MEDIA;
+                case VLC_ML_PARENT_FOLDER:
+                    return VLC_ML_LIST_FOLDER_MEDIA;
                 case VLC_ML_PARENT_PLAYLIST:
                     return VLC_ML_LIST_PLAYLIST_MEDIA;
                 default:
                     vlc_assert_unreachable();
             }
         case VLC_ML_COUNT_MEDIA_OF:
+        case VLC_ML_COUNT_VIDEO_OF:
+        case VLC_ML_COUNT_AUDIO_OF:
             switch ( parentType )
             {
                 case VLC_ML_PARENT_ALBUM:
@@ -1536,6 +1543,8 @@ int MediaLibrary::filterListChildrenQuery( int query, int parentType )
                     return VLC_ML_COUNT_GENRE_TRACKS;
                 case VLC_ML_PARENT_GROUP:
                     return VLC_ML_COUNT_GROUP_MEDIA;
+                case VLC_ML_PARENT_FOLDER:
+                    return VLC_ML_COUNT_FOLDER_MEDIA;
                 case VLC_ML_PARENT_PLAYLIST:
                     return VLC_ML_COUNT_PLAYLIST_MEDIA;
                 default:
@@ -1792,7 +1801,8 @@ int MediaLibrary::listGenre( int listQuery, const medialibrary::QueryParameters*
 }
 
 int MediaLibrary::listGroup( int listQuery, const medialibrary::QueryParameters* paramsPtr,
-                             const char* pattern, uint32_t nbItems, uint32_t offset, va_list args )
+                             medialibrary::IMedia::Type type, const char* pattern,
+                             uint32_t nbItems, uint32_t offset, va_list args )
 {
     switch( listQuery )
     {
@@ -1804,7 +1814,7 @@ int MediaLibrary::listGroup( int listQuery, const medialibrary::QueryParameters*
             if ( pattern )
                 query = m_ml->searchMediaGroups( pattern, paramsPtr );
             else
-                query = m_ml->mediaGroups( medialibrary::IMedia::Type::Unknown, paramsPtr );
+                query = m_ml->mediaGroups( type, paramsPtr );
 
             if ( query == nullptr )
                 return VLC_EGENERIC;
@@ -1837,10 +1847,9 @@ int MediaLibrary::listGroup( int listQuery, const medialibrary::QueryParameters*
             medialibrary::Query<medialibrary::IMedia> query;
 
             if ( pattern )
-                query = group->searchMedia( pattern, medialibrary::IMedia::Type::Unknown,
-                                            paramsPtr );
+                query = group->searchMedia( pattern, type, paramsPtr );
             else
-                query = group->media( medialibrary::IMedia::Type::Unknown, paramsPtr );
+                query = group->media( type, paramsPtr );
 
             if ( query == nullptr )
                 return VLC_EGENERIC;
@@ -1867,6 +1876,86 @@ int MediaLibrary::listGroup( int listQuery, const medialibrary::QueryParameters*
     }
 }
 
+int MediaLibrary::listFolder(int listQuery, const medialibrary::QueryParameters * paramsPtr,
+                             medialibrary::IMedia::Type type, const char * pattern,
+                             uint32_t nbItems, uint32_t offset, va_list args)
+{
+    switch (listQuery)
+    {
+        case VLC_ML_LIST_FOLDERS:
+        case VLC_ML_COUNT_FOLDERS:
+        case VLC_ML_LIST_FOLDERS_BY_TYPE:
+        case VLC_ML_COUNT_FOLDERS_BY_TYPE:
+        {
+            medialibrary::Query<medialibrary::IFolder> query;
+
+            if (pattern)
+                query = m_ml->searchFolders(pattern, type, paramsPtr);
+            else
+                query = m_ml->folders(type, paramsPtr);
+
+            if (query == nullptr)
+                return VLC_EGENERIC;
+
+            switch (listQuery)
+            {
+                case VLC_ML_LIST_FOLDERS:
+                case VLC_ML_LIST_FOLDERS_BY_TYPE:
+                    *va_arg(args, vlc_ml_folder_list_t **) =
+                        ml_convert_list<vlc_ml_folder_list_t, vlc_ml_folder_t>
+                        (query->items(nbItems, offset));
+                    return VLC_SUCCESS;
+
+                case VLC_ML_COUNT_FOLDERS:
+                case VLC_ML_COUNT_FOLDERS_BY_TYPE:
+                    *va_arg(args, size_t *) = query->count();
+                    return VLC_SUCCESS;
+
+                default:
+                    vlc_assert_unreachable();
+            }
+        }
+
+        case VLC_ML_LIST_FOLDER_MEDIA:
+        case VLC_ML_COUNT_FOLDER_MEDIA:
+        {
+            medialibrary::FolderPtr folder = m_ml->folder(va_arg(args, int64_t));
+
+            if (folder == nullptr)
+                return VLC_EGENERIC;
+
+            medialibrary::Query<medialibrary::IMedia> query;
+
+            if (pattern)
+                query = folder->searchMedia(pattern, type, paramsPtr);
+            else
+                query = folder->media(type, paramsPtr);
+
+            if (query == nullptr)
+                return VLC_EGENERIC;
+
+            switch (listQuery)
+            {
+                case VLC_ML_LIST_FOLDER_MEDIA:
+                    *va_arg(args, vlc_ml_media_list_t **) =
+                        ml_convert_list<vlc_ml_media_list_t, vlc_ml_media_t>
+                        (query->items(nbItems, offset));
+                    return VLC_SUCCESS;
+
+                case VLC_ML_COUNT_FOLDER_MEDIA:
+                    *va_arg(args, size_t *) = query->count();
+                    return VLC_SUCCESS;
+
+                default:
+                    vlc_assert_unreachable();
+            }
+        }
+
+        default:
+            vlc_assert_unreachable();
+    }
+}
+
 int MediaLibrary::listPlaylist( int listQuery, const medialibrary::QueryParameters* paramsPtr,
                                 const char* pattern, uint32_t nbItems, uint32_t offset, va_list args )
 {


=====================================
modules/misc/medialibrary/medialibrary.h
=====================================
@@ -170,7 +170,11 @@ private:
     int listGenre( int listQuery, const medialibrary::QueryParameters* paramsPtr,
                    const char* pattern, uint32_t nbItems, uint32_t offset, va_list args );
     int listGroup( int listQuery, const medialibrary::QueryParameters* paramsPtr,
-                   const char* pattern, uint32_t nbItems, uint32_t offset, va_list args );
+                   medialibrary::IMedia::Type type, const char* pattern, uint32_t nbItems,
+                   uint32_t offset, va_list args );
+    int listFolder( int listQuery, const medialibrary::QueryParameters* paramsPtr,
+                    medialibrary::IMedia::Type type, const char* pattern, uint32_t nbItems,
+                    uint32_t offset, va_list args );
     int listPlaylist( int listQuery, const medialibrary::QueryParameters* paramsPtr,
                       const char* pattern, uint32_t nbItems, uint32_t offset, va_list args );
     int listMedia( int listQuery, const medialibrary::QueryParameters* paramsPtr,


=====================================
po/POTFILES.in
=====================================
@@ -770,12 +770,12 @@ modules/gui/qt/dialogs/plugins/addons_manager.cpp
 modules/gui/qt/dialogs/plugins/addons_manager.hpp
 modules/gui/qt/medialibrary/mlbookmarkmodel.cpp
 modules/gui/qt/medialibrary/mlbookmarkmodel.hpp
+modules/gui/qt/medialibrary/mlfolder.cpp
+modules/gui/qt/medialibrary/mlfolder.hpp
 modules/gui/qt/medialibrary/mlfoldersmodel.cpp
 modules/gui/qt/medialibrary/mlfoldersmodel.hpp
 modules/gui/qt/medialibrary/mlgroup.cpp
 modules/gui/qt/medialibrary/mlgroup.hpp
-modules/gui/qt/medialibrary/mlgrouplistmodel.cpp
-modules/gui/qt/medialibrary/mlgrouplistmodel.hpp
 modules/gui/qt/medialibrary/mlitemcover.cpp
 modules/gui/qt/medialibrary/mlitemcover.hpp
 modules/gui/qt/medialibrary/mlplaylistlistmodel.cpp
@@ -784,6 +784,10 @@ modules/gui/qt/medialibrary/mlplaylistmedia.cpp
 modules/gui/qt/medialibrary/mlplaylistmedia.hpp
 modules/gui/qt/medialibrary/mlplaylistmodel.cpp
 modules/gui/qt/medialibrary/mlplaylistmodel.hpp
+modules/gui/qt/medialibrary/mlvideofoldersmodel.cpp
+modules/gui/qt/medialibrary/mlvideofoldersmodel.hpp
+modules/gui/qt/medialibrary/mlvideogroupsmodel.cpp
+modules/gui/qt/medialibrary/mlvideogroupsmodel.hpp
 modules/gui/qt/menus/menus.cpp
 modules/gui/qt/menus/menus.hpp
 modules/gui/qt/qt.cpp



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/21931db0aa9b2771851bc5bb397b99b839be2ba2...6fb0c5f38a18b9417c759fd36762b4de9fae5194

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/21931db0aa9b2771851bc5bb397b99b839be2ba2...6fb0c5f38a18b9417c759fd36762b4de9fae5194
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