Save embedded cover in the same process as tags

Possible fix for #1158
This commit is contained in:
Jonas Kvinge
2023-03-18 20:03:07 +01:00
parent 394955a03f
commit e20cbe4170
42 changed files with 1205 additions and 723 deletions

View File

@@ -0,0 +1,46 @@
/*
* Strawberry Music Player
* Copyright 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
* 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 COVEROPTIONS_H
#define COVEROPTIONS_H
class CoverOptions {
public:
enum class CoverType {
Cache = 1,
Album = 2,
Embedded = 3
};
enum class CoverFilename {
Hash = 1,
Pattern = 2
};
explicit CoverOptions() : cover_type(CoverType::Cache), cover_filename(CoverFilename::Hash), cover_overwrite(false), cover_lowercase(true), cover_replace_spaces(true) {}
CoverType cover_type;
CoverFilename cover_filename;
QString cover_pattern;
bool cover_overwrite;
bool cover_lowercase;
bool cover_replace_spaces;
};
#endif // COVEROPTIONS_H

View File

@@ -0,0 +1,166 @@
/*
* Strawberry Music Player
* 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
* 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 <QByteArray>
#include <QString>
#include <QRegularExpression>
#include <QUrl>
#include <QDir>
#include <QStandardPaths>
#include <QCryptographicHash>
#include "filenameconstants.h"
#include "transliterate.h"
#include "coverutils.h"
#include "core/logging.h"
QByteArray CoverUtils::Sha1CoverHash(const QString &artist, const QString &album) {
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(artist.toLower().toUtf8());
hash.addData(album.toLower().toUtf8());
return hash.result();
}
QString CoverUtils::AlbumCoverFilename(QString artist, QString album, const QString &extension) {
artist.remove('/').remove('\\');
album.remove('/').remove('\\');
QString filename = artist + "-" + album;
filename = Utilities::Transliterate(filename.toLower());
filename = filename.replace(' ', '-')
.replace("--", "-")
.remove(QRegularExpression(QString(kInvalidFatCharactersRegex), QRegularExpression::CaseInsensitiveOption))
.simplified();
if (!extension.isEmpty()) {
filename.append('.');
filename.append(extension);
}
return filename;
}
QString CoverUtils::CoverFilePath(const CoverOptions &options, const Song &song, const QString &album_dir, const QUrl &cover_url, const QString &extension) {
return CoverFilePath(options, song.source(), song.effective_albumartist(), song.album(), song.album_id(), album_dir, cover_url, extension);
}
QString CoverUtils::CoverFilePath(const CoverOptions &options, const Song::Source source, const QString &artist, const QString &album, const QString &album_id, const QString &album_dir, const QUrl &cover_url, const QString &extension) {
QString path;
if (source == Song::Source::Collection && options.cover_type == CoverOptions::CoverType::Album && !album_dir.isEmpty()) {
path = album_dir;
}
else {
path = Song::ImageCacheDir(source);
}
if (path.right(1) == QDir::separator() || path.right(1) == "/") {
path.chop(1);
}
QDir dir;
if (!dir.mkpath(path)) {
qLog(Error) << "Unable to create directory" << path;
path = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
}
QString filename;
if (source == Song::Source::Collection &&
options.cover_type == CoverOptions::CoverType::Album &&
options.cover_filename == CoverOptions::CoverFilename::Pattern &&
!options.cover_pattern.isEmpty()) {
filename = CoverFilenameFromVariable(options, artist, album);
filename.remove(QRegularExpression(QString(kInvalidFatCharactersRegex), QRegularExpression::CaseInsensitiveOption)).remove('/').remove('\\');
if (options.cover_lowercase) filename = filename.toLower();
if (options.cover_replace_spaces) filename.replace(QRegularExpression("\\s"), "-");
if (!extension.isEmpty()) {
filename.append('.');
filename.append(extension);
}
}
if (filename.isEmpty()) {
filename = CoverFilenameFromSource(source, cover_url, artist, album, album_id, extension);
}
QString filepath(path + "/" + filename);
return filepath;
}
QString CoverUtils::CoverFilenameFromSource(const Song::Source source, const QUrl &cover_url, const QString &artist, const QString &album, const QString &album_id, const QString &extension) {
QString filename;
switch (source) {
case Song::Source::Tidal:
if (!album_id.isEmpty()) {
filename = album_id + "-" + cover_url.fileName();
break;
}
[[fallthrough]];
case Song::Source::Subsonic:
case Song::Source::Qobuz:
if (!album_id.isEmpty()) {
filename = album_id;
break;
}
[[fallthrough]];
case Song::Source::Collection:
case Song::Source::LocalFile:
case Song::Source::CDDA:
case Song::Source::Device:
case Song::Source::Stream:
case Song::Source::SomaFM:
case Song::Source::RadioParadise:
case Song::Source::Unknown:
filename = Sha1CoverHash(artist, album).toHex();
break;
}
if (!extension.isEmpty()) {
filename.append('.');
filename.append(extension);
}
return filename;
}
QString CoverUtils::CoverFilenameFromVariable(const CoverOptions &options, const QString &artist, QString album, const QString &extension) {
album = album.remove(Song::kAlbumRemoveDisc);
QString filename(options.cover_pattern);
filename.replace("%albumartist", artist);
filename.replace("%artist", artist);
filename.replace("%album", album);
if (!extension.isEmpty()) {
filename.append('.');
filename.append(extension);
}
return filename;
}

View File

@@ -0,0 +1,42 @@
/*
* Strawberry Music Player
* 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
* 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 COVERUTILS_H
#define COVERUTILS_H
#include <QByteArray>
#include <QString>
#include <QUrl>
#include "core/song.h"
#include "coveroptions.h"
class CoverUtils {
public:
static QByteArray Sha1CoverHash(const QString &artist, const QString &album);
static QString AlbumCoverFilename(QString artist, QString album, const QString &extension);
static QString CoverFilenameFromSource(const Song::Source source, const QUrl &cover_url, const QString &artist, const QString &album, const QString &album_id, const QString &extension);
static QString CoverFilenameFromVariable(const CoverOptions &options, const QString &artist, QString album, const QString &extension = QString());
static QString CoverFilePath(const CoverOptions &options, const Song &song, const QString &album_dir, const QUrl &cover_url, const QString &extension = QString());
static QString CoverFilePath(const CoverOptions &options, const Song::Source source, const QString &artist, const QString &album, const QString &album_id, const QString &album_dir, const QUrl &cover_url, const QString &extension = QString());
};
#endif // COVERUTILS_H

View File

@@ -62,14 +62,4 @@ QByteArray HmacSha1(const QByteArray &key, const QByteArray &data) {
return Hmac(key, data, QCryptographicHash::Sha1);
}
QByteArray Sha1CoverHash(const QString &artist, const QString &album) {
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(artist.toLower().toUtf8());
hash.addData(album.toLower().toUtf8());
return hash.result();
}
} // namespace Utilities

View File

@@ -30,7 +30,6 @@ QByteArray Hmac(const QByteArray &key, const QByteArray &data, const QCryptograp
QByteArray HmacMd5(const QByteArray &key, const QByteArray &data);
QByteArray HmacSha256(const QByteArray &key, const QByteArray &data);
QByteArray HmacSha1(const QByteArray &key, const QByteArray &data);
QByteArray Sha1CoverHash(const QString &artist, const QString &album);
} // namespace Utilities

View File

@@ -0,0 +1,31 @@
/*
* Strawberry Music Player
* Copyright 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
* 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 FILENAMECONSTANTS_H
#define FILENAMECONSTANTS_H
#include "core/arraysize.h"
constexpr char kProblematicCharactersRegex[] = "[:?*\"<>|]";
constexpr char kInvalidFatCharactersRegex[] = "[^a-zA-Z0-9!#\\$%&'()\\-@\\^_`{}~/. ]";
constexpr char kInvalidDirCharactersRegex[] = "[/\\\\]";
constexpr char kInvalidPrefixCharacters[] = ".";
constexpr int kInvalidPrefixCharactersCount = arraysize(kInvalidPrefixCharacters) - 1;
#endif // FILENAMECONSTANTS_H