Commit 73072c14 authored by Benjamin Arnaud's avatar Benjamin Arnaud Committed by Hugo Beauzée-Luyssen
Browse files

qt/mlgenre: Add CoverGenerator implementation

parent e6ebc5f8
......@@ -345,7 +345,6 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/medialibrary/mlartistmodel.moc.cpp \
gui/qt/medialibrary/mlbasemodel.moc.cpp \
gui/qt/medialibrary/mlfoldersmodel.moc.cpp \
gui/qt/medialibrary/mlgenre.moc.cpp \
gui/qt/medialibrary/mlgenremodel.moc.cpp \
gui/qt/medialibrary/mlgrouplistmodel.moc.cpp \
gui/qt/medialibrary/mlqmltypes.moc.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<MLGenre>("org.videolan.medialib", 1);
registerAnonymousType<MLPlaylist>("org.videolan.medialib", 1);
qmlRegisterType<AlbumContextMenu>( "org.videolan.medialib", 0, 1, "AlbumContextMenu" );
......
......@@ -16,220 +16,27 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include <cassert>
#include <QPainter>
#include <QImage>
#include <QThreadPool>
#include <QMutex>
#include <QWaitCondition>
#include <QDir>
#include <QGradient>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QGraphicsBlurEffect>
#include <algorithm>
#include "mlgenre.hpp"
#include "qt.hpp"
namespace {
#define THUMBNAIL_WIDTH 260
#define THUMBNAIL_HEIGHT 130
QImage blurImage(const QImage& src)
{
QGraphicsScene scene;
QGraphicsPixmapItem item;
item.setPixmap(QPixmap::fromImage(src));
QGraphicsBlurEffect blurEffect;
blurEffect.setBlurRadius(4);
blurEffect.setBlurHints(QGraphicsBlurEffect::QualityHint);
item.setGraphicsEffect(&blurEffect);
scene.addItem(&item);
QImage res(src.size(), QImage::Format_ARGB32);
QPainter ptr(&res);
scene.render(&ptr);
return res;
}
MLGenre::MLGenre(vlc_medialibrary_t* ml, const vlc_ml_genre_t *_data )
: MLItem ( 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 )
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 thumbnails;
thumbnails.reserve(8);
for( const vlc_ml_album_t& media: ml_range_iterate<vlc_ml_album_t>( album_list ) ) {
if (media.thumbnails[VLC_ML_THUMBNAIL_SMALL].i_status ==
VLC_ML_THUMBNAIL_STATUS_AVAILABLE) {
QUrl mediaURL( media.thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl );
//QImage only accept local file
if (mediaURL.isValid() && mediaURL.isLocalFile()) {
thumbnails.append(mediaURL.path());
if (thumbnails.size() == 8)
break;
}
}
}
if (thumbnails.empty()) {
thumbnails.append(":/noart_album.svg");
}
assert(thumbnails.size() <= 8);
std::copy(thumbnails.begin(), ( thumbnails.begin() + ( 8 - thumbnails.size() ) ), std::back_inserter(thumbnails));
assert(thumbnails.size() == 8);
{
QMutexLocker lock(&m_taskLock);
if (m_canceled) {
m_running = false;
m_taskCond.wakeAll();
return;
}
}
QImage image(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, QImage::Format_RGB32);
image.fill(Qt::white);
QPainter painter;
painter.begin(&image);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 4; j++) {
drawRegion(painter, thumbnails[2*j+1], QRect( ( THUMBNAIL_WIDTH / 4 ) * j, ( THUMBNAIL_HEIGHT / 2 ) * i, THUMBNAIL_WIDTH / 4, THUMBNAIL_HEIGHT / 2 ));
}
}
painter.end();
image = blurImage(image);
QLinearGradient gradient;
gradient.setColorAt(0, QColor(0, 0, 0, 255*.3));
gradient.setColorAt(1, QColor(0, 0, 0, 255*.7));
painter.begin(&image);
painter.setOpacity(.7);
painter.fillRect(image.rect(), gradient);
painter.end();
if (image.save(m_filepath, "jpg"))
/* Set the cover from the main thread */
QMetaObject::invokeMethod(m_genre, [genre = m_genre, cover = QUrl::fromLocalFile(m_filepath).toString()]
{
genre->setCover(std::move(cover));
});
{
QMutexLocker lock(&m_taskLock);
m_running = false;
m_taskCond.wakeAll();
}
}
void cancel()
{
QMutexLocker lock(&m_taskLock);
m_canceled = true;
if (!m_running)
return;
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;
};
assert(_data);
}
MLGenre::MLGenre(vlc_medialibrary_t* ml, const vlc_ml_genre_t *_data, QObject *_parent )
: QObject(_parent)
, MLItem ( MLItemId( _data->i_id, VLC_ML_PARENT_GENRE ) )
, m_ml ( ml )
, m_name ( QString::fromUtf8( _data->psz_name ) )
, m_nbTracks ( (unsigned int)_data->i_nb_tracks )
bool MLGenre::hasGenerator() const
{
assert(_data);
connect(this, &MLGenre::askGenerateCover, this, &MLGenre::generateThumbnail);
return m_generator.get();
}
MLGenre::~MLGenre()
void MLGenre::setGenerator(CoverGenerator * generator)
{
if (m_coverTask) {
if (!QThreadPool::globalInstance()->tryTake(m_coverTask)) {
//task is done or running
static_cast<GenerateCoverTask*>(m_coverTask)->cancel();
}
delete m_coverTask;
}
m_generator.reset(generator);
}
QString MLGenre::getName() const
......@@ -244,43 +51,11 @@ unsigned int MLGenre::getNbTracks() const
QString MLGenre::getCover() const
{
if (!m_cover.isEmpty())
return m_cover;
if (!m_coverTask) {
emit askGenerateCover( QPrivateSignal() );
}
return m_cover;
}
void MLGenre::setCover(QString cover)
{
m_cover = cover;
//TODO store in media library
}
void MLGenre::generateThumbnail()
void MLGenre::setCover(const QString & fileName)
{
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(getId().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);
}
}
m_cover = fileName;
}
......@@ -22,33 +22,26 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "vlc_common.h"
#include <memory>
#include <QObject>
#include <QString>
#include <QList>
#include <QRunnable>
#include <vlc_media_library.h>
#include "mlhelper.hpp"
// Util includes
#include "util/covergenerator.hpp"
// MediaLibrary includes
#include "mlqmltypes.hpp"
class MLGenre : public QObject, public MLItem
class MLGenre : public MLItem
{
Q_OBJECT
public:
MLGenre( vlc_medialibrary_t* _ml, const vlc_ml_genre_t *_data, QObject *_parent = nullptr);
~MLGenre();
MLGenre( vlc_medialibrary_t* _ml, const vlc_ml_genre_t *_data );
bool hasGenerator() const;
void setGenerator(CoverGenerator * generator);
QString getName() const;
unsigned int getNbTracks() const;
QString getCover() const;
void setCover(QString cover);
signals:
void askGenerateCover( QPrivateSignal ) const;
QString getCover() const;
void setCover(const QString & fileName);
private slots:
void generateThumbnail();
......@@ -56,9 +49,11 @@ private slots:
private:
vlc_medialibrary_t* m_ml;
TaskHandle<CoverGenerator> m_generator;
QString m_name;
QString m_cover;
QRunnable* m_coverTask = nullptr;
unsigned int m_nbTracks;
};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment