194
src/core/imageutils.cpp
Normal file
194
src/core/imageutils.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2019, 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QBuffer>
|
||||
#include <QVariant>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QImageReader>
|
||||
#include <QPixmap>
|
||||
#include <QPainter>
|
||||
#include <QSize>
|
||||
#include <QSettings>
|
||||
|
||||
#include "imageutils.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/tagreaderclient.h"
|
||||
|
||||
QStringList ImageUtils::kSupportedImageMimeTypes;
|
||||
QStringList ImageUtils::kSupportedImageFormats;
|
||||
|
||||
QStringList ImageUtils::SupportedImageMimeTypes() {
|
||||
|
||||
if (kSupportedImageMimeTypes.isEmpty()) {
|
||||
for (const QByteArray &mimetype : QImageReader::supportedMimeTypes()) {
|
||||
kSupportedImageMimeTypes << mimetype;
|
||||
}
|
||||
}
|
||||
|
||||
return kSupportedImageMimeTypes;
|
||||
|
||||
}
|
||||
|
||||
QStringList ImageUtils::SupportedImageFormats() {
|
||||
|
||||
if (kSupportedImageFormats.isEmpty()) {
|
||||
for (const QByteArray &filetype : QImageReader::supportedImageFormats()) {
|
||||
kSupportedImageFormats << filetype;
|
||||
}
|
||||
}
|
||||
|
||||
return kSupportedImageFormats;
|
||||
|
||||
}
|
||||
|
||||
QList<QByteArray> ImageUtils::ImageFormatsForMimeType(const QByteArray &mimetype) {
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
|
||||
return QImageReader::imageFormatsForMimeType(mimetype);
|
||||
#else
|
||||
if (mimetype == "image/bmp") return QList<QByteArray>() << "BMP";
|
||||
else if (mimetype == "image/gif") return QList<QByteArray>() << "GIF";
|
||||
else if (mimetype == "image/jpeg") return QList<QByteArray>() << "JPG";
|
||||
else if (mimetype == "image/png") return QList<QByteArray>() << "PNG";
|
||||
else return QList<QByteArray>();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
QPixmap ImageUtils::TryLoadPixmap(const QUrl &art_automatic, const QUrl &art_manual, const QUrl &url) {
|
||||
|
||||
QPixmap ret;
|
||||
|
||||
if (!art_manual.path().isEmpty()) {
|
||||
if (art_manual.path() == Song::kManuallyUnsetCover) return ret;
|
||||
else if (art_manual.isLocalFile()) {
|
||||
ret.load(art_manual.toLocalFile());
|
||||
}
|
||||
else if (art_manual.scheme().isEmpty()) {
|
||||
ret.load(art_manual.path());
|
||||
}
|
||||
}
|
||||
if (ret.isNull() && !art_automatic.path().isEmpty()) {
|
||||
if (art_automatic.path() == Song::kEmbeddedCover && !url.isEmpty() && url.isLocalFile()) {
|
||||
ret = QPixmap::fromImage(TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(url.toLocalFile()));
|
||||
}
|
||||
else if (art_automatic.isLocalFile()) {
|
||||
ret.load(art_automatic.toLocalFile());
|
||||
}
|
||||
else if (art_automatic.scheme().isEmpty()) {
|
||||
ret.load(art_automatic.path());
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
QByteArray ImageUtils::SaveImageToJpegData(const QImage &image) {
|
||||
|
||||
if (image.isNull()) return QByteArray();
|
||||
|
||||
QByteArray image_data;
|
||||
QBuffer buffer(&image_data);
|
||||
if (buffer.open(QIODevice::WriteOnly)) {
|
||||
image.save(&buffer, "JPEG");
|
||||
buffer.close();
|
||||
}
|
||||
|
||||
return image_data;
|
||||
|
||||
}
|
||||
|
||||
QImage ImageUtils::ScaleAndPad(const QImage &image, const bool scale, const bool pad, const int desired_height) {
|
||||
|
||||
if (image.isNull()) return image;
|
||||
|
||||
// Scale the image down
|
||||
QImage image_scaled;
|
||||
if (scale) {
|
||||
image_scaled = image.scaled(QSize(desired_height, desired_height), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
else {
|
||||
image_scaled = image;
|
||||
}
|
||||
|
||||
// Pad the image to height x height
|
||||
if (pad) {
|
||||
QImage image_padded(desired_height, desired_height, QImage::Format_ARGB32);
|
||||
image_padded.fill(0);
|
||||
|
||||
QPainter p(&image_padded);
|
||||
p.drawImage((desired_height - image_scaled.width()) / 2, (desired_height - image_scaled.height()) / 2, image_scaled);
|
||||
p.end();
|
||||
|
||||
image_scaled = image_padded;
|
||||
}
|
||||
|
||||
return image_scaled;
|
||||
|
||||
}
|
||||
|
||||
QImage ImageUtils::CreateThumbnail(const QImage &image, const bool pad, const QSize size) {
|
||||
|
||||
if (image.isNull()) return image;
|
||||
|
||||
QImage image_thumbnail;
|
||||
if (pad) {
|
||||
image_thumbnail = image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
QImage image_padded(size, QImage::Format_ARGB32_Premultiplied);
|
||||
image_padded.fill(0);
|
||||
|
||||
QPainter p(&image_padded);
|
||||
p.drawImage((image_padded.width() - image_thumbnail.width()) / 2, (image_padded.height() - image_thumbnail.height()) / 2, image_thumbnail);
|
||||
p.end();
|
||||
|
||||
image_thumbnail = image_padded;
|
||||
}
|
||||
else {
|
||||
image_thumbnail = image.scaledToHeight(size.height(), Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
return image_thumbnail;
|
||||
|
||||
}
|
||||
|
||||
QImage ImageUtils::GenerateNoCoverImage(const QSize size) {
|
||||
|
||||
QImage image(":/pictures/cdcase.png");
|
||||
|
||||
// Get a square version of the nocover image with some transparency:
|
||||
QImage image_scaled = image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
QImage image_square(size, QImage::Format_ARGB32);
|
||||
image_square.fill(0);
|
||||
QPainter p(&image_square);
|
||||
p.setOpacity(0.4);
|
||||
p.drawImage((size.width() - image_scaled.width()) / 2, (size.height() - image_scaled.height()) / 2, image_scaled);
|
||||
p.end();
|
||||
|
||||
return image_square;
|
||||
|
||||
}
|
||||
51
src/core/imageutils.h
Normal file
51
src/core/imageutils.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2019, 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGEUTILS_H
|
||||
#define IMAGEUTILS_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
|
||||
class ImageUtils {
|
||||
|
||||
private:
|
||||
static QStringList kSupportedImageMimeTypes;
|
||||
static QStringList kSupportedImageFormats;
|
||||
|
||||
public:
|
||||
static QStringList SupportedImageMimeTypes();
|
||||
static QStringList SupportedImageFormats();
|
||||
static QList<QByteArray> ImageFormatsForMimeType(const QByteArray &mimetype);
|
||||
static QByteArray SaveImageToJpegData(const QImage &image = QImage());
|
||||
static QPixmap TryLoadPixmap(const QUrl &automatic, const QUrl &manual, const QUrl &url = QUrl());
|
||||
static QImage ScaleAndPad(const QImage &image, const bool scale, const bool pad, const int desired_height);
|
||||
static QImage CreateThumbnail(const QImage &image, const bool pad, const QSize size);
|
||||
static QImage GenerateNoCoverImage(const QSize size = QSize());
|
||||
|
||||
};
|
||||
|
||||
#endif // IMAGEUTILS_H
|
||||
@@ -143,6 +143,7 @@
|
||||
#include "covermanager/albumcoverloaderresult.h"
|
||||
#include "covermanager/currentalbumcoverloader.h"
|
||||
#include "covermanager/coverproviders.h"
|
||||
#include "covermanager/albumcoverimageresult.h"
|
||||
#include "lyrics/lyricsproviders.h"
|
||||
#ifndef Q_OS_WIN
|
||||
# include "device/devicemanager.h"
|
||||
@@ -613,6 +614,8 @@ MainWindow::MainWindow(Application *app, SystemTrayIcon *tray_icon, OSDBase *osd
|
||||
QObject::connect(album_cover_choice_controller_->cover_from_url_action(), &QAction::triggered, this, &MainWindow::LoadCoverFromURL);
|
||||
QObject::connect(album_cover_choice_controller_->search_for_cover_action(), &QAction::triggered, this, &MainWindow::SearchForCover);
|
||||
QObject::connect(album_cover_choice_controller_->unset_cover_action(), &QAction::triggered, this, &MainWindow::UnsetCover);
|
||||
QObject::connect(album_cover_choice_controller_->clear_cover_action(), &QAction::triggered, this, &MainWindow::ClearCover);
|
||||
QObject::connect(album_cover_choice_controller_->delete_cover_action(), &QAction::triggered, this, &MainWindow::DeleteCover);
|
||||
QObject::connect(album_cover_choice_controller_->show_cover_action(), &QAction::triggered, this, &MainWindow::ShowCover);
|
||||
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::triggered, this, &MainWindow::SearchCoverAutomatically);
|
||||
QObject::connect(album_cover_choice_controller_->search_cover_auto_action(), &QAction::toggled, this, &MainWindow::ToggleSearchCoverAuto);
|
||||
@@ -1234,7 +1237,7 @@ void MainWindow::MediaStopped() {
|
||||
|
||||
song_playing_ = Song();
|
||||
song_ = Song();
|
||||
image_original_ = QImage();
|
||||
album_cover_ = AlbumCoverImageResult();
|
||||
|
||||
app_->scrobbler()->ClearPlaying();
|
||||
|
||||
@@ -1312,6 +1315,14 @@ void MainWindow::SongChanged(const Song &song) {
|
||||
|
||||
SendNowPlaying();
|
||||
|
||||
const bool enable_cover_options = song.url().isLocalFile() && !song.effective_albumartist().isEmpty() && !song.album().isEmpty();
|
||||
album_cover_choice_controller_->cover_from_file_action()->setEnabled(enable_cover_options);
|
||||
album_cover_choice_controller_->cover_from_url_action()->setEnabled(enable_cover_options);
|
||||
album_cover_choice_controller_->search_for_cover_action()->setEnabled(enable_cover_options);
|
||||
album_cover_choice_controller_->unset_cover_action()->setEnabled(enable_cover_options);
|
||||
album_cover_choice_controller_->delete_cover_action()->setEnabled(enable_cover_options);
|
||||
album_cover_choice_controller_->clear_cover_action()->setEnabled(enable_cover_options);
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::TrackSkipped(PlaylistItemPtr item) {
|
||||
@@ -2073,7 +2084,7 @@ void MainWindow::RenumberTracks() {
|
||||
song.set_track(track);
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
|
||||
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); });
|
||||
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
|
||||
}
|
||||
++track;
|
||||
}
|
||||
@@ -2085,7 +2096,7 @@ void MainWindow::SongSaveComplete(TagReaderReply *reply, const QPersistentModelI
|
||||
if (reply->is_successful() && idx.isValid()) {
|
||||
app_->playlist_manager()->current()->ReloadItems(QList<int>()<< idx.row());
|
||||
}
|
||||
reply->deleteLater();
|
||||
metaObject()->invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
|
||||
|
||||
}
|
||||
|
||||
@@ -2104,7 +2115,7 @@ void MainWindow::SelectionSetValue() {
|
||||
if (Playlist::set_column_value(song, column, column_value)) {
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
|
||||
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); });
|
||||
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2914,15 +2925,23 @@ void MainWindow::SearchForCover() {
|
||||
}
|
||||
|
||||
void MainWindow::SaveCoverToFile() {
|
||||
album_cover_choice_controller_->SaveCoverToFileManual(song_, image_original_);
|
||||
album_cover_choice_controller_->SaveCoverToFileManual(song_, album_cover_);
|
||||
}
|
||||
|
||||
void MainWindow::UnsetCover() {
|
||||
album_cover_choice_controller_->UnsetCover(&song_);
|
||||
}
|
||||
|
||||
void MainWindow::ClearCover() {
|
||||
album_cover_choice_controller_->ClearCover(&song_);
|
||||
}
|
||||
|
||||
void MainWindow::DeleteCover() {
|
||||
album_cover_choice_controller_->DeleteCover(&song_);
|
||||
}
|
||||
|
||||
void MainWindow::ShowCover() {
|
||||
album_cover_choice_controller_->ShowCover(song_, image_original_);
|
||||
album_cover_choice_controller_->ShowCover(song_, album_cover_.image);
|
||||
}
|
||||
|
||||
void MainWindow::SearchCoverAutomatically() {
|
||||
@@ -2936,9 +2955,9 @@ void MainWindow::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult
|
||||
if (song != song_playing_) return;
|
||||
|
||||
song_ = song;
|
||||
image_original_ = result.image_original;
|
||||
album_cover_ = result.album_cover;
|
||||
|
||||
emit AlbumCoverReady(song, result.image_original);
|
||||
emit AlbumCoverReady(song, result.album_cover.image);
|
||||
|
||||
GetCoverAutomatically();
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#include "settings/settingsdialog.h"
|
||||
#include "settings/behavioursettingspage.h"
|
||||
#include "covermanager/albumcoverloaderresult.h"
|
||||
#include "covermanager/albumcoverimageresult.h"
|
||||
|
||||
class About;
|
||||
class Console;
|
||||
@@ -255,6 +256,8 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
void LoadCoverFromURL();
|
||||
void SearchForCover();
|
||||
void UnsetCover();
|
||||
void ClearCover();
|
||||
void DeleteCover();
|
||||
void ShowCover();
|
||||
void SearchCoverAutomatically();
|
||||
void AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult &result);
|
||||
@@ -386,7 +389,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
|
||||
|
||||
Song song_;
|
||||
Song song_playing_;
|
||||
QImage image_original_;
|
||||
AlbumCoverImageResult album_cover_;
|
||||
int exit_count_;
|
||||
bool delete_files_;
|
||||
|
||||
|
||||
@@ -115,10 +115,10 @@ void RegisterMetaTypes() {
|
||||
qRegisterMetaType<PlaylistSequence::ShuffleMode>("PlaylistSequence::ShuffleMode");
|
||||
qRegisterMetaType<AlbumCoverLoaderResult>("AlbumCoverLoaderResult");
|
||||
qRegisterMetaType<AlbumCoverLoaderResult::Type>("AlbumCoverLoaderResult::Type");
|
||||
qRegisterMetaType<CoverSearchResult>("CoverSearchResult");
|
||||
qRegisterMetaType<CoverProviderSearchResult>("CoverProviderSearchResult");
|
||||
qRegisterMetaType<CoverSearchStatistics>("CoverSearchStatistics");
|
||||
qRegisterMetaType<QList<CoverSearchResult> >("QList<CoverSearchResult>");
|
||||
qRegisterMetaType<CoverSearchResults>("CoverSearchResults");
|
||||
qRegisterMetaType<QList<CoverProviderSearchResult> >("QList<CoverProviderSearchResult>");
|
||||
qRegisterMetaType<CoverProviderSearchResults>("CoverProviderSearchResults");
|
||||
qRegisterMetaType<Equalizer::Params>("Equalizer::Params");
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
qRegisterMetaTypeStreamOperators<Equalizer::Params>("Equalizer::Params");
|
||||
|
||||
@@ -394,8 +394,8 @@ void Mpris2::AlbumCoverLoaded(const Song &song, const AlbumCoverLoaderResult &re
|
||||
AddMetadata("mpris:trackid", current_track_id(), &last_metadata_);
|
||||
|
||||
QUrl cover_url;
|
||||
if (result.cover_url.isValid() && result.cover_url.isLocalFile() && QFile(result.cover_url.toLocalFile()).exists()) {
|
||||
cover_url = result.cover_url;
|
||||
if (result.album_cover.cover_url.isValid() && result.album_cover.cover_url.isLocalFile() && QFile(result.album_cover.cover_url.toLocalFile()).exists()) {
|
||||
cover_url = result.album_cover.cover_url;
|
||||
}
|
||||
else if (result.temp_cover_url.isValid() && result.temp_cover_url.isLocalFile()) {
|
||||
cover_url = result.temp_cover_url;
|
||||
|
||||
@@ -183,7 +183,7 @@ class Mpris2 : public QObject {
|
||||
|
||||
// Methods
|
||||
void ActivatePlaylist(const QDBusObjectPath &playlist_id);
|
||||
QList<MprisPlaylist> GetPlaylists(quint32 index, quint32 max_count, const QString &order, bool reverse_order);
|
||||
MprisPlaylistList GetPlaylists(quint32 index, quint32 max_count, const QString &order, bool reverse_order);
|
||||
|
||||
signals:
|
||||
// Player
|
||||
|
||||
@@ -341,10 +341,22 @@ bool Song::compilation_on() const { return d->compilation_on_; }
|
||||
const QUrl &Song::art_automatic() const { return d->art_automatic_; }
|
||||
const QUrl &Song::art_manual() const { return d->art_manual_; }
|
||||
bool Song::has_manually_unset_cover() const { return d->art_manual_.path() == kManuallyUnsetCover; }
|
||||
void Song::manually_unset_cover() { d->art_manual_ = QUrl::fromLocalFile(kManuallyUnsetCover); }
|
||||
void Song::set_manually_unset_cover() { d->art_manual_ = QUrl::fromLocalFile(kManuallyUnsetCover); }
|
||||
bool Song::has_embedded_cover() const { return d->art_automatic_.path() == kEmbeddedCover; }
|
||||
void Song::set_embedded_cover() { d->art_automatic_ = QUrl::fromLocalFile(kEmbeddedCover); }
|
||||
|
||||
void Song::clear_art_automatic() { d->art_automatic_.clear(); }
|
||||
void Song::clear_art_manual() { d->art_manual_.clear(); }
|
||||
|
||||
bool Song::save_embedded_cover_supported(const FileType filetype) {
|
||||
|
||||
return filetype == FileType_FLAC ||
|
||||
filetype == FileType_OggVorbis ||
|
||||
filetype == FileType_MPEG ||
|
||||
filetype == FileType_MP4;
|
||||
|
||||
}
|
||||
|
||||
const QUrl &Song::stream_url() const { return d->stream_url_; }
|
||||
const QUrl &Song::effective_stream_url() const { return !d->stream_url_.isEmpty() && d->stream_url_.isValid() ? d->stream_url_ : d->url_; }
|
||||
const QImage &Song::image() const { return d->image_; }
|
||||
@@ -382,6 +394,8 @@ bool Song::art_manual_is_valid() const {
|
||||
);
|
||||
}
|
||||
|
||||
bool Song::has_valid_art() const { return art_automatic_is_valid() || art_manual_is_valid(); }
|
||||
|
||||
const QString &Song::error() const { return d->error_; }
|
||||
|
||||
void Song::set_id(int id) { d->id_ = id; }
|
||||
@@ -1054,13 +1068,19 @@ void Song::InitArtManual() {
|
||||
if (QFile::exists(path)) {
|
||||
d->art_manual_ = QUrl::fromLocalFile(path);
|
||||
}
|
||||
else if (d->url_.isLocalFile()) { // Pick the first image file in the album directory.
|
||||
QFileInfo file(d->url_.toLocalFile());
|
||||
QDir dir(file.path());
|
||||
QStringList files = dir.entryList(QStringList() << "*.jpg" << "*.png" << "*.gif" << "*.jpeg", QDir::Files|QDir::Readable, QDir::Name);
|
||||
if (files.count() > 0) {
|
||||
d->art_manual_ = QUrl::fromLocalFile(file.path() + QDir::separator() + files.first());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Song::InitArtAutomatic() {
|
||||
|
||||
if (d->source_ == Source_LocalFile && d->url_.isLocalFile() && d->art_automatic_.isEmpty()) {
|
||||
// Pick the first image file in the album directory.
|
||||
QFileInfo file(d->url_.toLocalFile());
|
||||
QDir dir(file.path());
|
||||
QStringList files = dir.entryList(QStringList() << "*.jpg" << "*.png" << "*.gif" << "*.jpeg", QDir::Files|QDir::Readable, QDir::Name);
|
||||
if (files.count() > 0) {
|
||||
d->art_automatic_ = QUrl::fromLocalFile(file.path() + QDir::separator() + files.first());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1491,11 +1511,15 @@ bool Song::IsMetadataEqual(const Song &other) const {
|
||||
d->bitrate_ == other.d->bitrate_ &&
|
||||
d->samplerate_ == other.d->samplerate_ &&
|
||||
d->bitdepth_ == other.d->bitdepth_ &&
|
||||
d->art_automatic_ == other.d->art_automatic_ &&
|
||||
d->art_manual_ == other.d->art_manual_ &&
|
||||
d->cue_path_ == other.d->cue_path_;
|
||||
}
|
||||
|
||||
bool Song::IsMetadataAndArtEqual(const Song &other) const {
|
||||
|
||||
return IsMetadataEqual(other) && d->art_automatic_ == other.d->art_automatic_ && d->art_manual_ == other.d->art_manual_;
|
||||
|
||||
}
|
||||
|
||||
bool Song::IsEditable() const {
|
||||
return d->valid_ && !d->url_.isEmpty() && !is_stream() && d->source_ != Source_Unknown && d->filetype_ != FileType_Unknown && !has_cue();
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ class Song {
|
||||
void InitFromQuery(const SqlRow &query, bool reliable_metadata, int col = 0);
|
||||
void InitFromFilePartial(const QString &filename); // Just store the filename: incomplete but fast
|
||||
void InitArtManual(); // Check if there is already a art in the cache and store the filename in art_manual
|
||||
void InitArtAutomatic();
|
||||
|
||||
bool MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle);
|
||||
|
||||
@@ -255,6 +256,7 @@ class Song {
|
||||
bool is_metadata_good() const;
|
||||
bool art_automatic_is_valid() const;
|
||||
bool art_manual_is_valid() const;
|
||||
bool has_valid_art() const;
|
||||
bool is_compilation() const;
|
||||
|
||||
// Playlist views are special because you don't want to fill in album artists automatically for compilations, but you do for normal albums:
|
||||
@@ -264,13 +266,19 @@ class Song {
|
||||
// Returns true if this Song had it's cover manually unset by user.
|
||||
bool has_manually_unset_cover() const;
|
||||
// This method represents an explicit request to unset this song's cover.
|
||||
void manually_unset_cover();
|
||||
void set_manually_unset_cover();
|
||||
|
||||
// Returns true if this song (it's media file) has an embedded cover.
|
||||
bool has_embedded_cover() const;
|
||||
// Sets a flag saying that this song (it's media file) has an embedded cover.
|
||||
void set_embedded_cover();
|
||||
|
||||
void clear_art_automatic();
|
||||
void clear_art_manual();
|
||||
|
||||
static bool save_embedded_cover_supported(const FileType filetype);
|
||||
bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); };
|
||||
|
||||
const QUrl &stream_url() const;
|
||||
const QUrl &effective_stream_url() const;
|
||||
const QImage &image() const;
|
||||
@@ -355,6 +363,7 @@ class Song {
|
||||
|
||||
// Comparison functions
|
||||
bool IsMetadataEqual(const Song &other) const;
|
||||
bool IsMetadataAndArtEqual(const Song &other) const;
|
||||
bool IsOnSameAlbum(const Song &other) const;
|
||||
bool IsSimilar(const Song &other) const;
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ void StandardItemIconLoader::AlbumCoverLoaded(const quint64 id, const AlbumCover
|
||||
QStandardItem *item = pending_covers_.take(id);
|
||||
if (!item) return;
|
||||
|
||||
if (!result.image_scaled.isNull()) {
|
||||
if (result.success && !result.image_scaled.isNull() && result.type != AlbumCoverLoaderResult::Type_ManuallyUnset) {
|
||||
item->setIcon(QIcon(QPixmap::fromImage(result.image_scaled)));
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,24 @@ bool TagReaderClient::IsMediaFileBlocking(const QString &filename) {
|
||||
|
||||
}
|
||||
|
||||
QImage TagReaderClient::LoadEmbeddedArtBlocking(const QString &filename) {
|
||||
QByteArray TagReaderClient::LoadEmbeddedArtBlocking(const QString &filename) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
QByteArray ret;
|
||||
|
||||
TagReaderReply *reply = LoadEmbeddedArt(filename);
|
||||
if (reply->WaitForFinished()) {
|
||||
const std::string &data_str = reply->message().load_embedded_art_response().data();
|
||||
ret = QByteArray(data_str.data(), data_str.size());
|
||||
}
|
||||
metaObject()->invokeMethod(reply, "deleteLater", Qt::QueuedConnection);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
QImage TagReaderClient::LoadEmbeddedArtAsImageBlocking(const QString &filename) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ class TagReaderClient : public QObject {
|
||||
void ReadFileBlocking(const QString &filename, Song *song);
|
||||
bool SaveFileBlocking(const QString &filename, const Song &metadata);
|
||||
bool IsMediaFileBlocking(const QString &filename);
|
||||
QImage LoadEmbeddedArtBlocking(const QString &filename);
|
||||
QByteArray LoadEmbeddedArtBlocking(const QString &filename);
|
||||
QImage LoadEmbeddedArtAsImageBlocking(const QString &filename);
|
||||
bool SaveEmbeddedArtBlocking(const QString &filename, const QByteArray &data);
|
||||
|
||||
// TODO: Make this not a singleton
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
#include <QtEvents>
|
||||
#include <QMessageBox>
|
||||
#include <QNetworkInterface>
|
||||
#include <QImageReader>
|
||||
#include <QMimeDatabase>
|
||||
#include <QtDebug>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
# include <QRandomGenerator>
|
||||
@@ -102,9 +102,6 @@
|
||||
|
||||
namespace Utilities {
|
||||
|
||||
QStringList kSupportedImageMimeTypes;
|
||||
QStringList kSupportedImageFormats;
|
||||
|
||||
static QString tr(const char *str) {
|
||||
return QCoreApplication::translate("", str);
|
||||
}
|
||||
@@ -945,41 +942,23 @@ bool IsColorDark(const QColor &color) {
|
||||
return ((30 * color.red() + 59 * color.green() + 11 * color.blue()) / 100) <= 130;
|
||||
}
|
||||
|
||||
QStringList SupportedImageMimeTypes() {
|
||||
QByteArray ReadDataFromFile(const QString &filename) {
|
||||
|
||||
if (kSupportedImageMimeTypes.isEmpty()) {
|
||||
for (const QByteArray &mimetype : QImageReader::supportedMimeTypes()) {
|
||||
kSupportedImageMimeTypes << mimetype;
|
||||
}
|
||||
QFile file(filename);
|
||||
QByteArray data;
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
data = file.readAll();
|
||||
file.close();
|
||||
}
|
||||
|
||||
return kSupportedImageMimeTypes;
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
QStringList SupportedImageFormats() {
|
||||
QString MimeTypeFromData(const QByteArray &data) {
|
||||
|
||||
if (kSupportedImageFormats.isEmpty()) {
|
||||
for (const QByteArray &filetype : QImageReader::supportedImageFormats()) {
|
||||
kSupportedImageFormats << filetype;
|
||||
}
|
||||
}
|
||||
if (data.isEmpty()) return QString();
|
||||
|
||||
return kSupportedImageFormats;
|
||||
|
||||
}
|
||||
|
||||
QList<QByteArray> ImageFormatsForMimeType(const QByteArray &mimetype) {
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
|
||||
return QImageReader::imageFormatsForMimeType(mimetype);
|
||||
#else
|
||||
if (mimetype == "image/bmp") return QList<QByteArray>() << "BMP";
|
||||
else if (mimetype == "image/gif") return QList<QByteArray>() << "GIF";
|
||||
else if (mimetype == "image/jpeg") return QList<QByteArray>() << "JPG";
|
||||
else if (mimetype == "image/png") return QList<QByteArray>() << "PNG";
|
||||
else return QList<QByteArray>();
|
||||
#endif
|
||||
return QMimeDatabase().mimeTypeForData(data).name();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -143,9 +143,8 @@ QString ReplaceVariable(const QString &variable, const Song &song, const QString
|
||||
|
||||
bool IsColorDark(const QColor &color);
|
||||
|
||||
QStringList SupportedImageMimeTypes();
|
||||
QStringList SupportedImageFormats();
|
||||
QList<QByteArray> ImageFormatsForMimeType(const QByteArray &mimetype);
|
||||
QByteArray ReadDataFromFile(const QString &filename);
|
||||
QString MimeTypeFromData(const QByteArray &data);
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
Reference in New Issue
Block a user