diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 32a4c3de7e3fee8bfa656f2a230551f16ba692a2..3a0b6fa3c77b51a8f3f7704375cd29ab5022c1dc 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -191,6 +191,8 @@ 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 \
@@ -403,6 +405,7 @@ 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 \
diff --git a/modules/gui/qt/maininterface/mainui.cpp b/modules/gui/qt/maininterface/mainui.cpp
index 874f25a254fde890b603b361403377769c2f8aa2..8d426df7dc343877948b2442c6b1fa7846a6b44e 100644
--- a/modules/gui/qt/maininterface/mainui.cpp
+++ b/modules/gui/qt/maininterface/mainui.cpp
@@ -14,6 +14,7 @@
 #include "medialibrary/mlrecentsvideomodel.hpp"
 #include "medialibrary/mlfoldersmodel.hpp"
 #include "medialibrary/mlvideogroupsmodel.hpp"
+#include "medialibrary/mlvideofoldersmodel.hpp"
 #include "medialibrary/mlplaylistlistmodel.hpp"
 #include "medialibrary/mlplaylistmodel.hpp"
 #include "medialibrary/mlplaylist.hpp"
@@ -325,6 +326,7 @@ void MainUI::registerQMLTypes()
         qmlRegisterType<MLVideoModel>( uri, versionMajor, versionMinor, "MLVideoModel" );
         qmlRegisterType<MLRecentsVideoModel>( uri, versionMajor, versionMinor, "MLRecentsVideoModel" );
         qmlRegisterType<MLVideoGroupsModel>( uri, versionMajor, versionMinor, "MLVideoGroupsModel" );
+        qmlRegisterType<MLVideoFoldersModel>( uri, versionMajor, versionMinor, "MLVideoFoldersModel" );
         qmlRegisterType<MLPlaylistListModel>( uri, versionMajor, versionMinor, "MLPlaylistListModel" );
         qmlRegisterType<MLPlaylistModel>( uri, versionMajor, versionMinor, "MLPlaylistModel" );
 
diff --git a/modules/gui/qt/medialibrary/mlvideofoldersmodel.cpp b/modules/gui/qt/medialibrary/mlvideofoldersmodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..31e099d6b168ead0aeea6383a62cf75738975f20
--- /dev/null
+++ b/modules/gui/qt/medialibrary/mlvideofoldersmodel.cpp
@@ -0,0 +1,169 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee@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;
+}
diff --git a/modules/gui/qt/medialibrary/mlvideofoldersmodel.hpp b/modules/gui/qt/medialibrary/mlvideofoldersmodel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dd968776ceb44e62820702c0da6644057a733433
--- /dev/null
+++ b/modules/gui/qt/medialibrary/mlvideofoldersmodel.hpp
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee@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
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9080dc30ecc80a8d33131591a893da3e486e36a9..3a6d10f58495e93bc7e05b8597982ae71b07d1dd 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -784,6 +784,8 @@ 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