[vlc-commits] [Git][videolan/vlc][master] 9 commits: medialibrary: Add nb_media and nb_present_media for playlists
Jean-Baptiste Kempf
gitlab at videolan.org
Thu May 20 11:35:28 UTC 2021
Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits:
9e43f884 by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
medialibrary: Add nb_media and nb_present_media for playlists
- - - - -
56cc0c9c by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
qt/covergenerator: Add the prefix property
This lets us enforce a specific prefix for the cover fileName.
- - - - -
6d98cbe4 by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
qt/covergenerator: Add better svg support and QImageReader implementation
- - - - -
27eb7756 by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
qt/covergenerator: Improve Divide and count(s) implementation
- - - - -
b23998a5 by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
qt/mlplaylist: Add CoverGenerator support and proper duration
- - - - -
6b7a873f by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
qt/mlplaylistlistmodel: Add cover generation and duration support
- - - - -
54c0ecc8 by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
qt: Create MLItemCover
This class allows us to avoid duplicates for the CoverGenerator and the cover property.
- - - - -
42f964bd by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
qml/PlaylistMediaList: Revamp implementation based on design guidelines
- - - - -
f235d8ff by Benjamin Arnaud at 2021-05-20T11:15:31+00:00
qml/MusicPlaylistsDisplay: Revamp implementation based on design guidelines
- - - - -
19 changed files:
- include/vlc_media_library.h
- modules/gui/qt/Makefile.am
- modules/gui/qt/maininterface/mainui.cpp
- modules/gui/qt/medialibrary/mlgenre.cpp
- modules/gui/qt/medialibrary/mlgenre.hpp
- modules/gui/qt/medialibrary/mlgroup.cpp
- modules/gui/qt/medialibrary/mlgroup.hpp
- + modules/gui/qt/medialibrary/mlitemcover.cpp
- + modules/gui/qt/medialibrary/mlitemcover.hpp
- modules/gui/qt/medialibrary/mlplaylist.cpp
- modules/gui/qt/medialibrary/mlplaylist.hpp
- modules/gui/qt/medialibrary/mlplaylistlistmodel.cpp
- modules/gui/qt/medialibrary/mlplaylistlistmodel.hpp
- modules/gui/qt/medialibrary/qml/MusicPlaylistsDisplay.qml
- modules/gui/qt/medialibrary/qml/PlaylistMediaList.qml
- modules/gui/qt/util/covergenerator.cpp
- modules/gui/qt/util/covergenerator.hpp
- modules/misc/medialibrary/entities.cpp
- po/POTFILES.in
Changes:
=====================================
include/vlc_media_library.h
=====================================
@@ -251,6 +251,9 @@ typedef struct vlc_ml_playlist_t
char* psz_artwork_mrl;
+ unsigned int i_nb_media;
+ unsigned int i_nb_present_media;
+
uint32_t i_creation_date;
bool b_is_read_only;
=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -163,6 +163,8 @@ libqt_plugin_la_SOURCES = \
gui/qt/medialibrary/mlgrouplistmodel.hpp \
gui/qt/medialibrary/mlhelper.cpp \
gui/qt/medialibrary/mlhelper.hpp \
+ gui/qt/medialibrary/mlitemcover.cpp \
+ gui/qt/medialibrary/mlitemcover.hpp \
gui/qt/medialibrary/mlqmltypes.hpp \
gui/qt/medialibrary/mlqueryparams.cpp \
gui/qt/medialibrary/mlqueryparams.hpp \
@@ -353,7 +355,6 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/medialibrary/mlurlmodel.moc.cpp \
gui/qt/medialibrary/mlvideo.moc.cpp \
gui/qt/medialibrary/mlvideomodel.moc.cpp \
- gui/qt/medialibrary/mlplaylist.moc.cpp \
gui/qt/medialibrary/mlplaylistlistmodel.moc.cpp \
gui/qt/medialibrary/mlplaylistmodel.moc.cpp \
gui/qt/menus/custom_menus.moc.cpp \
=====================================
modules/gui/qt/maininterface/mainui.cpp
=====================================
@@ -199,7 +199,6 @@ void MainUI::registerQMLTypes()
registerAnonymousType<MLAlbum>("org.videolan.medialib", 1);
registerAnonymousType<MLArtist>("org.videolan.medialib", 1);
registerAnonymousType<MLAlbumTrack>("org.videolan.medialib", 1);
- registerAnonymousType<MLPlaylist>("org.videolan.medialib", 1);
qmlRegisterType<AlbumContextMenu>( "org.videolan.medialib", 0, 1, "AlbumContextMenu" );
qmlRegisterType<ArtistContextMenu>( "org.videolan.medialib", 0, 1, "ArtistContextMenu" );
=====================================
modules/gui/qt/medialibrary/mlgenre.cpp
=====================================
@@ -19,9 +19,8 @@
#include "mlgenre.hpp"
MLGenre::MLGenre(vlc_medialibrary_t* ml, const vlc_ml_genre_t *_data )
- : MLItem ( MLItemId( _data->i_id, VLC_ML_PARENT_GENRE ) )
+ : MLItemCover( MLItemId( _data->i_id, VLC_ML_PARENT_GENRE ) )
, m_ml ( ml )
- , m_generator( nullptr )
, m_name ( QString::fromUtf8( _data->psz_name ) )
, m_nbTracks ( (unsigned int)_data->i_nb_tracks )
@@ -29,16 +28,6 @@ MLGenre::MLGenre(vlc_medialibrary_t* ml, const vlc_ml_genre_t *_data )
assert(_data);
}
-bool MLGenre::hasGenerator() const
-{
- return m_generator.get();
-}
-
-void MLGenre::setGenerator(CoverGenerator * generator)
-{
- m_generator.reset(generator);
-}
-
QString MLGenre::getName() const
{
return m_name;
@@ -48,14 +37,3 @@ unsigned int MLGenre::getNbTracks() const
{
return m_nbTracks;
}
-
-QString MLGenre::getCover() const
-{
- return m_cover;
-}
-
-void MLGenre::setCover(const QString & fileName)
-{
- m_cover = fileName;
-}
-
=====================================
modules/gui/qt/medialibrary/mlgenre.hpp
=====================================
@@ -23,36 +23,22 @@
#include "config.h"
#endif
-// Util includes
-#include "util/covergenerator.hpp"
-
// MediaLibrary includes
-#include "mlqmltypes.hpp"
+#include "mlitemcover.hpp"
-class MLGenre : public MLItem
+class MLGenre : public MLItemCover
{
public:
- MLGenre( vlc_medialibrary_t* _ml, const vlc_ml_genre_t *_data );
-
- bool hasGenerator() const;
- void setGenerator(CoverGenerator * generator);
+ MLGenre(vlc_medialibrary_t * _ml, const vlc_ml_genre_t * _data);
QString getName() const;
- unsigned int getNbTracks() const;
- QString getCover() const;
- void setCover(const QString & fileName);
-
-private slots:
- void generateThumbnail();
+ unsigned int getNbTracks() const;
private:
- vlc_medialibrary_t* m_ml;
-
- TaskHandle<CoverGenerator> m_generator;
+ vlc_medialibrary_t * m_ml;
QString m_name;
- QString m_cover;
unsigned int m_nbTracks;
};
=====================================
modules/gui/qt/medialibrary/mlgroup.cpp
=====================================
@@ -28,9 +28,8 @@
//-------------------------------------------------------------------------------------------------
MLGroup::MLGroup(vlc_medialibrary_t * ml, const vlc_ml_group_t * data)
- : MLItem(MLItemId(data->i_id, VLC_ML_PARENT_GROUP))
+ : MLItemCover(MLItemId(data->i_id, VLC_ML_PARENT_GROUP))
, m_ml(ml)
- , m_generator(nullptr)
, m_name(qfu(data->psz_name))
, m_duration(data->i_duration)
, m_date(data->i_creation_date)
@@ -43,18 +42,6 @@ MLGroup::MLGroup(vlc_medialibrary_t * ml, const vlc_ml_group_t * data)
// Interface
//-------------------------------------------------------------------------------------------------
-bool MLGroup::hasGenerator() const
-{
- return m_generator.get();
-}
-
-void MLGroup::setGenerator(CoverGenerator * generator)
-{
- m_generator.reset(generator);
-}
-
-//-------------------------------------------------------------------------------------------------
-
QString MLGroup::getName() const
{
return m_name;
@@ -62,18 +49,6 @@ QString MLGroup::getName() const
//-------------------------------------------------------------------------------------------------
-QString MLGroup::getCover() const
-{
- return m_cover;
-}
-
-void MLGroup::setCover(const QString & fileName)
-{
- m_cover = fileName;
-}
-
-//-------------------------------------------------------------------------------------------------
-
int64_t MLGroup::getDuration() const
{
return m_duration;
=====================================
modules/gui/qt/medialibrary/mlgroup.hpp
=====================================
@@ -25,26 +25,17 @@
#include "config.h"
#endif
-// Util includes
-#include "util/covergenerator.hpp"
-
// MediaLibrary includes
-#include "mlqmltypes.hpp"
+#include "mlitemcover.hpp"
-class MLGroup : public MLItem
+class MLGroup : public MLItemCover
{
public:
MLGroup(vlc_medialibrary_t * ml, const vlc_ml_group_t * data);
public: // Interface
- bool hasGenerator() const;
- void setGenerator(CoverGenerator * generator);
-
QString getName() const;
- QString getCover() const;
- void setCover(const QString & fileName);
-
int64_t getDuration() const;
unsigned int getDate() const;
@@ -54,12 +45,8 @@ public: // Interface
private:
vlc_medialibrary_t * m_ml;
- TaskHandle<CoverGenerator> m_generator;
-
QString m_name;
- QString m_cover;
-
int64_t m_duration;
unsigned int m_date;
=====================================
modules/gui/qt/medialibrary/mlitemcover.cpp
=====================================
@@ -0,0 +1,55 @@
+/*****************************************************************************
+ * 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 "mlitemcover.hpp"
+
+//-------------------------------------------------------------------------------------------------
+// Ctor / dtor
+//-------------------------------------------------------------------------------------------------
+
+MLItemCover::MLItemCover(const MLItemId & id)
+ : MLItem(id)
+ , m_generator(nullptr) {}
+
+//-------------------------------------------------------------------------------------------------
+// Interface
+//-------------------------------------------------------------------------------------------------
+
+bool MLItemCover::hasGenerator() const
+{
+ return m_generator.get();
+}
+
+void MLItemCover::setGenerator(CoverGenerator * generator)
+{
+ m_generator.reset(generator);
+}
+
+//-------------------------------------------------------------------------------------------------
+
+QString MLItemCover::getCover() const
+{
+ return m_cover;
+}
+
+void MLItemCover::setCover(const QString & fileName)
+{
+ m_cover = fileName;
+}
=====================================
modules/gui/qt/medialibrary/mlitemcover.hpp
=====================================
@@ -0,0 +1,52 @@
+/*****************************************************************************
+ * 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 MLITEMCOVER_HPP
+#define MLITEMCOVER_HPP
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+// Util includes
+#include "util/covergenerator.hpp"
+
+// MediaLibrary includes
+#include "mlqmltypes.hpp"
+
+class MLItemCover : public MLItem
+{
+public:
+ /* explicit */ MLItemCover(const MLItemId & id);
+
+public: // Interface
+ bool hasGenerator() const;
+ void setGenerator(CoverGenerator * generator);
+
+ QString getCover() const;
+ void setCover(const QString & fileName);
+
+private:
+ TaskHandle<CoverGenerator> m_generator;
+
+ QString m_cover;
+};
+
+#endif
=====================================
modules/gui/qt/medialibrary/mlplaylist.cpp
=====================================
@@ -25,19 +25,14 @@
// Ctor / dtor
//-------------------------------------------------------------------------------------------------
-MLPlaylist::MLPlaylist(vlc_medialibrary_t * ml, const vlc_ml_playlist_t * data, QObject * parent)
- : QObject(parent)
- , MLItem(MLItemId(data->i_id, VLC_ML_PARENT_PLAYLIST))
+MLPlaylist::MLPlaylist(vlc_medialibrary_t * ml, const vlc_ml_playlist_t * data)
+ : MLItemCover(MLItemId(data->i_id, VLC_ML_PARENT_PLAYLIST))
, m_ml(ml)
+ , m_name(qfu(data->psz_name))
+ , m_duration(0) // TODO m_duration
+ , m_count(data->i_nb_media)
{
assert(data);
-
- m_name = qfu(data->psz_name);
-
- // TODO m_cover
-
- // TODO m_count
- m_count = 0;//data->i_nb_tracks;
}
//-------------------------------------------------------------------------------------------------
@@ -49,9 +44,11 @@ QString MLPlaylist::getName() const
return m_name;
}
-QString MLPlaylist::getCover() const
+//-------------------------------------------------------------------------------------------------
+
+int64_t MLPlaylist::getDuration() const
{
- return m_cover;
+ return m_duration;
}
unsigned int MLPlaylist::getCount() const
=====================================
modules/gui/qt/medialibrary/mlplaylist.hpp
=====================================
@@ -24,22 +24,17 @@
#endif
// MediaLibrary includes
-#include "mlqmltypes.hpp"
+#include "mlitemcover.hpp"
-// Qt includes
-#include <QObject>
-
-class MLPlaylist : public QObject, public MLItem
+class MLPlaylist : public MLItemCover
{
- Q_OBJECT
-
public:
- MLPlaylist(vlc_medialibrary_t * ml,
- const vlc_ml_playlist_t * data, QObject * parent = nullptr);
+ MLPlaylist(vlc_medialibrary_t * ml, const vlc_ml_playlist_t * data);
public: // Interface
- QString getName () const;
- QString getCover() const;
+ QString getName() const;
+
+ int64_t getDuration() const;
unsigned int getCount() const;
@@ -47,7 +42,8 @@ private:
vlc_medialibrary_t * m_ml;
QString m_name;
- QString m_cover;
+
+ int64_t m_duration;
unsigned int m_count;
};
=====================================
modules/gui/qt/medialibrary/mlplaylistlistmodel.cpp
=====================================
@@ -30,18 +30,27 @@
#include "mlhelper.hpp"
#include "mlplaylist.hpp"
+//-------------------------------------------------------------------------------------------------
+// Static variables
+
+// NOTE: We multiply by 2 to cover most dpi settings.
+static const int MLPLAYLISTMODEL_COVER_WIDTH = 512 * 2; // 16 / 10 ratio
+static const int MLPLAYLISTMODEL_COVER_HEIGHT = 320 * 2;
+
//=================================================================================================
// MLPlaylistListModel
//=================================================================================================
MLPlaylistListModel::MLPlaylistListModel(vlc_medialibrary_t * ml, QObject * parent)
: MLBaseModel(parent)
+ , m_coverDefault(":/noart_videoCover.svg")
{
m_ml = ml;
}
/* explicit */ MLPlaylistListModel::MLPlaylistListModel(QObject * parent)
- : MLBaseModel(parent) {}
+ : MLBaseModel(parent)
+ , m_coverSize(MLPLAYLISTMODEL_COVER_WIDTH, MLPLAYLISTMODEL_COVER_HEIGHT) {}
//-------------------------------------------------------------------------------------------------
@@ -156,16 +165,19 @@ QHash<int, QByteArray> MLPlaylistListModel::roleNames() const /* override */
{
return
{
- { PLAYLIST_ID, "id" },
- { PLAYLIST_NAME, "name" },
- { PLAYLIST_COVER, "cover" },
- { PLAYLIST_COUNT, "count" }
+ { PLAYLIST_ID, "id" },
+ { PLAYLIST_NAME, "name" },
+ { PLAYLIST_THUMBNAIL, "thumbnail" },
+ { PLAYLIST_DURATION, "duration" },
+ { PLAYLIST_COUNT, "count" }
};
}
QVariant MLPlaylistListModel::data(const QModelIndex & index, int role) const /* override */
{
- const MLPlaylist * playlist = static_cast<MLPlaylist *>(item(index.row()));
+ int row = index.row();
+
+ MLPlaylist * playlist = static_cast<MLPlaylist *>(item(row));
if (playlist == nullptr)
return QVariant();
@@ -175,16 +187,18 @@ QVariant MLPlaylistListModel::data(const QModelIndex & index, int role) const /*
// NOTE: This is the condition for QWidget view(s).
case Qt::DisplayRole:
if (index.column() == 0)
- return QVariant::fromValue(playlist->getName());
+ return playlist->getName();
else
return QVariant();
// NOTE: These are the conditions for QML view(s).
case PLAYLIST_ID:
return QVariant::fromValue(playlist->getId());
case PLAYLIST_NAME:
- return QVariant::fromValue(playlist->getName());
- case PLAYLIST_COVER:
- return QVariant::fromValue(playlist->getCover());
+ return playlist->getName();
+ case PLAYLIST_THUMBNAIL:
+ return getCover(playlist, row);
+ case PLAYLIST_DURATION:
+ return QVariant::fromValue(playlist->getDuration());
case PLAYLIST_COUNT:
return QVariant::fromValue(playlist->getCount());
default:
@@ -225,6 +239,36 @@ ListCacheLoader<std::unique_ptr<MLItem>> * MLPlaylistListModel::createLoader() c
return new Loader(*this);
}
+//-------------------------------------------------------------------------------------------------
+// Private functions
+//-------------------------------------------------------------------------------------------------
+
+QString MLPlaylistListModel::getCover(MLPlaylist * playlist, int index) const
+{
+ QString cover = playlist->getCover();
+
+ // NOTE: Making sure we're not already generating a cover.
+ if (cover.isNull() == false || playlist->hasGenerator())
+ return cover;
+
+ CoverGenerator * generator = new CoverGenerator(m_ml, playlist->getId(), index);
+
+ generator->setSize(m_coverSize);
+
+ generator->setDefaultThumbnail(m_coverDefault);
+
+ generator->setPrefix(m_coverPrefix);
+
+ // NOTE: We'll apply the new thumbnail once it's loaded.
+ connect(generator, &CoverGenerator::result, this, &MLPlaylistListModel::onCover);
+
+ generator->start(*QThreadPool::globalInstance());
+
+ playlist->setGenerator(generator);
+
+ return cover;
+}
+
//-------------------------------------------------------------------------------------------------
// Private MLBaseModel reimplementation
//-------------------------------------------------------------------------------------------------
@@ -246,6 +290,94 @@ void MLPlaylistListModel::onVlcMlEvent(const MLEvent & event) /* override */
MLBaseModel::onVlcMlEvent(event);
}
+void MLPlaylistListModel::thumbnailUpdated(int idx) /* override */
+{
+ QModelIndex index = this->index(idx);
+
+ emit dataChanged(index, index, { PLAYLIST_THUMBNAIL });
+}
+
+//-------------------------------------------------------------------------------------------------
+// Private slots
+//-------------------------------------------------------------------------------------------------
+
+void MLPlaylistListModel::onCover()
+{
+ CoverGenerator * generator = static_cast<CoverGenerator *> (sender());
+
+ int index = generator->getIndex();
+
+ // NOTE: We want to avoid calling 'MLBaseModel::item' for performance issues.
+ MLItem * item = this->itemCache(index);
+
+ // NOTE: When the item is no longer cached or has been moved we return right away.
+ if (item == nullptr || item->getId() != generator->getId())
+ {
+ generator->deleteLater();
+
+ return;
+ }
+
+ MLPlaylist * playlist = static_cast<MLPlaylist *> (item);
+
+ QString fileName = QUrl::fromLocalFile(generator->takeResult()).toString();
+
+ playlist->setCover(fileName);
+
+ playlist->setGenerator(nullptr);
+
+ thumbnailUpdated(index);
+}
+
+//-------------------------------------------------------------------------------------------------
+// Properties
+//-------------------------------------------------------------------------------------------------
+
+QSize MLPlaylistListModel::coverSize() const
+{
+ return m_coverSize;
+}
+
+void MLPlaylistListModel::setCoverSize(const QSize & size)
+{
+ if (m_coverSize == size)
+ return;
+
+ m_coverSize = size;
+
+ emit coverSizeChanged();
+}
+
+QString MLPlaylistListModel::coverDefault() const
+{
+ return m_coverDefault;
+}
+
+void MLPlaylistListModel::setCoverDefault(const QString & fileName)
+{
+ if (m_coverDefault == fileName)
+ return;
+
+ m_coverDefault = fileName;
+
+ emit coverDefaultChanged();
+}
+
+QString MLPlaylistListModel::coverPrefix() const
+{
+ return m_coverPrefix;
+}
+
+void MLPlaylistListModel::setCoverPrefix(const QString & prefix)
+{
+ if (m_coverPrefix == prefix)
+ return;
+
+ m_coverPrefix = prefix;
+
+ emit coverPrefixChanged();
+}
+
//=================================================================================================
// Loader
//=================================================================================================
=====================================
modules/gui/qt/medialibrary/mlplaylistlistmodel.hpp
=====================================
@@ -24,18 +24,26 @@
// Forward declarations
class vlc_medialibrary_t;
-class vlc_ml_playlist_t;
+class MLPlaylist;
class MLPlaylistListModel : public MLBaseModel
{
Q_OBJECT
+ Q_PROPERTY(QSize coverSize READ coverSize WRITE setCoverSize NOTIFY coverSizeChanged)
+
+ Q_PROPERTY(QString coverDefault READ coverDefault WRITE setCoverDefault
+ NOTIFY coverDefaultChanged)
+
+ Q_PROPERTY(QString coverPrefix READ coverPrefix WRITE setCoverPrefix NOTIFY coverPrefixChanged)
+
public:
enum Roles
{
PLAYLIST_ID = Qt::UserRole + 1,
PLAYLIST_NAME,
- PLAYLIST_COVER,
+ PLAYLIST_THUMBNAIL,
+ PLAYLIST_DURATION,
PLAYLIST_COUNT
};
@@ -67,9 +75,37 @@ protected: // MLBaseModel implementation
ListCacheLoader<std::unique_ptr<MLItem>> * createLoader() const override;
+private: // Functions
+ QString getCover(MLPlaylist * playlist, int index) const;
+
private: // MLBaseModel implementation
void onVlcMlEvent(const MLEvent & event) override;
+ void thumbnailUpdated(int idx) override;
+
+private slots:
+ void onCover();
+
+signals:
+ void coverSizeChanged ();
+ void coverDefaultChanged();
+ void coverPrefixChanged ();
+
+public: // Properties
+ QSize coverSize() const;
+ void setCoverSize(const QSize & size);
+
+ QString coverDefault() const;
+ void setCoverDefault(const QString & fileName);
+
+ QString coverPrefix() const;
+ void setCoverPrefix(const QString & prefix);
+
+private: // Variables
+ QSize m_coverSize;
+ QString m_coverDefault;
+ QString m_coverPrefix;
+
private:
struct Loader : public MLBaseModel::BaseLoader
{
=====================================
modules/gui/qt/medialibrary/qml/MusicPlaylistsDisplay.qml
=====================================
@@ -92,6 +92,8 @@ Widgets.PageLoader {
PlaylistMediaList {
anchors.fill: parent
+ isMusic: true
+
onCurrentIndexChanged: _updateHistoryList(currentIndex)
onShowList: history.push(["mc", "music", "playlists", "list",
=====================================
modules/gui/qt/medialibrary/qml/PlaylistMediaList.qml
=====================================
@@ -37,6 +37,8 @@ Widgets.NavigableFocusScope {
readonly property int currentIndex: currentItem.currentIndex
+ property bool isMusic: false
+
property int initialIndex: 0
property var sortModel: [{ text: i18n.qtr("Alphabetic"), criteria: "title" }]
@@ -44,15 +46,23 @@ Widgets.NavigableFocusScope {
//---------------------------------------------------------------------------------------------
// Private
- property int _width: VLCStyle.colWidth(2)
+ property int _width: (isMusic) ? VLCStyle.gridItem_music_width
+ : VLCStyle.gridItem_video_width
- property int _height: Math.round(_width / 2)
+ property int _height: (isMusic) ? VLCStyle.gridItem_music_height
+ : VLCStyle.gridItem_video_height
property int _widthColumn:
Math.max(VLCStyle.gridColumnsForWidth(tableView.availableRowWidth
- VLCStyle.listAlbumCover_width
- VLCStyle.column_margin_width) - 1, 1)
+ property int _widthCover: (isMusic) ? VLCStyle.gridCover_music_width
+ : VLCStyle.gridCover_video_width
+
+ property int _heightCover: (isMusic) ? VLCStyle.gridCover_music_height
+ : VLCStyle.gridCover_video_height
+
//---------------------------------------------------------------------------------------------
// Alias
//---------------------------------------------------------------------------------------------
@@ -145,6 +155,16 @@ Widgets.NavigableFocusScope {
}
}
+ function _getCount(model)
+ {
+ var count = model.count;
+
+ if (count < 100)
+ return count;
+ else
+ return i18n.qtr("99+");
+ }
+
//---------------------------------------------------------------------------------------------
// Childs
//---------------------------------------------------------------------------------------------
@@ -154,6 +174,13 @@ Widgets.NavigableFocusScope {
ml: medialib
+ coverSize: (isMusic) ? Qt.size(512, 512)
+ : Qt.size(1024, 640)
+
+ coverDefault: (isMusic) ? ":/noart_album.svg" : ":/noart_videoCover.svg"
+
+ coverPrefix: (isMusic) ? "playlist-music" : "playlist-video"
+
onCountChanged: {
if (count === 0 || modelSelect.hasSelection) return;
@@ -183,7 +210,7 @@ Widgets.NavigableFocusScope {
})
var covers = items.map(function (item) {
- return { artwork: item.cover || VLCStyle.noArtCover };
+ return { artwork: item.thumbnail || VLCStyle.noArtCover };
})
var title = items.map(function (item) {
@@ -237,21 +264,7 @@ Widgets.NavigableFocusScope {
focus: true
- //-------------------------------------------------------------------------------------
- // Events
-
- onSelectAll: modelSelect.selectAll()
-
- onSelectionUpdated: modelSelect.updateSelection(keyModifiers, oldIndex, newIndex)
-
- onActionAtIndex: _actionAtIndex()
-
- //-------------------------------------------------------------------------------------
- // Childs
-
- delegate: Widgets.GridItem {
- id: item
-
+ delegate: VideoGridItem {
//---------------------------------------------------------------------------------
// Properties
@@ -262,76 +275,70 @@ Widgets.NavigableFocusScope {
//---------------------------------------------------------------------------------
// Settings
- width : _width
- height: _height
+ pictureWidth : _widthCover
+ pictureHeight: _heightCover
+
+ title: (model.name) ? model.name
+ : i18n.qtr("Unknown title")
- pictureWidth : width
- pictureHeight: height
+ labels: (model.count > 1) ? [ i18n.qtr("%1 Tracks").arg(_getCount(model)) ]
+ : [ i18n.qtr("%1 Track") .arg(_getCount(model)) ]
- image: VLCStyle.noArtAlbum
+ // NOTE: We don't want to show the new indicator for a playlist.
+ showNewIndicator: false
dragItem: dragItemPlaylist
+ selectedUnderlay : shadows.selected
unselectedUnderlay: shadows.unselected
- selectedUnderlay: shadows.selected
-
- pictureOverlay: Item {
- Column {
- anchors.centerIn: parent
-
- Label {
- width: item.width
- horizontalAlignment: Text.AlignHCenter
-
- text: model.name
-
- elide: Text.ElideRight
+ //---------------------------------------------------------------------------------
+ // Events
- color: "white"
+ onItemClicked: gridView.leftClickOnItem(modifier, index)
- font.pixelSize: VLCStyle.fontSize_large
- font.weight : Font.DemiBold
- }
+ onItemDoubleClicked: showList(model)
- Widgets.CaptionLabel {
- width: item.width
+ onPlayClicked: if (model.id) medialib.addAndPlay(model.id)
- horizontalAlignment: Text.AlignHCenter
+ onContextMenuButtonClicked: {
+ gridView.rightClickOnItem(index);
- opacity: 0.7
+ contextMenu.popup(modelSelect.selectedIndexes, globalMousePos);
+ }
- text: (model.count > 1) ? i18n.qtr("%1 Tracks").arg(model.count)
- : i18n.qtr("%1 Track") .arg(model.count)
+ //---------------------------------------------------------------------------------
+ // Animations
- color: "white"
- }
- }
- }
+ Behavior on opacity { NumberAnimation { duration: 100 } }
+ }
- playCoverBorderWidth: VLCStyle.dp(3, VLCStyle.scale)
+ //-------------------------------------------------------------------------------------
+ // Events
- //---------------------------------------------------------------------------------
- // Events
+ // NOTE: Define the initial position and selection. This is done on activeFocus rather
+ // than Component.onCompleted because modelSelect.selectedGroup update itself
+ // after this event.
+ onActiveFocusChanged: {
+ if (activeFocus == false || model.count === 0 || modelSelect.hasSelection) return;
- onItemDoubleClicked: showList(model)
+ modelSelect.select(model.index(0,0), ItemSelectionModel.ClearAndSelect)
+ }
- onItemClicked: gridView.leftClickOnItem(modifier, index)
+ onSelectAll: modelSelect.selectAll()
- onPlayClicked: if (model.id) medialib.addAndPlay(model.id)
+ onSelectionUpdated: modelSelect.updateSelection(keyModifiers, oldIndex, newIndex)
- onContextMenuButtonClicked: {
- gridView.rightClickOnItem(index);
+ onActionAtIndex: _actionAtIndex()
- contextMenu.popup(modelSelect.selectedIndexes, globalMousePos);
- }
- }
+ //-------------------------------------------------------------------------------------
+ // Childs
Widgets.GridShadows {
id: shadows
- coverWidth: root._width
- coverHeight: root._height
+ coverWidth : _widthCover
+ coverHeight: _heightCover
}
}
}
@@ -371,7 +378,7 @@ Widgets.NavigableFocusScope {
sortModel: [{
isPrimary: true,
- criteria: "cover",
+ criteria: "thumbnail",
width: VLCStyle.listAlbumCover_width,
@@ -409,9 +416,24 @@ Widgets.NavigableFocusScope {
showTitleText: false
- titleCover_width : VLCStyle.listAlbumCover_width
- titleCover_height: VLCStyle.listAlbumCover_height
- titleCover_radius: VLCStyle.listAlbumCover_radius
+ //---------------------------------------------------------------------------------
+ // NOTE: When it's music we want the cover to be square
+
+ titleCover_width: (isMusic) ? VLCStyle.trackListAlbumCover_width
+ : VLCStyle.listAlbumCover_width
+
+ titleCover_height: (isMusic) ? VLCStyle.trackListAlbumCover_heigth
+ : VLCStyle.listAlbumCover_height
+
+ titleCover_radius: (isMusic) ? VLCStyle.trackListAlbumCover_radius
+ : VLCStyle.listAlbumCover_radius
+
+ //---------------------------------------------------------------------------------
+
+ // NOTE: This makes sure we display the playlist count on the item.
+ function titlecoverLabels(model) {
+ return [ _getCount(model) ];
+ }
}
}
}
=====================================
modules/gui/qt/util/covergenerator.cpp
=====================================
@@ -28,6 +28,7 @@
// Qt includes
#include <QDir>
+#include <QImageReader>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QGraphicsBlurEffect>
@@ -107,6 +108,11 @@ CoverGenerator::CoverGenerator(vlc_medialibrary_t * ml, const MLItemId & itemId,
m_default = fileName;
}
+/* Q_INVOKABLE */ void CoverGenerator::setPrefix(const QString & prefix)
+{
+ m_prefix = prefix;
+}
+
//-------------------------------------------------------------------------------------------------
// QRunnable implementation
//-------------------------------------------------------------------------------------------------
@@ -121,9 +127,15 @@ QString CoverGenerator::execute() /* override */
int64_t id = m_id.id;
- QString string = getStringType(type);
+ QString fileName;
- QString fileName = QString("%1_thumbnail_%2.jpg").arg(string).arg(id);
+ // NOTE: If we don't have a valid prefix we generate one based on the item type.
+ if (m_prefix.isEmpty())
+ {
+ m_prefix = getPrefix(type);
+ }
+
+ fileName = QString("%1_thumbnail_%2.jpg").arg(m_prefix).arg(id);
fileName = dir.absoluteFilePath(fileName);
@@ -141,6 +153,9 @@ QString CoverGenerator::execute() /* override */
else
thumbnails = getMedias(count, id, type);
+ int countX;
+ int countY;
+
if (thumbnails.isEmpty())
{
if (m_split == CoverGenerator::Duplicate)
@@ -149,13 +164,16 @@ QString CoverGenerator::execute() /* override */
{
thumbnails.append(m_default);
}
+
+ countX = m_countX;
+ countY = m_countY;
}
else
{
thumbnails.append(m_default);
- m_countX = 1;
- m_countY = 1;
+ countX = 1;
+ countY = 1;
}
}
else if (m_split == CoverGenerator::Duplicate)
@@ -168,15 +186,16 @@ QString CoverGenerator::execute() /* override */
index++;
}
+
+ countX = m_countX;
+ countY = m_countY;
}
else // if (m_split == CoverGenerator::Divide)
{
- // NOTE: This handles the 2x2 case.
- if (thumbnails.count() == 2)
- {
- m_countX = 2;
- m_countY = 1;
- }
+ countX = m_countX;
+
+ // NOTE: We try to divide thumbnails as far as we can based on their total count.
+ countY = std::ceil((qreal) thumbnails.count() / m_countX);
}
QImage image(m_size, QImage::Format_RGB32);
@@ -187,7 +206,7 @@ QString CoverGenerator::execute() /* override */
painter.begin(&image);
- draw(painter, thumbnails);
+ draw(painter, thumbnails, countX, countY);
painter.end();
@@ -203,57 +222,89 @@ QString CoverGenerator::execute() /* override */
// Private functions
//-------------------------------------------------------------------------------------------------
-void CoverGenerator::draw(QPainter & painter, const QStringList & fileNames)
+void CoverGenerator::draw(QPainter & painter,
+ const QStringList & fileNames, int countX, int countY)
{
int count = fileNames.count();
- int width = m_size.width() / m_countX;
- int height = m_size.height() / m_countY;
+ int width = m_size.width() / countX;
+ int height = m_size.height() / countY;
- for (int y = 0; y < m_countY; y++)
+ for (int y = 0; y < countY; y++)
{
- for (int x = 0; x < m_countX; x++)
+ for (int x = 0; x < countX; x++)
{
- int index = m_countX * y + x;
+ int index = countX * y + x;
if (index == count) return;
QRect rect;
// NOTE: This handles the wider thumbnail case (e.g. for a 2x1 grid).
- if (index == count - 1 && x != m_countX - 1)
+ if (index == count - 1 && x != countX - 1)
{
- rect = QRect(width * x, height * y, width * m_countX - x, height);
+ rect = QRect(width * x, height * y, width * countX - x, height);
}
else
rect = QRect(width * x, height * y, width, height);
- drawImage(painter, fileNames.at(index), rect);
+ QString fileName = fileNames.at(index);
+
+ if (fileName.isEmpty())
+ drawImage(painter, m_default, rect);
+ else
+ drawImage(painter, fileName, rect);
}
}
}
void CoverGenerator::drawImage(QPainter & painter, const QString & fileName, const QRect & target)
{
- QImage image;
-
- if (fileName.isEmpty())
- image.load(m_default);
- else
- image.load(fileName);
+ QFile file(fileName);
- // NOTE: This image does not seem valid so we paint the placeholder instead.
- if (image.isNull())
+ if (file.open(QIODevice::ReadOnly) == false)
{
- image.load(m_default);
+ // NOTE: This image does not seem valid so we paint the placeholder instead.
+ if (fileName != m_default)
+ drawImage(painter, m_default, target);
+
+ return;
}
- // NOTE: Should we use Qt::SmoothTransformation or favor efficiency ?
- if (m_smooth)
- image = image.scaled(target.size(), Qt::KeepAspectRatioByExpanding,
- Qt::SmoothTransformation);
+ QImageReader reader(&file);
+
+ if (reader.canRead() == false)
+ return;
+
+ QSize size = reader.size().scaled(target.width(),
+ target.height(), Qt::KeepAspectRatioByExpanding);
+
+ QImage image;
+
+ if (fileName.endsWith(".svg", Qt::CaseInsensitive))
+ {
+ if (size.isEmpty() == false)
+ {
+ reader.setScaledSize(size);
+ }
+
+ if (reader.read(&image) == false)
+ return;
+ }
else
- image = image.scaled(target.size(), Qt::KeepAspectRatioByExpanding);
+ {
+ if (reader.read(&image) == false)
+ return;
+
+ if (size.isEmpty() == false)
+ {
+ // NOTE: Should we use Qt::SmoothTransformation or favor efficiency ?
+ if (m_smooth)
+ image = image.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ else
+ image = image.scaled(size, Qt::IgnoreAspectRatio);
+ }
+ }
int x = (image.width () - target.width ()) / 2;
int y = (image.height() - target.height()) / 2;
@@ -295,7 +346,7 @@ void CoverGenerator::blur(QImage * image)
//-------------------------------------------------------------------------------------------------
-QString CoverGenerator::getStringType(vlc_ml_parent_type type) const
+QString CoverGenerator::getPrefix(vlc_ml_parent_type type) const
{
switch (type)
{
=====================================
modules/gui/qt/util/covergenerator.hpp
=====================================
@@ -75,17 +75,20 @@ public: // Interface
Q_INVOKABLE void setDefaultThumbnail(const QString & fileName);
+ // NOTE: This lets us enforce a specific prefix for the cover fileName.
+ Q_INVOKABLE void setPrefix(const QString & prefix);
+
public: // AsyncTask implementation
QString execute() override;
private: // Functions
- void draw(QPainter & painter, const QStringList & fileNames);
+ void draw(QPainter & painter, const QStringList & fileNames, int countX, int countY);
void drawImage(QPainter & painter, const QString & fileName, const QRect & rect);
void blur(QImage * image);
- QString getStringType(vlc_ml_parent_type type) const;
+ QString getPrefix(vlc_ml_parent_type type) const;
QStringList getMedias(int count, int64_t id, vlc_ml_parent_type type) const;
QStringList getGenre (int count, int64_t id) const;
@@ -109,6 +112,8 @@ private:
int m_blur;
QString m_default;
+
+ QString m_prefix;
};
#endif // COVERGENERATOR_HPP
=====================================
modules/misc/medialibrary/entities.cpp
=====================================
@@ -429,6 +429,9 @@ bool Convert( const medialibrary::IPlaylist* input, vlc_ml_playlist_t& output )
{
output.i_id = input->id();
+ output.i_nb_media = input->nbMedia();
+ output.i_nb_present_media = input->nbPresentMedia();
+
output.i_creation_date = input->creationDate();
output.b_is_read_only = input->isReadOnly();
=====================================
po/POTFILES.in
=====================================
@@ -777,6 +777,8 @@ 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
modules/gui/qt/medialibrary/mlplaylistlistmodel.hpp
modules/gui/qt/medialibrary/mlplaylistmedia.cpp
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/a7f24ef49f462442749f14e19388ffb3d6d03bd5...f235d8ff4282705936729298d4cf25aee595a82a
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/a7f24ef49f462442749f14e19388ffb3d6d03bd5...f235d8ff4282705936729298d4cf25aee595a82a
You're receiving this email because of your account on code.videolan.org.
More information about the vlc-commits
mailing list