Rewrite album cover loader

This commit is contained in:
Jonas Kvinge
2023-05-14 11:34:55 +02:00
parent 3c160c2f13
commit 331aa382f9
68 changed files with 2948 additions and 2565 deletions

View File

@@ -1,8 +1,6 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019-2021, Jonas Kvinge <jonas@jkvinge.net>
* Copyright 2019-2023, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,21 +22,19 @@
#include "config.h"
#include <memory>
#include <QtGlobal>
#include <QObject>
#include <QMutex>
#include <QPair>
#include <QSet>
#include <QHash>
#include <QMultiMap>
#include <QQueue>
#include <QByteArray>
#include <QString>
#include <QImage>
#include <QPixmap>
#include "core/song.h"
#include "core/tagreaderclient.h"
#include "albumcoverloaderoptions.h"
#include "albumcoverloaderresult.h"
#include "albumcoverimageresult.h"
@@ -47,111 +43,96 @@ class QThread;
class QNetworkReply;
class NetworkAccessManager;
using std::shared_ptr;
class AlbumCoverLoader : public QObject {
Q_OBJECT
public:
explicit AlbumCoverLoader(QObject *parent = nullptr);
enum class State {
None,
Manual,
Automatic
};
void ExitAsync();
void Stop() { stop_requested_ = true; }
quint64 LoadImageAsync(const AlbumCoverLoaderOptions &options, const Song &song);
quint64 LoadImageAsync(const AlbumCoverLoaderOptions &options, const QUrl &art_automatic, const QUrl &art_manual, const QUrl &song_url = QUrl(), const Song::Source song_source = Song::Source::Unknown);
quint64 LoadImageAsync(const AlbumCoverLoaderOptions &options, const bool art_embedded, const QUrl &art_automatic, const QUrl &art_manual, const bool art_unset, const QUrl &song_url = QUrl(), const Song::Source song_source = Song::Source::Unknown);
quint64 LoadImageAsync(const AlbumCoverLoaderOptions &options, const AlbumCoverImageResult &album_cover);
quint64 LoadImageAsync(const AlbumCoverLoaderOptions &options, const QImage &image);
void CancelTask(const quint64 id);
void CancelTasks(const QSet<quint64> &ids);
quint64 SaveEmbeddedCoverAsync(const QString &song_filename, const QString &cover_filename);
quint64 SaveEmbeddedCoverAsync(const QString &song_filename, const QImage &image);
quint64 SaveEmbeddedCoverAsync(const QString &song_filename, const QByteArray &image_data);
quint64 SaveEmbeddedCoverAsync(const QList<QUrl> &urls, const QString &cover_filename);
quint64 SaveEmbeddedCoverAsync(const QList<QUrl> &urls, const QImage &image);
quint64 SaveEmbeddedCoverAsync(const QList<QUrl> &urls, const QByteArray &image_data);
signals:
void ExitFinished();
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result);
void SaveEmbeddedCoverAsyncFinished(const quint64 id, const bool success, const bool cleared);
protected slots:
void Exit();
void ProcessTasks();
void RemoteFetchFinished(QNetworkReply *reply, const QUrl &cover_url);
private:
class Task {
public:
explicit Task() : id(0), success(false), art_embedded(false), art_unset(false), result_type(AlbumCoverLoaderResult::Type::None), art_updated(false), redirects(0) {}
void SaveEmbeddedCover(const quint64 id, const QString &song_filename, const QString &cover_filename);
void SaveEmbeddedCover(const quint64 id, const QString &song_filename, const QImage &image);
void SaveEmbeddedCover(const quint64 id, const QString &song_filename, const QByteArray &image_data);
void SaveEmbeddedCover(const quint64 id, const QList<QUrl> &urls, const QImage &image);
void SaveEmbeddedCover(const quint64 id, const QList<QUrl> &urls, const QString &cover_filename);
void SaveEmbeddedCover(const quint64 id, const QList<QUrl> &urls, const QByteArray &image_data);
void SaveEmbeddedArtFinished(const quint64 id, TagReaderReply *reply, const bool cleared);
protected:
struct Task {
explicit Task() : id(0), state(State::None), type(AlbumCoverLoaderResult::Type::None), art_updated(false), redirects(0) {}
quint64 id;
bool success;
AlbumCoverLoaderOptions options;
quint64 id;
bool raw_image_data() const { return options.options & AlbumCoverLoaderOptions::Option::RawImageData; }
bool original_image() const { return options.options & AlbumCoverLoaderOptions::Option::OriginalImage; }
bool scaled_image() const { return options.options & AlbumCoverLoaderOptions::Option::ScaledImage; }
bool pad_scaled_image() const { return options.options & AlbumCoverLoaderOptions::Option::PadScaledImage; }
bool art_embedded;
QUrl art_automatic;
QUrl art_manual;
bool art_unset;
QUrl song_url;
Song::Source song_source;
Song song;
AlbumCoverImageResult album_cover;
State state;
AlbumCoverLoaderResult::Type type;
AlbumCoverLoaderResult::Type result_type;
bool art_updated;
int redirects;
};
using TaskPtr = std::shared_ptr<Task>;
struct TryLoadResult {
explicit TryLoadResult(const bool _started_async = false,
const bool _loaded_success = false,
const AlbumCoverLoaderResult::Type _type = AlbumCoverLoaderResult::Type::None,
const AlbumCoverImageResult &_album_cover = AlbumCoverImageResult()) :
started_async(_started_async),
loaded_success(_loaded_success),
type(_type),
album_cover(_album_cover) {}
bool started_async;
bool loaded_success;
using TaskPtr = shared_ptr<Task>;
class LoadImageResult {
public:
enum class Status {
Failure,
Async,
Success
};
explicit LoadImageResult(AlbumCoverLoaderResult::Type _type = AlbumCoverLoaderResult::Type::None, Status _status = Status::Failure) : type(_type), status(_status) {}
AlbumCoverLoaderResult::Type type;
AlbumCoverImageResult album_cover;
Status status;
};
private:
quint64 EnqueueTask(TaskPtr task);
void ProcessTask(TaskPtr task);
void NextState(TaskPtr task);
TryLoadResult TryLoadImage(TaskPtr task);
void InitArt(TaskPtr task);
LoadImageResult LoadImage(TaskPtr task, const AlbumCoverLoaderOptions::Type &type);
LoadImageResult LoadEmbeddedImage(TaskPtr task);
LoadImageResult LoadUrlImage(TaskPtr task, const AlbumCoverLoaderResult::Type result_type, const QUrl &cover_url);
LoadImageResult LoadLocalUrlImage(TaskPtr task, const AlbumCoverLoaderResult::Type result_type, const QUrl &cover_url);
LoadImageResult LoadLocalFileImage(TaskPtr task, const AlbumCoverLoaderResult::Type result_type, const QString &cover_file);
LoadImageResult LoadRemoteUrlImage(TaskPtr task, const AlbumCoverLoaderResult::Type result_type, const QUrl &cover_url);
void FinishTask(TaskPtr task, const AlbumCoverLoaderResult::Type result_type);
NetworkAccessManager *network_;
bool stop_requested_;
QMutex mutex_load_image_async_;
QMutex mutex_save_image_async_;
QQueue<TaskPtr> tasks_;
QHash<QNetworkReply*, TaskPtr> remote_tasks_;
quint64 load_image_async_id_;
quint64 save_image_async_id_;
private slots:
void Exit();
void ProcessTasks();
void LoadRemoteImageFinished(QNetworkReply *reply, TaskPtr task, const AlbumCoverLoaderResult::Type result_type, const QUrl &cover_url);
private:
static const int kMaxRedirects = 3;
NetworkAccessManager *network_;
bool stop_requested_;
QMutex mutex_load_image_async_;
QQueue<TaskPtr> tasks_;
quint64 load_image_async_id_;
QThread *original_thread_;
QMultiMap<quint64, TagReaderReply*> tagreader_save_embedded_art_requests_;
};
#endif // ALBUMCOVERLOADER_H