[vlc-devel] [PATCH 1/3] qt: generate medialib genre covers on C++ side
Pierre Lamot
pierre at videolabs.io
Tue Oct 29 14:47:16 CET 2019
---
.../gui/qt/components/mediacenter/mlgenre.cpp | 230 +++++++++++++++++-
.../gui/qt/components/mediacenter/mlgenre.hpp | 20 +-
.../components/mediacenter/mlgenremodel.cpp | 8 +-
3 files changed, 254 insertions(+), 4 deletions(-)
diff --git a/modules/gui/qt/components/mediacenter/mlgenre.cpp b/modules/gui/qt/components/mediacenter/mlgenre.cpp
index 8bed1c1fc7..a20686c16b 100644
--- a/modules/gui/qt/components/mediacenter/mlgenre.cpp
+++ b/modules/gui/qt/components/mediacenter/mlgenre.cpp
@@ -17,25 +17,208 @@
*****************************************************************************/
#include <cassert>
+#include <QPainter>
+#include <QImage>
+#include <QThreadPool>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QDir>
#include "mlgenre.hpp"
+#include "qt.hpp"
-MLGenre::MLGenre(const vlc_ml_genre_t *_data, QObject *_parent )
+namespace {
+
+#define THUMBNAIL_WIDTH 512
+#define THUMBNAIL_HEIGHT 512
+
+class GenerateCoverTask : public QRunnable
+{
+public:
+ GenerateCoverTask(vlc_medialibrary_t* ml, MLGenre* genre, QString filepath)
+ : QRunnable()
+ , m_ml(ml)
+ , m_genre(genre)
+ , m_filepath(filepath)
+ {
+ }
+
+ void drawRegion(QPainter& target, QString source, const QRect& rect)
+ {
+ QImage tmpImage;
+ if (tmpImage.load(source))
+ {
+ QRect sourceRect;
+ int size = std::min(tmpImage.width(), tmpImage.height());
+ if (rect.width() == rect.height())
+ {
+ sourceRect = QRect( (tmpImage.width() - size) / 2,
+ (tmpImage.height() - size) / 2,
+ size,
+ size);
+ }
+ else if (rect.width() > rect.height())
+ {
+ sourceRect = QRect( (tmpImage.width() - size) / 2,
+ (tmpImage.height() - size/2) / 2,
+ size,
+ size/2);
+ }
+ else
+ {
+ sourceRect = QRect( (tmpImage.width() - size / 2) / 2,
+ (tmpImage.height() - size) / 2,
+ size/2,
+ size);
+ }
+ target.drawImage(rect, tmpImage, sourceRect);
+ }
+ else
+ {
+ target.setPen(Qt::black);
+ target.drawRect(rect);
+ }
+ }
+
+ void run() override
+ {
+ {
+ QMutexLocker lock(&m_taskLock);
+ if (m_canceled) {
+ m_taskCond.wakeAll();
+ return;
+ }
+ m_running = true;
+ }
+
+ int64_t genreId = m_genre->getId().id;
+ ml_unique_ptr<vlc_ml_album_list_t> album_list;
+ //TODO only retreive albums with a cover.
+ vlc_ml_query_params_t queryParams;
+ memset(&queryParams, 0, sizeof(vlc_ml_query_params_t));
+ album_list.reset( vlc_ml_list_genre_albums(m_ml, &queryParams, genreId) );
+
+ QStringList thumnails;
+ int count = 0;
+ for( const vlc_ml_album_t& media: ml_range_iterate<vlc_ml_album_t>( album_list ) ) {
+ if (media.thumbnails[VLC_ML_THUMBNAIL_SMALL].b_generated) {
+ QUrl mediaURL( media.thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl );
+ //QImage only accept local file
+ if (mediaURL.isValid() && mediaURL.isLocalFile()) {
+ thumnails.append(mediaURL.path());
+ count++;
+ if (count >= 4)
+ break;
+ }
+ }
+ }
+
+ {
+ QMutexLocker lock(&m_taskLock);
+ if (m_canceled) {
+ m_running = false;
+ m_taskCond.wakeAll();
+ return;
+ }
+ }
+
+ QImage image(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QImage::Format_RGB32);
+
+ QPainter painter;
+ painter.begin(&image);
+ switch (count) {
+ case 0:
+ break;
+
+ case 1:
+ drawRegion(painter, thumnails[0], QRect(0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT ));
+ break;
+
+ case 2:
+ drawRegion(painter, thumnails[0], QRect(0, 0, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT ));
+ drawRegion(painter, thumnails[1], QRect(THUMBNAIL_WIDTH/2, 0, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT ));
+ break;
+
+ case 3:
+ drawRegion(painter, thumnails[0], QRect(0, 0, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT ));
+ drawRegion(painter, thumnails[1], QRect(THUMBNAIL_WIDTH/2, 0, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT/2));
+ drawRegion(painter, thumnails[2], QRect(THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT/2, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT/2));
+ break;
+
+ case 4:
+ drawRegion(painter, thumnails[0], QRect(0, 0, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT/2));
+ drawRegion(painter, thumnails[1], QRect(THUMBNAIL_WIDTH/2, 0, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT/2));
+ drawRegion(painter, thumnails[2], QRect(THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT/2, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT/2));
+ drawRegion(painter, thumnails[3], QRect(0, THUMBNAIL_HEIGHT/2, THUMBNAIL_WIDTH/2, THUMBNAIL_HEIGHT/2));
+ break;
+ }
+ painter.end();
+
+ if (count > 0) {
+ if (image.save(m_filepath, "jpg"))
+ m_genre->setCover(QUrl::fromLocalFile(m_filepath).toString());
+ }
+
+ {
+ QMutexLocker lock(&m_taskLock);
+ m_running = false;
+ m_taskCond.wakeAll();
+ }
+ }
+
+ void cancel()
+ {
+ QMutexLocker lock(&m_taskLock);
+ if (!m_running)
+ return;
+ m_canceled = true;
+ m_taskCond.wait(&m_taskLock);
+ }
+
+private:
+ bool m_canceled = false;
+ bool m_running = false;
+ QMutex m_taskLock;
+ QWaitCondition m_taskCond;
+
+ vlc_medialibrary_t* m_ml = nullptr;
+ MLGenre* m_genre = nullptr;
+ QString m_filepath;
+};
+
+}
+
+
+MLGenre::MLGenre(vlc_medialibrary_t* ml, const vlc_ml_genre_t *_data, QObject *_parent )
: QObject(_parent)
+ , m_ml ( ml )
, m_id ( _data->i_id, VLC_ML_PARENT_GENRE )
, m_name ( QString::fromUtf8( _data->psz_name ) )
{
assert(_data);
+ connect(this, &MLGenre::askGenerateCover, this, &MLGenre::generateThumbnail);
}
MLGenre::MLGenre(const MLGenre &genre, QObject *_parent)
: QObject(_parent)
+ , m_ml ( genre.m_ml )
, m_id ( genre.m_id )
, m_name ( genre.m_name )
{
}
+MLGenre::~MLGenre()
+{
+ if (m_coverTask) {
+ if (!QThreadPool::globalInstance()->tryTake(m_coverTask)) {
+ //task is done or running
+ static_cast<GenerateCoverTask*>(m_coverTask)->cancel();
+ }
+ delete m_coverTask;
+ }
+}
+
MLParentId MLGenre::getId() const
{
return m_id;
@@ -51,8 +234,53 @@ unsigned int MLGenre::getNbTracks() const
return m_nbTracks;
}
+QString MLGenre::getCover() const
+{
+ if (!m_cover.isEmpty())
+ return m_cover;
+ if (!m_coverTask) {
+ emit askGenerateCover( QPrivateSignal() );
+ }
+ return m_cover;
+}
+
+void MLGenre::setCover(const QString cover)
+{
+ m_cover = cover;
+ //TODO store in media library
+ emit coverChanged(m_cover);
+}
+
MLGenre *MLGenre::clone(QObject *parent) const
{
return new MLGenre(*this, parent);
}
+
+
+void MLGenre::generateThumbnail()
+{
+ if (!m_coverTask && m_cover.isNull()) {
+
+ QDir dir(config_GetUserDir(VLC_CACHE_DIR));
+ dir.mkdir("art");
+ dir.cd("art");
+ dir.mkdir("qt-genre-covers");
+ dir.cd("qt-genre-covers");
+
+ QString filename = QString("genre_thumbnail_%1.jpg").arg(m_id.id);
+ QString absoluteFilePath = dir.absoluteFilePath(filename);
+ if (dir.exists(filename))
+ {
+ setCover(QUrl::fromLocalFile(absoluteFilePath).toString());
+ }
+ else
+ {
+ GenerateCoverTask* coverTask = new GenerateCoverTask(m_ml, this, absoluteFilePath);
+ coverTask->setAutoDelete(false);
+ m_coverTask = coverTask;
+ QThreadPool::globalInstance()->start(coverTask);
+ }
+ }
+}
+
diff --git a/modules/gui/qt/components/mediacenter/mlgenre.hpp b/modules/gui/qt/components/mediacenter/mlgenre.hpp
index 61e44a9df3..66b5773be4 100644
--- a/modules/gui/qt/components/mediacenter/mlgenre.hpp
+++ b/modules/gui/qt/components/mediacenter/mlgenre.hpp
@@ -28,6 +28,7 @@
#include <QObject>
#include <QString>
#include <QList>
+#include <QRunnable>
#include <vlc_media_library.h>
#include "mlhelper.hpp"
#include "mlqmltypes.hpp"
@@ -39,21 +40,38 @@ class MLGenre : public QObject
Q_PROPERTY(MLParentId id READ getId CONSTANT)
Q_PROPERTY(QString name READ getName CONSTANT)
Q_PROPERTY(unsigned int nbtracks READ getNbTracks CONSTANT)
+ Q_PROPERTY(QString cover READ getCover WRITE setCover NOTIFY coverChanged)
public:
- MLGenre( const vlc_ml_genre_t *_data, QObject *_parent = nullptr);
+ MLGenre( vlc_medialibrary_t* _ml, const vlc_ml_genre_t *_data, QObject *_parent = nullptr);
+ ~MLGenre();
MLParentId getId() const;
QString getName() const;
unsigned int getNbTracks() const;
+ QString getCover() const;
MLGenre* clone(QObject *parent = nullptr) const;
+signals:
+ void coverChanged( const QString );
+ void askGenerateCover( QPrivateSignal ) const;
+
+public slots:
+ void setCover(const QString cover);
+
+private slots:
+ void generateThumbnail();
+
private:
MLGenre( const MLGenre& genre, QObject *_parent = nullptr);
+ vlc_medialibrary_t* m_ml;
+
MLParentId m_id;
QString m_name;
+ QString m_cover;
+ QRunnable* m_coverTask = nullptr;
unsigned int m_nbTracks;
};
diff --git a/modules/gui/qt/components/mediacenter/mlgenremodel.cpp b/modules/gui/qt/components/mediacenter/mlgenremodel.cpp
index c4a23ecbae..cf248411c6 100644
--- a/modules/gui/qt/components/mediacenter/mlgenremodel.cpp
+++ b/modules/gui/qt/components/mediacenter/mlgenremodel.cpp
@@ -29,6 +29,7 @@ namespace {
GENRE_ARTISTS,
GENRE_TRACKS,
GENRE_ALBUMS,
+ GENRE_COVER
};
}
@@ -59,6 +60,8 @@ QVariant MLGenreModel::data(const QModelIndex &index, int role) const
return QVariant::fromValue( ml_genre->getName() );
case GENRE_NB_TRACKS:
return QVariant::fromValue( ml_genre->getNbTracks() );
+ case GENRE_COVER:
+ return QVariant::fromValue( ml_genre->getCover() );
default :
return QVariant();
}
@@ -72,7 +75,8 @@ QHash<int, QByteArray> MLGenreModel::roleNames() const
{ GENRE_NB_TRACKS, "nb_tracks" },
{ GENRE_ARTISTS, "artists" },
{ GENRE_TRACKS, "tracks" },
- { GENRE_ALBUMS, "albums" }
+ { GENRE_ALBUMS, "albums" },
+ { GENRE_COVER, "cover" }
};
}
@@ -85,7 +89,7 @@ std::vector<std::unique_ptr<MLGenre>> MLGenreModel::fetch()
return {};
std::vector<std::unique_ptr<MLGenre>> res;
for( const vlc_ml_genre_t& genre: ml_range_iterate<vlc_ml_genre_t>( genre_list ) )
- res.emplace_back( std::unique_ptr<MLGenre>{ new MLGenre( &genre ) } );
+ res.emplace_back( std::unique_ptr<MLGenre>{ new MLGenre( m_ml, &genre ) } );
return res;
}
--
2.17.1
More information about the vlc-devel
mailing list