1 /* 2 * Strawberry Music Player 3 * This file was part of Clementine. 4 * Copyright 2010, David Sansome <me@davidsansome.com> 5 * Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net> 6 * 7 * Strawberry is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * Strawberry is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with Strawberry. If not, see <http://www.gnu.org/licenses/>. 19 * 20 */ 21 22 #include "config.h" 23 24 #include <QFile> 25 #include <QSize> 26 #include <QString> 27 #include <QStringBuilder> 28 #include <QUrl> 29 #include <QImage> 30 31 #include "core/song.h" 32 #include "core/tagreaderclient.h" 33 #include "albumcoverexport.h" 34 #include "coverexportrunnable.h" 35 36 CoverExportRunnable::CoverExportRunnable(const AlbumCoverExport::DialogResult &dialog_result, const Song &song, QObject *parent) 37 : QObject(parent), 38 dialog_result_(dialog_result), 39 song_(song) {} 40 41 void CoverExportRunnable::run() { 42 43 QString cover_path = GetCoverPath(); 44 45 // Manually unset? 46 if (cover_path.isEmpty()) { 47 EmitCoverSkipped(); 48 } 49 else { 50 if (dialog_result_.RequiresCoverProcessing()) { 51 ProcessAndExportCover(); 52 } 53 else { 54 ExportCover(); 55 } 56 } 57 58 } 59 60 QString CoverExportRunnable::GetCoverPath() { 61 62 if (song_.has_manually_unset_cover()) { 63 return QString(); 64 // Export downloaded covers? 65 } 66 else if (!song_.art_manual().isEmpty() && dialog_result_.export_downloaded_) { 67 return song_.art_manual().toLocalFile(); 68 // Export embedded covers? 69 } 70 else if (!song_.art_automatic().isEmpty() && song_.art_automatic().path() == Song::kEmbeddedCover && dialog_result_.export_embedded_) { 71 return song_.art_automatic().toLocalFile(); 72 } SizeOverlayDelegate(QObject * parent)73 else { 74 return QString(); 75 } paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & idx) const76 77 } 78 79 // Exports a single album cover using a "save QImage to file" approach. 80 // For performance reasons this method will be invoked only if loading and in memory processing of images is necessary for current settings which means that: 81 // - either the force size flag is being used 82 // - or the "overwrite smaller" mode is used 83 // In all other cases, the faster ExportCover() method will be used. 84 void CoverExportRunnable::ProcessAndExportCover() { 85 86 QString cover_path = GetCoverPath(); 87 88 // either embedded or disk - the one we'll export for the current album 89 QImage cover; 90 91 QImage embedded_cover; 92 QImage disk_cover; 93 94 // load embedded cover if any 95 if (song_.has_embedded_cover()) { 96 embedded_cover = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(song_.url().toLocalFile()); 97 98 if (embedded_cover.isNull()) { 99 EmitCoverSkipped(); 100 return; 101 } 102 cover = embedded_cover; 103 } 104 105 // load a file cover which iss mandatory if there's no embedded cover 106 disk_cover.load(cover_path); 107 108 if (embedded_cover.isNull()) { 109 if (disk_cover.isNull()) { 110 EmitCoverSkipped(); 111 return; 112 } 113 cover = disk_cover; 114 } 115 116 // rescale if necessary 117 if (dialog_result_.IsSizeForced()) { 118 cover = cover.scaled(QSize(dialog_result_.width_, dialog_result_.height_), Qt::IgnoreAspectRatio); 119 } 120 121 QString dir = song_.url().toLocalFile().section('/', 0, -2); 122 QString extension = cover_path.section('.', -1); 123 124 QString new_file = dir + '/' + dialog_result_.filename_ + '.' + (cover_path == Song::kEmbeddedCover ? "jpg" : extension); 125 126 // If the file exists, do not override! 127 if (dialog_result_.overwrite_ == AlbumCoverExport::OverwriteMode_None && QFile::exists(new_file)) { 128 EmitCoverSkipped(); 129 return; 130 } 131 132 // we're handling overwrite as remove + copy so we need to delete the old file first 133 if (QFile::exists(new_file) && dialog_result_.overwrite_ != AlbumCoverExport::OverwriteMode_None) { 134 135 // if the mode is "overwrite smaller" then skip the cover if a bigger one is already available in the folder 136 if (dialog_result_.overwrite_ == AlbumCoverExport::OverwriteMode_Smaller) { 137 QImage existing; 138 existing.load(new_file); 139 140 if (existing.isNull() || existing.size().height() >= cover.size().height() || existing.size().width() >= cover.size().width()) { 141 142 EmitCoverSkipped(); 143 return; 144 } 145 } 146 147 if (!QFile::remove(new_file)) { 148 EmitCoverSkipped(); 149 return; 150 } 151 } 152 153 if (cover.save(new_file)) { 154 EmitCoverExported(); 155 } 156 else { 157 EmitCoverSkipped(); 158 } 159 160 } 161 162 // Exports a single album cover using a "copy file" approach. 163 void CoverExportRunnable::ExportCover() { Exec(const QString & artist,const QString & album)164 165 QString cover_path = GetCoverPath(); 166 167 QString dir = song_.url().toLocalFile().section('/', 0, -2); 168 QString extension = cover_path.section('.', -1); 169 170 QString new_file = dir + '/' + dialog_result_.filename_ + '.' + (cover_path == Song::kEmbeddedCover ? "jpg" : extension); 171 172 // If the file exists, do not override! 173 if (dialog_result_.overwrite_ == AlbumCoverExport::OverwriteMode_None && QFile::exists(new_file)) { 174 EmitCoverSkipped(); 175 return; 176 } 177 178 // We're handling overwrite as remove + copy so we need to delete the old file first 179 if (dialog_result_.overwrite_ != AlbumCoverExport::OverwriteMode_None && QFile::exists(new_file)) { 180 if (!QFile::remove(new_file)) { 181 EmitCoverSkipped(); 182 return; 183 } 184 } 185 186 if (cover_path == Song::kEmbeddedCover) { 187 // an embedded cover 188 QImage embedded = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(song_.url().toLocalFile()); Search()189 if (!embedded.save(new_file)) { 190 EmitCoverSkipped(); 191 return; 192 } 193 } 194 else { 195 // Automatic or manual cover, available in an image file 196 if (!QFile::copy(cover_path, new_file)) { 197 EmitCoverSkipped(); 198 return; 199 } 200 } 201 EmitCoverExported(); 202 203 } 204 205 void CoverExportRunnable::EmitCoverExported() { emit CoverExported(); } 206 207 void CoverExportRunnable::EmitCoverSkipped() { emit CoverSkipped(); } 208