Add better error handling for Tag reader
This commit is contained in:
parent
ad9f3ce078
commit
32baa95500
@ -271,16 +271,22 @@ if(USE_TAGLIB)
|
||||
else()
|
||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
|
||||
endif()
|
||||
set(HAVE_TAGLIB ON)
|
||||
else()
|
||||
set(HAVE_TAGLIB OFF)
|
||||
endif()
|
||||
|
||||
# TAGPARSER
|
||||
if(USE_TAGPARSER)
|
||||
pkg_check_modules(TAGPARSER REQUIRED tagparser)
|
||||
set(HAVE_TAGPARSER ON)
|
||||
else()
|
||||
set(HAVE_TAGPARSER OFF)
|
||||
endif()
|
||||
|
||||
pkg_check_modules(LIBEBUR128 IMPORTED_TARGET libebur128)
|
||||
|
||||
if(NOT TAGLIB_FOUND AND NOT TAGPARSER_FOUND)
|
||||
if(NOT HAVE_TAGLIB AND NOT HAVE_TAGPARSER)
|
||||
message(FATAL_ERROR "You need either TagLib or TagParser!")
|
||||
endif()
|
||||
|
||||
@ -535,6 +541,6 @@ if(NOT CMAKE_CROSSCOMPILING)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND AND NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
|
||||
if(HAVE_TAGLIB AND TAGLIB_FOUND AND NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
|
||||
message(WARNING "There is a critical bug in TagLib (1.11.1) that can result in corrupt Ogg files, see: https://github.com/taglib/taglib/issues/864, please consider updating TagLib to the newest version.")
|
||||
endif()
|
||||
|
@ -120,13 +120,13 @@ template<typename MT>
|
||||
void AbstractMessageHandler<MT>::SendMessage(const MessageType &message) {
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
std::string data = message.SerializeAsString();
|
||||
const std::string data = message.SerializeAsString();
|
||||
WriteMessage(QByteArray(data.data(), data.size()));
|
||||
}
|
||||
|
||||
template<typename MT>
|
||||
void AbstractMessageHandler<MT>::SendMessageAsync(const MessageType &message) {
|
||||
std::string data = message.SerializeAsString();
|
||||
const std::string data = message.SerializeAsString();
|
||||
QMetaObject::invokeMethod(this, "WriteMessage", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
|
||||
}
|
||||
|
||||
|
@ -11,11 +11,11 @@ endif()
|
||||
|
||||
set(SOURCES tagreaderbase.cpp tagreadermessages.proto)
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
if(HAVE_TAGLIB)
|
||||
list(APPEND SOURCES tagreadertaglib.cpp tagreadergme.cpp)
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
if(HAVE_TAGPARSER)
|
||||
list(APPEND SOURCES tagreadertagparser.cpp)
|
||||
endif()
|
||||
|
||||
@ -24,11 +24,11 @@ link_directories(
|
||||
${PROTOBUF_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
if(HAVE_TAGLIB)
|
||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
if(HAVE_TAGPARSER)
|
||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
@ -56,12 +56,12 @@ target_link_libraries(libstrawberry-tagreader PRIVATE
|
||||
libstrawberry-common
|
||||
)
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
if(HAVE_TAGLIB)
|
||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
if(HAVE_TAGPARSER)
|
||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
||||
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
||||
endif()
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||
Copyright 2018-2024, 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
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QIODevice>
|
||||
@ -32,6 +33,28 @@
|
||||
|
||||
TagReaderBase::TagReaderBase() = default;
|
||||
|
||||
QString TagReaderBase::ErrorString(const Result &result) {
|
||||
|
||||
switch (result.error_code) {
|
||||
case Result::ErrorCode::Success:
|
||||
return QObject::tr("Success");
|
||||
case Result::ErrorCode::Unsupported:
|
||||
return QObject::tr("File is unsupported");
|
||||
case Result::ErrorCode::FilenameMissing:
|
||||
return QObject::tr("Filename is missing");
|
||||
case Result::ErrorCode::FileOpenError:
|
||||
return QObject::tr("File can not be opened");
|
||||
case Result::ErrorCode::FileParseError:
|
||||
return QObject::tr("Could not parse file");
|
||||
case Result::ErrorCode::FileSaveError:
|
||||
return QObject::tr("Could save file");
|
||||
case Result::ErrorCode::CustomError:
|
||||
return result.error;
|
||||
}
|
||||
|
||||
return QObject::tr("Unknown error");
|
||||
|
||||
}
|
||||
|
||||
float TagReaderBase::ConvertPOPMRating(const int POPM_rating) {
|
||||
|
||||
@ -57,16 +80,15 @@ int TagReaderBase::ConvertToPOPMRating(const float rating) {
|
||||
|
||||
}
|
||||
|
||||
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::SaveFileRequest &request) {
|
||||
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const QString &song_filename, const spb::tagreader::WriteFileRequest &request) {
|
||||
|
||||
if (!request.has_save_cover() || !request.save_cover()) {
|
||||
return Cover();
|
||||
}
|
||||
|
||||
const QString song_filename = QString::fromUtf8(request.filename().data(), static_cast<qint64>(request.filename().size()));
|
||||
QString cover_filename;
|
||||
if (request.has_cover_filename()) {
|
||||
cover_filename = QString::fromUtf8(request.cover_filename().data(), static_cast<qint64>(request.cover_filename().size()));
|
||||
cover_filename = QString::fromStdString(request.cover_filename());
|
||||
}
|
||||
QByteArray cover_data;
|
||||
if (request.has_cover_data()) {
|
||||
@ -81,12 +103,11 @@ TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::S
|
||||
|
||||
}
|
||||
|
||||
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const spb::tagreader::SaveEmbeddedArtRequest &request) {
|
||||
TagReaderBase::Cover TagReaderBase::LoadCoverFromRequest(const QString &song_filename, const spb::tagreader::SaveEmbeddedArtRequest &request) {
|
||||
|
||||
const QString song_filename = QString::fromUtf8(request.filename().data(), static_cast<qint64>(request.filename().size()));
|
||||
QString cover_filename;
|
||||
if (request.has_cover_filename()) {
|
||||
cover_filename = QString::fromUtf8(request.cover_filename().data(), static_cast<qint64>(request.cover_filename().size()));
|
||||
cover_filename = QString::fromStdString(request.cover_filename());
|
||||
}
|
||||
QByteArray cover_data;
|
||||
if (request.has_cover_data()) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||
Copyright 2018-2024, 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
|
||||
@ -36,6 +36,23 @@ class TagReaderBase {
|
||||
explicit TagReaderBase();
|
||||
~TagReaderBase() = default;
|
||||
|
||||
class Result {
|
||||
public:
|
||||
enum class ErrorCode {
|
||||
Success,
|
||||
Unsupported,
|
||||
FilenameMissing,
|
||||
FileOpenError,
|
||||
FileParseError,
|
||||
FileSaveError,
|
||||
CustomError,
|
||||
};
|
||||
Result(const ErrorCode _error_code, const QString &_error = QString()) : error_code(_error_code), error(_error) {}
|
||||
ErrorCode error_code;
|
||||
QString error;
|
||||
bool success() const { return error_code == ErrorCode::Success; }
|
||||
};
|
||||
|
||||
class Cover {
|
||||
public:
|
||||
explicit Cover(const QByteArray &_data = QByteArray(), const QString &_mime_type = QString()) : data(_data), mime_type(_mime_type) {}
|
||||
@ -44,22 +61,25 @@ class TagReaderBase {
|
||||
QString error;
|
||||
};
|
||||
|
||||
static QString ErrorString(const Result &result);
|
||||
|
||||
virtual bool IsMediaFile(const QString &filename) const = 0;
|
||||
|
||||
virtual bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
|
||||
virtual bool SaveFile(const spb::tagreader::SaveFileRequest &request) const = 0;
|
||||
virtual Result ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
|
||||
virtual Result WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const = 0;
|
||||
|
||||
virtual QByteArray LoadEmbeddedArt(const QString &filename) const = 0;
|
||||
virtual bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const = 0;
|
||||
virtual Result LoadEmbeddedArt(const QString &filename, QByteArray &data) const = 0;
|
||||
virtual Result SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const = 0;
|
||||
|
||||
virtual bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
|
||||
virtual bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
|
||||
virtual Result SaveSongPlaycountToFile(const QString &filename, const uint playcount) const = 0;
|
||||
virtual Result SaveSongRatingToFile(const QString &filename, const float rating) const = 0;
|
||||
|
||||
protected:
|
||||
static float ConvertPOPMRating(const int POPM_rating);
|
||||
static int ConvertToPOPMRating(const float rating);
|
||||
|
||||
static Cover LoadCoverFromRequest(const spb::tagreader::SaveFileRequest &request);
|
||||
static Cover LoadCoverFromRequest(const spb::tagreader::SaveEmbeddedArtRequest &request);
|
||||
static Cover LoadCoverFromRequest(const QString &song_filename, const spb::tagreader::WriteFileRequest &request);
|
||||
static Cover LoadCoverFromRequest(const QString &song_filename, const spb::tagreader::SaveEmbeddedArtRequest &request);
|
||||
|
||||
private:
|
||||
static Cover LoadCoverFromRequest(const QString &song_filename, const QString &cover_filename, QByteArray cover_data, QString cover_mime_type);
|
||||
|
@ -35,22 +35,20 @@
|
||||
#include "tagreaderbase.h"
|
||||
#include "tagreadertaglib.h"
|
||||
|
||||
bool GME::IsSupportedFormat(const QFileInfo &file_info) {
|
||||
return file_info.exists() && (file_info.completeSuffix().endsWith(QLatin1String("spc"), Qt::CaseInsensitive) || file_info.completeSuffix().endsWith(QLatin1String("vgm")), Qt::CaseInsensitive);
|
||||
bool GME::IsSupportedFormat(const QFileInfo &fileinfo) {
|
||||
return fileinfo.exists() && (fileinfo.completeSuffix().endsWith(QLatin1String("spc"), Qt::CaseInsensitive) || fileinfo.completeSuffix().endsWith(QLatin1String("vgm")), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool GME::ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
|
||||
TagReaderBase::Result GME::ReadFile(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song) {
|
||||
|
||||
if (file_info.completeSuffix().endsWith(QLatin1String("spc")), Qt::CaseInsensitive) {
|
||||
SPC::Read(file_info, song_info);
|
||||
return true;
|
||||
if (fileinfo.completeSuffix().endsWith(QLatin1String("spc")), Qt::CaseInsensitive) {
|
||||
return SPC::Read(fileinfo, song);
|
||||
}
|
||||
if (file_info.completeSuffix().endsWith(QLatin1String("vgm"), Qt::CaseInsensitive)) {
|
||||
VGM::Read(file_info, song_info);
|
||||
return true;
|
||||
if (fileinfo.completeSuffix().endsWith(QLatin1String("vgm"), Qt::CaseInsensitive)) {
|
||||
return VGM::Read(fileinfo, song);
|
||||
}
|
||||
|
||||
return false;
|
||||
return TagReaderBase::Result::ErrorCode::Unsupported;
|
||||
|
||||
}
|
||||
|
||||
@ -67,15 +65,19 @@ quint32 GME::UnpackBytes32(const char *const bytes, size_t length) {
|
||||
|
||||
}
|
||||
|
||||
void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
|
||||
TagReaderBase::Result GME::SPC::Read(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song) {
|
||||
|
||||
QFile file(file_info.filePath());
|
||||
if (!file.open(QIODevice::ReadOnly)) return;
|
||||
QFile file(fileinfo.filePath());
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return TagReaderBase::Result(TagReaderBase::Result::ErrorCode::FileOpenError, file.errorString());
|
||||
}
|
||||
|
||||
qLog(Debug) << "Reading tags from SPC file" << file_info.fileName();
|
||||
qLog(Debug) << "Reading tags from SPC file" << fileinfo.fileName();
|
||||
|
||||
// Check for header -- more reliable than file name alone.
|
||||
if (!file.read(33).startsWith(QStringLiteral("SNES-SPC700").toLatin1())) return;
|
||||
if (!file.read(33).startsWith(QStringLiteral("SNES-SPC700").toLatin1())) {
|
||||
return TagReaderBase::Result::ErrorCode::Unsupported;
|
||||
}
|
||||
|
||||
// First order of business -- get any tag values that exist within the core file information.
|
||||
// These only allow for a certain number of bytes per field,
|
||||
@ -88,13 +90,13 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||
const bool has_id6 = id6_status.length() >= 1 && id6_status[0] == static_cast<char>(xID6_STATUS::ON);
|
||||
|
||||
file.seek(SONG_TITLE_OFFSET);
|
||||
song_info->set_title(QString::fromLatin1(file.read(32)).toStdString());
|
||||
song->set_title(QString::fromLatin1(file.read(32)).toStdString());
|
||||
|
||||
file.seek(GAME_TITLE_OFFSET);
|
||||
song_info->set_album(QString::fromLatin1(file.read(32)).toStdString());
|
||||
song->set_album(QString::fromLatin1(file.read(32)).toStdString());
|
||||
|
||||
file.seek(ARTIST_OFFSET);
|
||||
song_info->set_artist(QString::fromLatin1(file.read(32)).toStdString());
|
||||
song->set_artist(QString::fromLatin1(file.read(32)).toStdString());
|
||||
|
||||
file.seek(INTRO_LENGTH_OFFSET);
|
||||
QByteArray length_bytes = file.read(INTRO_LENGTH_SIZE);
|
||||
@ -107,7 +109,7 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||
}
|
||||
|
||||
if (length_in_sec < 0x1FFF) {
|
||||
song_info->set_length_nanosec(length_in_sec * kNsecPerSec);
|
||||
song->set_length_nanosec(length_in_sec * kNsecPerSec);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +133,7 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||
qint64 xid6_size = xid6_head_data[0] | (xid6_head_data[1] << 8) | (xid6_head_data[2] << 16) | xid6_head_data[3];
|
||||
// This should be the size remaining for entire ID6 block, but it seems that most files treat this as the size of the remaining header space...
|
||||
|
||||
qLog(Debug) << file_info.fileName() << "has ID6 tag.";
|
||||
qLog(Debug) << fileinfo.fileName() << "has ID6 tag.";
|
||||
|
||||
while ((file.pos()) + 4 < XID6_OFFSET + xid6_size) {
|
||||
QByteArray arr = file.read(4);
|
||||
@ -152,21 +154,25 @@ void GME::SPC::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||
// an APETAG entry at the bottom of the file instead of writing into the xid6 tagging space.
|
||||
// This is where a lot of the extra data for a file is stored, such as genre or replaygain data.
|
||||
// This data is currently supported by TagLib, so we will simply use that for the remaining values.
|
||||
TagLib::APE::File ape(file_info.filePath().toStdString().data());
|
||||
TagLib::APE::File ape(fileinfo.filePath().toStdString().data());
|
||||
if (ape.hasAPETag()) {
|
||||
TagLib::Tag *tag = ape.tag();
|
||||
if (!tag) return;
|
||||
if (!tag) {
|
||||
return TagReaderBase::Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
TagReaderTagLib::TStringToStdString(tag->artist(), song_info->mutable_artist());
|
||||
TagReaderTagLib::TStringToStdString(tag->album(), song_info->mutable_album());
|
||||
TagReaderTagLib::TStringToStdString(tag->title(), song_info->mutable_title());
|
||||
TagReaderTagLib::TStringToStdString(tag->genre(), song_info->mutable_genre());
|
||||
song_info->set_track(static_cast<std::int32_t>(tag->track()));
|
||||
song_info->set_year(static_cast<std::int32_t>(tag->year()));
|
||||
TagReaderTagLib::TStringToStdString(tag->artist(), song->mutable_artist());
|
||||
TagReaderTagLib::TStringToStdString(tag->album(), song->mutable_album());
|
||||
TagReaderTagLib::TStringToStdString(tag->title(), song->mutable_title());
|
||||
TagReaderTagLib::TStringToStdString(tag->genre(), song->mutable_genre());
|
||||
song->set_track(static_cast<std::int32_t>(tag->track()));
|
||||
song->set_year(static_cast<std::int32_t>(tag->year()));
|
||||
}
|
||||
|
||||
song_info->set_valid(true);
|
||||
song_info->set_filetype(spb::tagreader::SongMetadata_FileType_SPC);
|
||||
song->set_valid(true);
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType_SPC);
|
||||
|
||||
return TagReaderBase::Result::ErrorCode::Success;
|
||||
|
||||
}
|
||||
|
||||
@ -188,18 +194,24 @@ quint64 GME::SPC::ConvertSPCStringToNum(const QByteArray &arr) {
|
||||
|
||||
}
|
||||
|
||||
void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info) {
|
||||
TagReaderBase::Result GME::VGM::Read(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song) {
|
||||
|
||||
QFile file(file_info.filePath());
|
||||
if (!file.open(QIODevice::ReadOnly)) return;
|
||||
QFile file(fileinfo.filePath());
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return TagReaderBase::Result(TagReaderBase::Result::ErrorCode::FileOpenError, file.errorString());
|
||||
}
|
||||
|
||||
qLog(Debug) << "Reading tags from VGM file" << file_info.fileName();
|
||||
qLog(Debug) << "Reading tags from VGM file" << fileinfo.filePath();
|
||||
|
||||
if (!file.read(4).startsWith(QStringLiteral("Vgm ").toLatin1())) return;
|
||||
if (!file.read(4).startsWith(QStringLiteral("Vgm ").toLatin1())) {
|
||||
return TagReaderBase::Result::ErrorCode::Unsupported;
|
||||
}
|
||||
|
||||
file.seek(GD3_TAG_PTR);
|
||||
QByteArray gd3_head = file.read(4);
|
||||
if (gd3_head.size() < 4) return;
|
||||
if (gd3_head.size() < 4) {
|
||||
return TagReaderBase::Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
quint64 pt = GME::UnpackBytes32(gd3_head.constData(), gd3_head.size());
|
||||
|
||||
@ -209,7 +221,9 @@ void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||
QByteArray loop_count_bytes = file.read(4);
|
||||
quint64 length = 0;
|
||||
|
||||
if (!GetPlaybackLength(sample_count_bytes, loop_count_bytes, length)) return;
|
||||
if (!GetPlaybackLength(sample_count_bytes, loop_count_bytes, length)) {
|
||||
return TagReaderBase::Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
file.seek(static_cast<qint64>(GD3_TAG_PTR + pt));
|
||||
QByteArray gd3_version = file.read(4);
|
||||
@ -227,18 +241,22 @@ void GME::VGM::Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *so
|
||||
fileTagStream.setCodec("UTF-16");
|
||||
#endif
|
||||
QStringList strings = fileTagStream.readLine(0).split(QLatin1Char('\0'));
|
||||
if (strings.count() < 10) return;
|
||||
if (strings.count() < 10) {
|
||||
return TagReaderBase::Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
// VGM standard dictates string tag data exist in specific order.
|
||||
// Order alternates between English and Japanese version of data.
|
||||
// Read GD3 tag standard for more details.
|
||||
song_info->set_title(strings[0].toStdString());
|
||||
song_info->set_album(strings[2].toStdString());
|
||||
song_info->set_artist(strings[6].toStdString());
|
||||
song_info->set_year(strings[8].left(4).toInt());
|
||||
song_info->set_length_nanosec(length * kNsecPerMsec);
|
||||
song_info->set_valid(true);
|
||||
song_info->set_filetype(spb::tagreader::SongMetadata_FileType_VGM);
|
||||
song->set_title(strings[0].toStdString());
|
||||
song->set_album(strings[2].toStdString());
|
||||
song->set_artist(strings[6].toStdString());
|
||||
song->set_year(strings[8].left(4).toInt());
|
||||
song->set_length_nanosec(length * kNsecPerMsec);
|
||||
song->set_valid(true);
|
||||
song->set_filetype(spb::tagreader::SongMetadata_FileType_VGM);
|
||||
|
||||
return TagReaderBase::Result::ErrorCode::Success;
|
||||
|
||||
}
|
||||
|
||||
@ -270,31 +288,60 @@ TagReaderGME::TagReaderGME() = default;
|
||||
|
||||
|
||||
bool TagReaderGME::IsMediaFile(const QString &filename) const {
|
||||
|
||||
QFileInfo fileinfo(filename);
|
||||
return GME::IsSupportedFormat(fileinfo);
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderGME::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
TagReaderBase::Result TagReaderGME::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
|
||||
QFileInfo fileinfo(filename);
|
||||
return GME::ReadFile(fileinfo, song);
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderGME::SaveFile(const spb::tagreader::SaveFileRequest&) const {
|
||||
return false;
|
||||
TagReaderBase::Result TagReaderGME::WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const {
|
||||
|
||||
Q_UNUSED(filename);
|
||||
Q_UNUSED(request);
|
||||
|
||||
return Result::ErrorCode::Unsupported;
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReaderGME::LoadEmbeddedArt(const QString&) const {
|
||||
return QByteArray();
|
||||
TagReaderBase::Result TagReaderGME::LoadEmbeddedArt(const QString &filename, QByteArray &data) const {
|
||||
|
||||
Q_UNUSED(filename);
|
||||
Q_UNUSED(data);
|
||||
|
||||
return Result::ErrorCode::Unsupported;
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderGME::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest&) const {
|
||||
return false;
|
||||
TagReaderBase::Result TagReaderGME::SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const {
|
||||
|
||||
Q_UNUSED(filename);
|
||||
Q_UNUSED(request);
|
||||
|
||||
return Result::ErrorCode::Unsupported;
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderGME::SaveSongPlaycountToFile(const QString&, const spb::tagreader::SongMetadata&) const {
|
||||
return false;
|
||||
TagReaderBase::Result TagReaderGME::SaveSongPlaycountToFile(const QString &filename, const uint playcount) const {
|
||||
|
||||
Q_UNUSED(filename);
|
||||
Q_UNUSED(playcount);
|
||||
|
||||
return Result::ErrorCode::Unsupported;
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderGME::SaveSongRatingToFile(const QString&, const spb::tagreader::SongMetadata&) const {
|
||||
return false;
|
||||
TagReaderBase::Result TagReaderGME::SaveSongRatingToFile(const QString &filename, const float rating) const {
|
||||
|
||||
Q_UNUSED(filename);
|
||||
Q_UNUSED(rating);
|
||||
|
||||
return Result::ErrorCode::Unsupported;
|
||||
|
||||
}
|
||||
|
@ -31,8 +31,8 @@
|
||||
|
||||
|
||||
namespace GME {
|
||||
bool IsSupportedFormat(const QFileInfo &file_info);
|
||||
bool ReadFile(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
|
||||
bool IsSupportedFormat(const QFileInfo &fileinfo);
|
||||
TagReaderBase::Result ReadFile(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song);
|
||||
|
||||
uint32_t UnpackBytes32(const char *const bytes, size_t length);
|
||||
|
||||
@ -72,7 +72,7 @@ enum class xID6_TYPE {
|
||||
Integer = 0x4
|
||||
};
|
||||
|
||||
void Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
|
||||
TagReaderBase::Result Read(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song);
|
||||
qint16 GetNextMemAddressAlign32bit(qint16 input);
|
||||
quint64 ConvertSPCStringToNum(const QByteArray &arr);
|
||||
} // namespace SPC
|
||||
@ -88,7 +88,7 @@ constexpr int LOOP_SAMPLE_COUNT = 0x20;
|
||||
constexpr int SAMPLE_TIMEBASE = 44100;
|
||||
constexpr int GST_GME_LOOP_TIME_MS = 8000;
|
||||
|
||||
void Read(const QFileInfo &file_info, spb::tagreader::SongMetadata *song_info);
|
||||
TagReaderBase::Result Read(const QFileInfo &fileinfo, spb::tagreader::SongMetadata *song);
|
||||
// Takes in two QByteArrays, expected to be 4 bytes long. Desired length is returned via output parameter out_length. Returns false on error.
|
||||
bool GetPlaybackLength(const QByteArray &sample_count_bytes, const QByteArray &loop_count_bytes, quint64 &out_length);
|
||||
|
||||
@ -106,14 +106,14 @@ class TagReaderGME : public TagReaderBase {
|
||||
|
||||
bool IsMediaFile(const QString &filename) const override;
|
||||
|
||||
bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||
bool SaveFile(const spb::tagreader::SaveFileRequest &request) const override;
|
||||
Result ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||
Result WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const override;
|
||||
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
||||
bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||
Result LoadEmbeddedArt(const QString &filename, QByteArray &data) const override;
|
||||
Result SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||
|
||||
bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||
bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||
Result SaveSongPlaycountToFile(const QString &filename, const uint playcount) const override;
|
||||
Result SaveSongRatingToFile(const QString &filename, const float rating) const override;
|
||||
};
|
||||
|
||||
#endif // TAGREADERGME_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
syntax = "proto2";
|
||||
syntax = "proto3";
|
||||
|
||||
package spb.tagreader;
|
||||
|
||||
@ -97,7 +97,6 @@ message IsMediaFileRequest {
|
||||
|
||||
message IsMediaFileResponse {
|
||||
optional bool success = 1;
|
||||
optional string error = 2;
|
||||
}
|
||||
|
||||
message ReadFileRequest {
|
||||
@ -105,11 +104,12 @@ message ReadFileRequest {
|
||||
}
|
||||
|
||||
message ReadFileResponse {
|
||||
optional SongMetadata metadata = 1;
|
||||
optional string error = 2;
|
||||
optional bool success = 1;
|
||||
optional SongMetadata metadata = 2;
|
||||
optional string error = 3;
|
||||
}
|
||||
|
||||
message SaveFileRequest {
|
||||
message WriteFileRequest {
|
||||
optional string filename = 1;
|
||||
optional bool save_tags = 2;
|
||||
optional bool save_playcount = 3;
|
||||
@ -121,7 +121,7 @@ message SaveFileRequest {
|
||||
optional string cover_mime_type = 9;
|
||||
}
|
||||
|
||||
message SaveFileResponse {
|
||||
message WriteFileResponse {
|
||||
optional bool success = 1;
|
||||
optional string error = 2;
|
||||
}
|
||||
@ -131,8 +131,9 @@ message LoadEmbeddedArtRequest {
|
||||
}
|
||||
|
||||
message LoadEmbeddedArtResponse {
|
||||
optional bytes data = 1;
|
||||
optional string error = 2;
|
||||
optional bool success = 1;
|
||||
optional bytes data = 2;
|
||||
optional string error = 3;
|
||||
}
|
||||
|
||||
message SaveEmbeddedArtRequest {
|
||||
@ -149,7 +150,7 @@ message SaveEmbeddedArtResponse {
|
||||
|
||||
message SaveSongPlaycountToFileRequest {
|
||||
optional string filename = 1;
|
||||
optional SongMetadata metadata = 2;
|
||||
optional uint32 playcount = 2;
|
||||
}
|
||||
|
||||
message SaveSongPlaycountToFileResponse {
|
||||
@ -159,7 +160,7 @@ message SaveSongPlaycountToFileResponse {
|
||||
|
||||
message SaveSongRatingToFileRequest {
|
||||
optional string filename = 1;
|
||||
optional SongMetadata metadata = 2;
|
||||
optional float rating = 2;
|
||||
}
|
||||
|
||||
message SaveSongRatingToFileResponse {
|
||||
@ -173,8 +174,8 @@ message Message {
|
||||
optional ReadFileRequest read_file_request = 2;
|
||||
optional ReadFileResponse read_file_response = 3;
|
||||
|
||||
optional SaveFileRequest save_file_request = 4;
|
||||
optional SaveFileResponse save_file_response = 5;
|
||||
optional WriteFileRequest write_file_request = 4;
|
||||
optional WriteFileResponse write_file_response = 5;
|
||||
|
||||
optional IsMediaFileRequest is_media_file_request = 6;
|
||||
optional IsMediaFileResponse is_media_file_response = 7;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2013, David Sansome <me@davidsansome.com>
|
||||
Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||
Copyright 2018-2024, 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
|
||||
@ -229,7 +229,11 @@ spb::tagreader::SongMetadata_FileType TagReaderTagLib::GuessFileType(TagLib::Fil
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
TagReaderBase::Result TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
return Result::ErrorCode::FilenameMissing;
|
||||
}
|
||||
|
||||
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
||||
const QFileInfo fileinfo(filename);
|
||||
@ -252,8 +256,8 @@ bool TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
if (fileref->isNull()) {
|
||||
qLog(Info) << "TagLib hasn't been able to read" << filename << "file";
|
||||
return false;
|
||||
qLog(Error) << "TagLib hasn't been able to read" << filename << "file";
|
||||
return Result::ErrorCode::FileOpenError;
|
||||
}
|
||||
|
||||
song->set_filetype(GuessFileType(fileref.get()));
|
||||
@ -552,8 +556,8 @@ bool TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta
|
||||
|
||||
if (compilation.isEmpty()) {
|
||||
// well, it wasn't set, but if the artist is VA assume it's a compilation
|
||||
const QString albumartist = QString::fromUtf8(song->albumartist().data(), static_cast<qint64>(song->albumartist().size()));
|
||||
const QString artist = QString::fromUtf8(song->artist().data(), static_cast<qint64>(song->artist().size()));
|
||||
const QString albumartist = QString::fromStdString(song->albumartist());
|
||||
const QString artist = QString::fromStdString(song->artist());
|
||||
if (artist.compare(QLatin1String("various artists")) == 0 || albumartist.compare(QLatin1String("various artists")) == 0) {
|
||||
song->set_compilation(true);
|
||||
}
|
||||
@ -575,7 +579,14 @@ bool TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMeta
|
||||
if (song->bitrate() <= 0) { song->set_bitrate(-1); }
|
||||
if (song->lastplayed() <= 0) { song->set_lastplayed(-1); }
|
||||
|
||||
return song->filetype() != spb::tagreader::SongMetadata_FileType_UNKNOWN;
|
||||
if (song->filetype() == spb::tagreader::SongMetadata_FileType_UNKNOWN) {
|
||||
qLog(Error) << "Unknown audio filetype reading" << filename;
|
||||
return Result::ErrorCode::Unsupported;
|
||||
}
|
||||
|
||||
qLog(Debug) << "Got tags for" << filename;
|
||||
|
||||
return Result::ErrorCode::Success;
|
||||
|
||||
}
|
||||
|
||||
@ -846,12 +857,13 @@ void TagReaderTagLib::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comment
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) const {
|
||||
TagReaderBase::Result TagReaderTagLib::WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const {
|
||||
|
||||
if (request.filename().empty()) return false;
|
||||
if (filename.isEmpty()) {
|
||||
return Result::ErrorCode::FilenameMissing;
|
||||
}
|
||||
|
||||
const QString filename = QString::fromUtf8(request.filename().data(), static_cast<qint64>(request.filename().size()));
|
||||
const spb::tagreader::SongMetadata song = request.metadata();
|
||||
const spb::tagreader::SongMetadata &song = request.metadata();
|
||||
const bool save_tags = request.has_save_tags() && request.save_tags();
|
||||
const bool save_playcount = request.has_save_playcount() && request.save_playcount();
|
||||
const bool save_rating = request.has_save_rating() && request.save_rating();
|
||||
@ -873,10 +885,13 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||
|
||||
qLog(Debug) << "Saving" << save_tags_options.join(QLatin1String(", ")) << "to" << filename;
|
||||
|
||||
const Cover cover = LoadCoverFromRequest(request);
|
||||
const Cover cover = LoadCoverFromRequest(filename, request);
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
if (!fileref || fileref->isNull()) return false;
|
||||
if (!fileref || fileref->isNull()) {
|
||||
qLog(Error) << "TagLib hasn't been able to open" << filename << "file";
|
||||
return Result::ErrorCode::FileOpenError;
|
||||
}
|
||||
|
||||
if (save_tags) {
|
||||
fileref->tag()->setTitle(song.title().empty() ? TagLib::String() : StdStringToTaglibString(song.title()));
|
||||
@ -892,115 +907,123 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||
if (TagLib::FLAC::File *file_flac = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||
is_flac = true;
|
||||
TagLib::Ogg::XiphComment *xiph_comment = file_flac->xiphComment(true);
|
||||
if (!xiph_comment) return false;
|
||||
if (save_tags) {
|
||||
SetVorbisComments(xiph_comment, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(xiph_comment, song);
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(xiph_comment, song);
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(file_flac, xiph_comment, cover.data, cover.mime_type);
|
||||
if (xiph_comment) {
|
||||
if (save_tags) {
|
||||
SetVorbisComments(xiph_comment, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(xiph_comment, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(xiph_comment, song.rating());
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(file_flac, xiph_comment, cover.data, cover.mime_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (TagLib::WavPack::File *file_wavpack = dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = file_wavpack->APETag(true);
|
||||
if (!tag) return false;
|
||||
if (save_tags) {
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song);
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song);
|
||||
if (tag) {
|
||||
if (save_tags) {
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song.rating());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (TagLib::APE::File *file_ape = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = file_ape->APETag(true);
|
||||
if (!tag) return false;
|
||||
if (save_tags) {
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song);
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song);
|
||||
if (tag) {
|
||||
if (save_tags) {
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song.rating());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (TagLib::MPC::File *file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = file_mpc->APETag(true);
|
||||
if (!tag) return false;
|
||||
if (save_tags) {
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song);
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song);
|
||||
if (tag) {
|
||||
if (save_tags) {
|
||||
SaveAPETag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song.rating());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
||||
if (!tag) return false;
|
||||
if (save_tags) {
|
||||
SaveID3v2Tag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song);
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song);
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||
if (tag) {
|
||||
if (save_tags) {
|
||||
SaveID3v2Tag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song.rating());
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (TagLib::MP4::File *file_mp4 = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||
TagLib::MP4::Tag *tag = file_mp4->tag();
|
||||
if (!tag) return false;
|
||||
if (save_tags) {
|
||||
tag->setItem("disk", TagLib::MP4::Item(song.disc() <= 0 - 1 ? 0 : song.disc(), 0));
|
||||
tag->setItem("\251wrt", TagLib::StringList(TagLib::String(song.composer(), TagLib::String::UTF8)));
|
||||
tag->setItem("\251grp", TagLib::StringList(TagLib::String(song.grouping(), TagLib::String::UTF8)));
|
||||
tag->setItem("\251lyr", TagLib::StringList(TagLib::String(song.lyrics(), TagLib::String::UTF8)));
|
||||
tag->setItem("aART", TagLib::StringList(TagLib::String(song.albumartist(), TagLib::String::UTF8)));
|
||||
tag->setItem("cpil", TagLib::MP4::Item(song.compilation()));
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song);
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song);
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(file_mp4, tag, cover.data, cover.mime_type);
|
||||
if (tag) {
|
||||
if (save_tags) {
|
||||
tag->setItem("disk", TagLib::MP4::Item(song.disc() <= 0 - 1 ? 0 : song.disc(), 0));
|
||||
tag->setItem("\251wrt", TagLib::StringList(TagLib::String(song.composer(), TagLib::String::UTF8)));
|
||||
tag->setItem("\251grp", TagLib::StringList(TagLib::String(song.grouping(), TagLib::String::UTF8)));
|
||||
tag->setItem("\251lyr", TagLib::StringList(TagLib::String(song.lyrics(), TagLib::String::UTF8)));
|
||||
tag->setItem("aART", TagLib::StringList(TagLib::String(song.albumartist(), TagLib::String::UTF8)));
|
||||
tag->setItem("cpil", TagLib::MP4::Item(song.compilation()));
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song.rating());
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(file_mp4, tag, cover.data, cover.mime_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (TagLib::RIFF::WAV::File *file_wav = dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag *tag = file_wav->ID3v2Tag();
|
||||
if (save_tags) {
|
||||
SaveID3v2Tag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song);
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song);
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||
if (tag) {
|
||||
if (save_tags) {
|
||||
SaveID3v2Tag(tag, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(tag, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(tag, song.rating());
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1008,17 +1031,19 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||
// apart, so we keep specific behavior for some formats by adding another "else if" block above.
|
||||
if (!is_flac) {
|
||||
if (TagLib::Ogg::XiphComment *xiph_comment = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||
if (save_tags) {
|
||||
SetVorbisComments(xiph_comment, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(xiph_comment, song);
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(xiph_comment, song);
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(xiph_comment, cover.data, cover.mime_type);
|
||||
if (xiph_comment) {
|
||||
if (save_tags) {
|
||||
SetVorbisComments(xiph_comment, song);
|
||||
}
|
||||
if (save_playcount) {
|
||||
SetPlaycount(xiph_comment, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SetRating(xiph_comment, song.rating());
|
||||
}
|
||||
if (save_cover) {
|
||||
SetEmbeddedArt(xiph_comment, cover.data, cover.mime_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1031,7 +1056,7 @@ bool TagReaderTagLib::SaveFile(const spb::tagreader::SaveFileRequest &request) c
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
return success;
|
||||
return success ? Result(Result::ErrorCode::Success) : Result(Result::ErrorCode::FileSaveError);
|
||||
|
||||
}
|
||||
|
||||
@ -1154,9 +1179,11 @@ void TagReaderTagLib::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
TagReaderBase::Result TagReaderTagLib::LoadEmbeddedArt(const QString &filename, QByteArray &data) const {
|
||||
|
||||
if (filename.isEmpty()) return QByteArray();
|
||||
if (filename.isEmpty()) {
|
||||
return Result::ErrorCode::FilenameMissing;
|
||||
}
|
||||
|
||||
qLog(Debug) << "Loading art from" << filename;
|
||||
|
||||
@ -1166,7 +1193,10 @@ QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
TagLib::FileRef fileref(QFile::encodeName(filename).constData());
|
||||
#endif
|
||||
|
||||
if (fileref.isNull() || !fileref.file()) return QByteArray();
|
||||
if (fileref.isNull() || !fileref.file()) {
|
||||
qLog(Error) << "TagLib hasn't been able to open file" << filename;
|
||||
return Result::ErrorCode::FileOpenError;
|
||||
}
|
||||
|
||||
// FLAC
|
||||
if (TagLib::FLAC::File *flac_file = dynamic_cast<TagLib::FLAC::File*>(fileref.file())) {
|
||||
@ -1175,9 +1205,9 @@ QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
if (!pictures.isEmpty()) {
|
||||
for (TagLib::FLAC::Picture *picture : pictures) {
|
||||
if (picture->type() == TagLib::FLAC::Picture::FrontCover && picture->data().size() > 0) {
|
||||
QByteArray data(picture->data().data(), picture->data().size());
|
||||
data = QByteArray(picture->data().data(), picture->data().size());
|
||||
if (!data.isEmpty()) {
|
||||
return data;
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1188,21 +1218,30 @@ QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
// WavPack
|
||||
if (TagLib::WavPack::File *wavpack_file = dynamic_cast<TagLib::WavPack::File*>(fileref.file())) {
|
||||
if (wavpack_file->APETag()) {
|
||||
return LoadEmbeddedAPEArt(wavpack_file->APETag()->itemListMap());
|
||||
data = LoadEmbeddedAPEArt(wavpack_file->APETag()->itemListMap());
|
||||
if (!data.isEmpty()) {
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// APE
|
||||
if (TagLib::APE::File *ape_file = dynamic_cast<TagLib::APE::File*>(fileref.file())) {
|
||||
if (ape_file->APETag()) {
|
||||
return LoadEmbeddedAPEArt(ape_file->APETag()->itemListMap());
|
||||
data = LoadEmbeddedAPEArt(ape_file->APETag()->itemListMap());
|
||||
if (!data.isEmpty()) {
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MPC
|
||||
if (TagLib::MPC::File *mpc_file = dynamic_cast<TagLib::MPC::File*>(fileref.file())) {
|
||||
if (mpc_file->APETag()) {
|
||||
return LoadEmbeddedAPEArt(mpc_file->APETag()->itemListMap());
|
||||
data = LoadEmbeddedAPEArt(mpc_file->APETag()->itemListMap());
|
||||
if (!data.isEmpty()) {
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1214,9 +1253,9 @@ QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
if (!pictures.isEmpty()) {
|
||||
for (TagLib::FLAC::Picture *picture : pictures) {
|
||||
if (picture->type() == TagLib::FLAC::Picture::FrontCover && picture->data().size() > 0) {
|
||||
QByteArray data(picture->data().data(), picture->data().size());
|
||||
data = QByteArray(picture->data().data(), picture->data().size());
|
||||
if (!data.isEmpty()) {
|
||||
return data;
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1224,10 +1263,12 @@ QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
|
||||
// Ogg lacks a definitive standard for embedding cover art, but it seems b64 encoding a field called COVERART is the general convention
|
||||
if (map.contains("COVERART")) {
|
||||
return QByteArray::fromBase64(map["COVERART"].toString().toCString());
|
||||
data = QByteArray::fromBase64(map["COVERART"].toString().toCString());
|
||||
if (!data.isEmpty()) {
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// MP3
|
||||
@ -1235,12 +1276,15 @@ QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
if (file_mp3->ID3v2Tag()) {
|
||||
TagLib::ID3v2::FrameList apic_frames = file_mp3->ID3v2Tag()->frameListMap()["APIC"];
|
||||
if (apic_frames.isEmpty()) {
|
||||
return QByteArray();
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
|
||||
TagLib::ID3v2::AttachedPictureFrame *picture = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(apic_frames.front());
|
||||
|
||||
return QByteArray(reinterpret_cast<const char*>(picture->picture().data()), picture->picture().size());
|
||||
data = QByteArray(reinterpret_cast<const char*>(picture->picture().data()), picture->picture().size());
|
||||
if (!data.isEmpty()) {
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1253,30 +1297,31 @@ QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||
if (!art_list.isEmpty()) {
|
||||
// Just take the first one for now
|
||||
const TagLib::MP4::CoverArt &art = art_list.front();
|
||||
return QByteArray(art.data().data(), art.data().size());
|
||||
data = QByteArray(art.data().data(), art.data().size());
|
||||
if (!data.isEmpty()) {
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
return Result::ErrorCode::Success;
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReaderTagLib::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const {
|
||||
|
||||
QByteArray ret;
|
||||
|
||||
TagLib::APE::ItemListMap::ConstIterator it = map.find("COVER ART (FRONT)");
|
||||
if (it != map.end()) {
|
||||
TagLib::ByteVector data = it->second.binaryData();
|
||||
|
||||
int pos = data.find('\0') + 1;
|
||||
if ((pos > 0) && (static_cast<uint>(pos) < data.size())) {
|
||||
ret = QByteArray(data.data() + pos, data.size() - pos);
|
||||
return QByteArray(data.data() + pos, data.size() - pos);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return QByteArray();
|
||||
|
||||
}
|
||||
|
||||
@ -1356,15 +1401,15 @@ void TagReaderTagLib::SetEmbeddedArt(TagLib::MP4::File *aac_file, TagLib::MP4::T
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagLib::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const {
|
||||
TagReaderBase::Result TagReaderTagLib::SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const {
|
||||
|
||||
if (request.filename().empty()) return false;
|
||||
|
||||
const QString filename = QString::fromUtf8(request.filename().data(), static_cast<qint64>(request.filename().size()));
|
||||
if (filename.isEmpty()) {
|
||||
return Result::ErrorCode::FilenameMissing;
|
||||
}
|
||||
|
||||
qLog(Debug) << "Saving art to" << filename;
|
||||
|
||||
const Cover cover = LoadCoverFromRequest(request);
|
||||
const Cover cover = LoadCoverFromRequest(filename, request);
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
TagLib::FileRef fileref(filename.toStdWString().c_str());
|
||||
@ -1372,13 +1417,17 @@ bool TagReaderTagLib::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtReque
|
||||
TagLib::FileRef fileref(QFile::encodeName(filename).constData());
|
||||
#endif
|
||||
|
||||
if (fileref.isNull() || !fileref.file()) return false;
|
||||
if (fileref.isNull() || !fileref.file()) {
|
||||
qLog(Error) << "TagLib could not open file" << filename;
|
||||
return Result::ErrorCode::FileOpenError;
|
||||
}
|
||||
|
||||
// FLAC
|
||||
if (TagLib::FLAC::File *flac_file = dynamic_cast<TagLib::FLAC::File*>(fileref.file())) {
|
||||
TagLib::Ogg::XiphComment *xiph_comment = flac_file->xiphComment(true);
|
||||
if (!xiph_comment) return false;
|
||||
SetEmbeddedArt(flac_file, xiph_comment, cover.data, cover.mime_type);
|
||||
if (xiph_comment) {
|
||||
SetEmbeddedArt(flac_file, xiph_comment, cover.data, cover.mime_type);
|
||||
}
|
||||
}
|
||||
|
||||
// Ogg Vorbis / Opus / Speex
|
||||
@ -1389,19 +1438,24 @@ bool TagReaderTagLib::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtReque
|
||||
// MP3
|
||||
else if (TagLib::MPEG::File *file_mp3 = dynamic_cast<TagLib::MPEG::File*>(fileref.file())) {
|
||||
TagLib::ID3v2::Tag *tag = file_mp3->ID3v2Tag();
|
||||
if (!tag) return false;
|
||||
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||
if (tag) {
|
||||
SetEmbeddedArt(tag, cover.data, cover.mime_type);
|
||||
}
|
||||
}
|
||||
|
||||
// MP4/AAC
|
||||
else if (TagLib::MP4::File *aac_file = dynamic_cast<TagLib::MP4::File*>(fileref.file())) {
|
||||
TagLib::MP4::Tag *tag = aac_file->tag();
|
||||
if (!tag) return false;
|
||||
SetEmbeddedArt(aac_file, tag, cover.data, cover.mime_type);
|
||||
if (tag) {
|
||||
SetEmbeddedArt(aac_file, tag, cover.data, cover.mime_type);
|
||||
}
|
||||
}
|
||||
|
||||
// Not supported.
|
||||
else return false;
|
||||
else {
|
||||
qLog(Error) << "Saving embedded art is not supported for %1" << filename;
|
||||
return Result::ErrorCode::Unsupported;
|
||||
}
|
||||
|
||||
const bool success = fileref.file()->save();
|
||||
#ifdef Q_OS_LINUX
|
||||
@ -1411,7 +1465,7 @@ bool TagReaderTagLib::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtReque
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
return success;
|
||||
return success ? Result::ErrorCode::Success : Result::ErrorCode::FileSaveError;
|
||||
|
||||
}
|
||||
|
||||
@ -1433,10 +1487,10 @@ TagLib::ID3v2::PopularimeterFrame *TagReaderTagLib::GetPOPMFrameFromTag(TagLib::
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::Ogg::XiphComment *xiph_comment, const spb::tagreader::SongMetadata &song) const {
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::Ogg::XiphComment *xiph_comment, const uint playcount) const {
|
||||
|
||||
if (song.playcount() > 0) {
|
||||
xiph_comment->addField("FMPS_PLAYCOUNT", TagLib::String::number(static_cast<int>(song.playcount())), true);
|
||||
if (playcount > 0) {
|
||||
xiph_comment->addField("FMPS_PLAYCOUNT", TagLib::String::number(static_cast<int>(playcount)), true);
|
||||
}
|
||||
else {
|
||||
xiph_comment->removeFields("FMPS_PLAYCOUNT");
|
||||
@ -1444,10 +1498,10 @@ void TagReaderTagLib::SetPlaycount(TagLib::Ogg::XiphComment *xiph_comment, const
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::APE::Tag *tag, const uint playcount) const {
|
||||
|
||||
if (song.playcount() > 0) {
|
||||
tag->setItem("FMPS_Playcount", TagLib::APE::Item("FMPS_Playcount", TagLib::String::number(static_cast<int>(song.playcount()))));
|
||||
if (playcount > 0) {
|
||||
tag->setItem("FMPS_Playcount", TagLib::APE::Item("FMPS_Playcount", TagLib::String::number(static_cast<int>(playcount))));
|
||||
}
|
||||
else {
|
||||
tag->removeItem("FMPS_Playcount");
|
||||
@ -1455,20 +1509,20 @@ void TagReaderTagLib::SetPlaycount(TagLib::APE::Tag *tag, const spb::tagreader::
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::ID3v2::Tag *tag, const uint playcount) const {
|
||||
|
||||
SetUserTextFrame(QStringLiteral("FMPS_Playcount"), QString::number(song.playcount()), tag);
|
||||
SetUserTextFrame(QStringLiteral("FMPS_Playcount"), QString::number(playcount), tag);
|
||||
TagLib::ID3v2::PopularimeterFrame *frame = GetPOPMFrameFromTag(tag);
|
||||
if (frame) {
|
||||
frame->setCounter(song.playcount());
|
||||
frame->setCounter(playcount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::MP4::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::MP4::Tag *tag, const uint playcount) const {
|
||||
|
||||
if (song.playcount() > 0) {
|
||||
tag->setItem(kMP4_FMPS_Playcount_ID, TagLib::MP4::Item(TagLib::String::number(static_cast<int>(song.playcount()))));
|
||||
if (playcount > 0) {
|
||||
tag->setItem(kMP4_FMPS_Playcount_ID, TagLib::MP4::Item(TagLib::String::number(static_cast<int>(playcount))));
|
||||
}
|
||||
else {
|
||||
tag->removeItem(kMP4_FMPS_Playcount_ID);
|
||||
@ -1476,10 +1530,10 @@ void TagReaderTagLib::SetPlaycount(TagLib::MP4::Tag *tag, const spb::tagreader::
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
void TagReaderTagLib::SetPlaycount(TagLib::ASF::Tag *tag, const uint playcount) const {
|
||||
|
||||
if (song.playcount() > 0) {
|
||||
tag->setAttribute("FMPS/Playcount", TagLib::ASF::Attribute(QStringToTaglibString(QString::number(song.playcount()))));
|
||||
if (playcount > 0) {
|
||||
tag->setAttribute("FMPS/Playcount", TagLib::ASF::Attribute(QStringToTaglibString(QString::number(playcount))));
|
||||
}
|
||||
else {
|
||||
tag->removeItem("FMPS/Playcount");
|
||||
@ -1487,169 +1541,69 @@ void TagReaderTagLib::SetPlaycount(TagLib::ASF::Tag *tag, const spb::tagreader::
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagLib::SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
||||
TagReaderBase::Result TagReaderTagLib::SaveSongPlaycountToFile(const QString &filename, const uint playcount) const {
|
||||
|
||||
if (filename.isEmpty()) return false;
|
||||
if (filename.isEmpty()) {
|
||||
return Result::ErrorCode::FilenameMissing;
|
||||
}
|
||||
|
||||
qLog(Debug) << "Saving song playcount to" << filename;
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
if (!fileref || fileref->isNull()) return false;
|
||||
if (!fileref || fileref->isNull()) {
|
||||
qLog(Error) << "TagLib hasn't been able to open file" << filename;
|
||||
return Result::ErrorCode::FileOpenError;
|
||||
}
|
||||
|
||||
if (TagLib::FLAC::File *flac_file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||
TagLib::Ogg::XiphComment *xiph_comment = flac_file->xiphComment(true);
|
||||
if (!xiph_comment) return false;
|
||||
SetPlaycount(xiph_comment, song);
|
||||
if (xiph_comment) {
|
||||
SetPlaycount(xiph_comment, playcount);
|
||||
}
|
||||
}
|
||||
else if (TagLib::WavPack::File *wavpack_file = dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = wavpack_file->APETag(true);
|
||||
if (!tag) return false;
|
||||
SetPlaycount(tag, song);
|
||||
if (tag) {
|
||||
SetPlaycount(tag, playcount);
|
||||
}
|
||||
}
|
||||
else if (TagLib::APE::File *ape_file = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = ape_file->APETag(true);
|
||||
if (!tag) return false;
|
||||
SetPlaycount(tag, song);
|
||||
if (tag) {
|
||||
SetPlaycount(tag, playcount);
|
||||
}
|
||||
}
|
||||
else if (TagLib::Ogg::XiphComment *xiph_comment = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||
SetPlaycount(xiph_comment, song);
|
||||
if (xiph_comment) {
|
||||
SetPlaycount(xiph_comment, playcount);
|
||||
}
|
||||
}
|
||||
else if (TagLib::MPEG::File *mpeg_file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag *tag = mpeg_file->ID3v2Tag(true);
|
||||
if (!tag) return false;
|
||||
SetPlaycount(tag, song);
|
||||
if (tag) {
|
||||
SetPlaycount(tag, playcount);
|
||||
}
|
||||
}
|
||||
else if (TagLib::MP4::File *mp4_file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||
TagLib::MP4::Tag *tag = mp4_file->tag();
|
||||
if (!tag) return false;
|
||||
SetPlaycount(tag, song);
|
||||
if (tag) {
|
||||
SetPlaycount(tag, playcount);
|
||||
}
|
||||
}
|
||||
else if (TagLib::MPC::File *mpc_file = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = mpc_file->APETag(true);
|
||||
if (!tag) return false;
|
||||
SetPlaycount(tag, song);
|
||||
if (tag) {
|
||||
SetPlaycount(tag, playcount);
|
||||
}
|
||||
}
|
||||
else if (TagLib::ASF::File *asf_file = dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
||||
TagLib::ASF::Tag *tag = asf_file->tag();
|
||||
if (!tag) return false;
|
||||
if (song.playcount() > 0) {
|
||||
tag->addAttribute("FMPS/Playcount", TagLib::ASF::Attribute(QStringToTaglibString(QString::number(song.playcount()))));
|
||||
if (tag && playcount > 0) {
|
||||
tag->addAttribute("FMPS/Playcount", TagLib::ASF::Attribute(QStringToTaglibString(QString::number(playcount))));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool success = fileref->save();
|
||||
#ifdef Q_OS_LINUX
|
||||
if (success) {
|
||||
// Linux: inotify doesn't seem to notice the change to the file unless we change the timestamps as well. (this is what touch does)
|
||||
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::Ogg::XiphComment *xiph_comment, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
if (song.rating() > 0.0F) {
|
||||
xiph_comment->addField("FMPS_RATING", QStringToTaglibString(QString::number(song.rating())), true);
|
||||
}
|
||||
else {
|
||||
xiph_comment->removeFields("FMPS_RATING");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
if (song.rating() > 0.0F) {
|
||||
tag->setItem("FMPS_Rating", TagLib::APE::Item("FMPS_Rating", TagLib::StringList(QStringToTaglibString(QString::number(song.rating())))));
|
||||
}
|
||||
else {
|
||||
tag->removeItem("FMPS_Rating");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
SetUserTextFrame(QStringLiteral("FMPS_Rating"), QString::number(song.rating()), tag);
|
||||
TagLib::ID3v2::PopularimeterFrame *frame = GetPOPMFrameFromTag(tag);
|
||||
if (frame) {
|
||||
frame->setRating(ConvertToPOPMRating(song.rating()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::MP4::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
tag->setItem(kMP4_FMPS_Rating_ID, TagLib::StringList(QStringToTaglibString(QString::number(song.rating()))));
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
tag->addAttribute("FMPS/Rating", TagLib::ASF::Attribute(QStringToTaglibString(QString::number(song.rating()))));
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagLib::SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
if (filename.isNull()) return false;
|
||||
|
||||
qLog(Debug) << "Saving song rating to" << filename;
|
||||
|
||||
if (song.rating() < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
|
||||
if (!fileref || fileref->isNull()) return false;
|
||||
|
||||
if (TagLib::FLAC::File *flac_file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||
TagLib::Ogg::XiphComment *xiph_comment = flac_file->xiphComment(true);
|
||||
if (!xiph_comment) return false;
|
||||
SetRating(xiph_comment, song);
|
||||
}
|
||||
else if (TagLib::WavPack::File *wavpack_file = dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = wavpack_file->APETag(true);
|
||||
if (!tag) return false;
|
||||
SetRating(tag, song);
|
||||
}
|
||||
else if (TagLib::APE::File *ape_file = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = ape_file->APETag(true);
|
||||
if (!tag) return false;
|
||||
SetRating(tag, song);
|
||||
}
|
||||
else if (TagLib::Ogg::XiphComment *xiph_comment = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||
SetRating(xiph_comment, song);
|
||||
}
|
||||
else if (TagLib::MPEG::File *mpeg_file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag *tag = mpeg_file->ID3v2Tag(true);
|
||||
if (!tag) return false;
|
||||
SetRating(tag, song);
|
||||
}
|
||||
else if (TagLib::MP4::File *mp4_file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||
TagLib::MP4::Tag *tag = mp4_file->tag();
|
||||
if (!tag) return false;
|
||||
SetRating(tag, song);
|
||||
}
|
||||
else if (TagLib::ASF::File *asf_file = dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
||||
TagLib::ASF::Tag *tag = asf_file->tag();
|
||||
if (!tag) return false;
|
||||
SetRating(tag, song);
|
||||
}
|
||||
else if (TagLib::MPC::File *mpc_file = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = mpc_file->APETag(true);
|
||||
if (!tag) return false;
|
||||
SetRating(tag, song);
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
return Result::ErrorCode::Unsupported;
|
||||
}
|
||||
|
||||
const bool success = fileref->save();
|
||||
@ -1660,6 +1614,135 @@ bool TagReaderTagLib::SaveSongRatingToFile(const QString &filename, const spb::t
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
return success;
|
||||
return success ? Result::ErrorCode::Success : Result::ErrorCode::FileSaveError;
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::Ogg::XiphComment *xiph_comment, const float rating) const {
|
||||
|
||||
if (rating > 0.0F) {
|
||||
xiph_comment->addField("FMPS_RATING", QStringToTaglibString(QString::number(rating)), true);
|
||||
}
|
||||
else {
|
||||
xiph_comment->removeFields("FMPS_RATING");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::APE::Tag *tag, const float rating) const {
|
||||
|
||||
if (rating > 0.0F) {
|
||||
tag->setItem("FMPS_Rating", TagLib::APE::Item("FMPS_Rating", TagLib::StringList(QStringToTaglibString(QString::number(rating)))));
|
||||
}
|
||||
else {
|
||||
tag->removeItem("FMPS_Rating");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::ID3v2::Tag *tag, const float rating) const {
|
||||
|
||||
SetUserTextFrame(QStringLiteral("FMPS_Rating"), QString::number(rating), tag);
|
||||
TagLib::ID3v2::PopularimeterFrame *frame = GetPOPMFrameFromTag(tag);
|
||||
if (frame) {
|
||||
frame->setRating(ConvertToPOPMRating(rating));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::MP4::Tag *tag, const float rating) const {
|
||||
|
||||
tag->setItem(kMP4_FMPS_Rating_ID, TagLib::StringList(QStringToTaglibString(QString::number(rating))));
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagLib::SetRating(TagLib::ASF::Tag *tag, const float rating) const {
|
||||
|
||||
tag->addAttribute("FMPS/Rating", TagLib::ASF::Attribute(QStringToTaglibString(QString::number(rating))));
|
||||
|
||||
}
|
||||
|
||||
TagReaderBase::Result TagReaderTagLib::SaveSongRatingToFile(const QString &filename, const float rating) const {
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
return Result::ErrorCode::FilenameMissing;
|
||||
}
|
||||
|
||||
qLog(Debug) << "Saving song rating to" << filename;
|
||||
|
||||
if (rating < 0) {
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
|
||||
if (!fileref || fileref->isNull()) {
|
||||
qLog(Error) << "TagLib hasn't been able to open file" << filename;
|
||||
return Result::ErrorCode::FileOpenError;
|
||||
}
|
||||
|
||||
if (TagLib::FLAC::File *flac_file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||
TagLib::Ogg::XiphComment *xiph_comment = flac_file->xiphComment(true);
|
||||
if (xiph_comment) {
|
||||
SetRating(xiph_comment, rating);
|
||||
}
|
||||
}
|
||||
else if (TagLib::WavPack::File *wavpack_file = dynamic_cast<TagLib::WavPack::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = wavpack_file->APETag(true);
|
||||
if (tag) {
|
||||
SetRating(tag, rating);
|
||||
}
|
||||
}
|
||||
else if (TagLib::APE::File *ape_file = dynamic_cast<TagLib::APE::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = ape_file->APETag(true);
|
||||
if (tag) {
|
||||
SetRating(tag, rating);
|
||||
}
|
||||
}
|
||||
else if (TagLib::Ogg::XiphComment *xiph_comment = dynamic_cast<TagLib::Ogg::XiphComment*>(fileref->file()->tag())) {
|
||||
SetRating(xiph_comment, rating);
|
||||
}
|
||||
else if (TagLib::MPEG::File *mpeg_file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag *tag = mpeg_file->ID3v2Tag(true);
|
||||
if (tag) {
|
||||
SetRating(tag, rating);
|
||||
}
|
||||
}
|
||||
else if (TagLib::MP4::File *mp4_file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||
TagLib::MP4::Tag *tag = mp4_file->tag();
|
||||
if (tag) {
|
||||
SetRating(tag, rating);
|
||||
}
|
||||
}
|
||||
else if (TagLib::ASF::File *asf_file = dynamic_cast<TagLib::ASF::File*>(fileref->file())) {
|
||||
TagLib::ASF::Tag *tag = asf_file->tag();
|
||||
if (tag) {
|
||||
SetRating(tag, rating);
|
||||
}
|
||||
}
|
||||
else if (TagLib::MPC::File *mpc_file = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||
TagLib::APE::Tag *tag = mpc_file->APETag(true);
|
||||
if (tag) {
|
||||
SetRating(tag, rating);
|
||||
}
|
||||
}
|
||||
else {
|
||||
qLog(Error) << "Unsupported file for saving rating for" << filename;
|
||||
return Result::ErrorCode::Unsupported;
|
||||
}
|
||||
|
||||
const bool success = fileref->save();
|
||||
#ifdef Q_OS_LINUX
|
||||
if (success) {
|
||||
// Linux: inotify doesn't seem to notice the change to the file unless we change the timestamps as well. (this is what touch does)
|
||||
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
if (!success) {
|
||||
qLog(Error) << "TagLib hasn't been able to save file" << filename;
|
||||
}
|
||||
|
||||
return success ? Result::ErrorCode::Success : Result::ErrorCode::FileSaveError;
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2013, David Sansome <me@davidsansome.com>
|
||||
Copyright 2018-2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||
Copyright 2018-2024, 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
|
||||
@ -54,14 +54,14 @@ class TagReaderTagLib : public TagReaderBase {
|
||||
|
||||
bool IsMediaFile(const QString &filename) const override;
|
||||
|
||||
bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||
bool SaveFile(const spb::tagreader::SaveFileRequest &request) const override;
|
||||
Result ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||
Result WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const override;
|
||||
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
||||
bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||
Result LoadEmbeddedArt(const QString &filename, QByteArray &data) const;
|
||||
Result SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||
|
||||
bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||
bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||
Result SaveSongPlaycountToFile(const QString &filename, const uint playcount) const override;
|
||||
Result SaveSongRatingToFile(const QString &filename, const float rating) const override;
|
||||
|
||||
static void TStringToStdString(const TagLib::String &tag, std::string *output);
|
||||
|
||||
@ -86,17 +86,17 @@ class TagReaderTagLib : public TagReaderBase {
|
||||
|
||||
static TagLib::ID3v2::PopularimeterFrame *GetPOPMFrameFromTag(TagLib::ID3v2::Tag *tag);
|
||||
|
||||
void SetPlaycount(TagLib::Ogg::XiphComment *xiph_comment, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetPlaycount(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetPlaycount(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetPlaycount(TagLib::MP4::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetPlaycount(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetPlaycount(TagLib::Ogg::XiphComment *xiph_comment, const uint playcount) const;
|
||||
void SetPlaycount(TagLib::APE::Tag *tag, const uint playcount) const;
|
||||
void SetPlaycount(TagLib::ID3v2::Tag *tag, const uint playcount) const;
|
||||
void SetPlaycount(TagLib::MP4::Tag *tag, const uint playcount) const;
|
||||
void SetPlaycount(TagLib::ASF::Tag *tag, const uint playcount) const;
|
||||
|
||||
void SetRating(TagLib::Ogg::XiphComment *xiph_comment, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetRating(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetRating(TagLib::ID3v2::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetRating(TagLib::MP4::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetRating(TagLib::ASF::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SetRating(TagLib::Ogg::XiphComment *xiph_comment, const float rating) const;
|
||||
void SetRating(TagLib::APE::Tag *tag, const float rating) const;
|
||||
void SetRating(TagLib::ID3v2::Tag *tag, const float rating) const;
|
||||
void SetRating(TagLib::MP4::Tag *tag, const float rating) const;
|
||||
void SetRating(TagLib::ASF::Tag *tag, const float rating) const;
|
||||
|
||||
void SetEmbeddedArt(TagLib::FLAC::File *flac_file, TagLib::Ogg::XiphComment *xiph_comment, const QByteArray &data, const QString &mime_type) const;
|
||||
void SetEmbeddedArt(TagLib::Ogg::XiphComment *xiph_comment, const QByteArray &data, const QString &mime_type) const;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||
Copyright 2021-2024, 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
|
||||
@ -53,7 +53,7 @@ bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
|
||||
qLog(Debug) << "Checking for valid file" << filename;
|
||||
|
||||
QFileInfo fileinfo(filename);
|
||||
if (!fileinfo.exists() || fileinfo.suffix().compare("bak", Qt::CaseInsensitive) == 0) return false;
|
||||
if (!fileinfo.exists() || fileinfo.suffix().compare(QLatin1String("bak"), Qt::CaseInsensitive) == 0) return false;
|
||||
|
||||
try {
|
||||
TagParser::MediaFileInfo taginfo;
|
||||
@ -94,13 +94,13 @@ bool TagReaderTagParser::IsMediaFile(const QString &filename) const {
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
TagReaderBase::Result TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||
|
||||
qLog(Debug) << "Reading tags from" << filename;
|
||||
|
||||
const QFileInfo fileinfo(filename);
|
||||
|
||||
if (!fileinfo.exists() || fileinfo.suffix().compare("bak", Qt::CaseInsensitive) == 0) return false;
|
||||
if (!fileinfo.exists() || fileinfo.suffix().compare(QLatin1String("bak"), Qt::CaseInsensitive) == 0) return Result::ErrorCode::FileParseError;
|
||||
|
||||
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
||||
const QByteArray basefilename = fileinfo.fileName().toUtf8();
|
||||
@ -134,19 +134,19 @@ bool TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
taginfo.parseTracks(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
for (const TagParser::DiagMessage &msg : diag) {
|
||||
@ -205,7 +205,7 @@ bool TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
|
||||
|
||||
if (song->filetype() == spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_UNKNOWN) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::Unsupported;
|
||||
}
|
||||
|
||||
for (TagParser::Tag *tag : taginfo.tags()) {
|
||||
@ -246,21 +246,20 @@ bool TagReaderTagParser::ReadFile(const QString &filename, spb::tagreader::SongM
|
||||
|
||||
taginfo.close();
|
||||
|
||||
return true;
|
||||
return Result::ErrorCode::Success;
|
||||
|
||||
}
|
||||
catch(...) {
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request) const {
|
||||
TagReaderBase::Result TagReaderTagParser::WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const {
|
||||
|
||||
if (request.filename().empty()) return false;
|
||||
if (request.filename().empty()) return Result::ErrorCode::FilenameMissing;
|
||||
|
||||
const QString filename = QString::fromUtf8(request.filename().data(), request.filename().size());
|
||||
const spb::tagreader::SongMetadata song = request.metadata();
|
||||
const spb::tagreader::SongMetadata &song = request.metadata();
|
||||
const bool save_tags = request.has_save_tags() && request.save_tags();
|
||||
const bool save_playcount = request.has_save_playcount() && request.save_playcount();
|
||||
const bool save_rating = request.has_save_rating() && request.save_rating();
|
||||
@ -268,21 +267,21 @@ bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request
|
||||
|
||||
QStringList save_tags_options;
|
||||
if (save_tags) {
|
||||
save_tags_options << "tags";
|
||||
save_tags_options << QStringLiteral("tags");
|
||||
}
|
||||
if (save_playcount) {
|
||||
save_tags_options << "playcount";
|
||||
save_tags_options << QStringLiteral("playcount");
|
||||
}
|
||||
if (save_rating) {
|
||||
save_tags_options << "rating";
|
||||
save_tags_options << QStringLiteral("rating");
|
||||
}
|
||||
if (save_cover) {
|
||||
save_tags_options << "embedded cover";
|
||||
save_tags_options << QStringLiteral("embedded cover");
|
||||
}
|
||||
|
||||
qLog(Debug) << "Saving" << save_tags_options.join(", ") << "to" << filename;
|
||||
qLog(Debug) << "Saving" << save_tags_options.join(QLatin1String(", ")) << "to" << filename;
|
||||
|
||||
const QByteArray cover_data = LoadCoverDataFromRequest(request);
|
||||
const Cover cover = LoadCoverFromRequest(filename, request);
|
||||
|
||||
try {
|
||||
TagParser::MediaFileInfo taginfo;
|
||||
@ -298,19 +297,19 @@ bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
taginfo.parseTracks(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
if (taginfo.tags().size() <= 0) {
|
||||
@ -335,13 +334,13 @@ bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request
|
||||
tag->setValue(TagParser::KnownField::ReleaseDate, TagParser::TagValue(song.originalyear()));
|
||||
}
|
||||
if (save_playcount) {
|
||||
SaveSongPlaycountToFile(tag, song);
|
||||
SaveSongPlaycountToFile(tag, song.playcount());
|
||||
}
|
||||
if (save_rating) {
|
||||
SaveSongRatingToFile(tag, song);
|
||||
SaveSongRatingToFile(tag, song.rating());
|
||||
}
|
||||
if (save_cover) {
|
||||
SaveEmbeddedArt(tag, cover_data);
|
||||
SaveEmbeddedArt(tag, cover.data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,17 +351,17 @@ bool TagReaderTagParser::SaveFile(const spb::tagreader::SaveFileRequest &request
|
||||
qLog(Debug) << QString::fromStdString(msg.message());
|
||||
}
|
||||
|
||||
return true;
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
|
||||
TagReaderBase::Result TagReaderTagParser::LoadEmbeddedArt(const QString &filename, QByteArray &data) const {
|
||||
|
||||
if (filename.isEmpty()) return QByteArray();
|
||||
if (filename.isEmpty()) return Result::ErrorCode::FilenameMissing;
|
||||
|
||||
qLog(Debug) << "Loading art from" << filename;
|
||||
|
||||
@ -383,20 +382,20 @@ QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return QByteArray();
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return QByteArray();
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
for (TagParser::Tag *tag : taginfo.tags()) {
|
||||
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
|
||||
QByteArray data(tag->value(TagParser::KnownField::Cover).dataPointer(), tag->value(TagParser::KnownField::Cover).dataSize());
|
||||
data = QByteArray(tag->value(TagParser::KnownField::Cover).dataPointer(), tag->value(TagParser::KnownField::Cover).dataSize());
|
||||
taginfo.close();
|
||||
return data;
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,7 +408,7 @@ QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return QByteArray();
|
||||
return Result::ErrorCode::FileParseError;
|
||||
|
||||
}
|
||||
|
||||
@ -419,15 +418,13 @@ void TagReaderTagParser::SaveEmbeddedArt(TagParser::Tag *tag, const QByteArray &
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const {
|
||||
TagReaderBase::Result TagReaderTagParser::SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const {
|
||||
|
||||
if (request.filename().empty()) return false;
|
||||
|
||||
const QString filename = QString::fromUtf8(request.filename().data(), request.filename().size());
|
||||
if (request.filename().empty()) return Result::ErrorCode::FilenameMissing;
|
||||
|
||||
qLog(Debug) << "Saving art to" << filename;
|
||||
|
||||
const QByteArray cover_data = LoadCoverDataFromRequest(request);
|
||||
const Cover cover = LoadCoverFromRequest(filename, request);
|
||||
|
||||
try {
|
||||
|
||||
@ -446,13 +443,13 @@ bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRe
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
if (taginfo.tags().size() <= 0) {
|
||||
@ -460,7 +457,7 @@ bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRe
|
||||
}
|
||||
|
||||
for (TagParser::Tag *tag : taginfo.tags()) {
|
||||
SaveEmbeddedArt(tag, cover_data);
|
||||
SaveEmbeddedArt(tag, cover.data);
|
||||
}
|
||||
|
||||
taginfo.applyChanges(diag, progress);
|
||||
@ -470,28 +467,40 @@ bool TagReaderTagParser::SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRe
|
||||
qLog(Debug) << QString::fromStdString(msg.message());
|
||||
}
|
||||
|
||||
return true;
|
||||
return Result::ErrorCode::Success;
|
||||
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagParser::SaveSongPlaycountToFile(TagParser::Tag*, const spb::tagreader::SongMetadata&) const {}
|
||||
void TagReaderTagParser::SaveSongPlaycountToFile(TagParser::Tag *tag, const uint playcount) const {
|
||||
|
||||
bool TagReaderTagParser::SaveSongPlaycountToFile(const QString&, const spb::tagreader::SongMetadata&) const { return false; }
|
||||
|
||||
void TagReaderTagParser::SaveSongRatingToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||
|
||||
tag->setValue(TagParser::KnownField::Rating, TagParser::TagValue(ConvertToPOPMRating(song.rating())));
|
||||
Q_UNUSED(tag);
|
||||
Q_UNUSED(playcount);
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
||||
TagReaderBase::Result TagReaderTagParser::SaveSongPlaycountToFile(const QString &filename, const uint playcount) const {
|
||||
|
||||
if (filename.isEmpty()) return false;
|
||||
Q_UNUSED(filename);
|
||||
Q_UNUSED(playcount);
|
||||
|
||||
return Result::ErrorCode::Unsupported;
|
||||
|
||||
}
|
||||
|
||||
void TagReaderTagParser::SaveSongRatingToFile(TagParser::Tag *tag, const float rating) const {
|
||||
|
||||
tag->setValue(TagParser::KnownField::Rating, TagParser::TagValue(ConvertToPOPMRating(rating)));
|
||||
|
||||
}
|
||||
|
||||
TagReaderBase::Result TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const float rating) const {
|
||||
|
||||
if (filename.isEmpty()) return Result::ErrorCode::FilenameMissing;
|
||||
|
||||
qLog(Debug) << "Saving song rating to" << filename;
|
||||
|
||||
@ -509,19 +518,19 @@ bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb
|
||||
taginfo.parseContainerFormat(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
taginfo.parseTracks(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
taginfo.parseTags(diag, progress);
|
||||
if (progress.isAborted()) {
|
||||
taginfo.close();
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
}
|
||||
|
||||
if (taginfo.tags().size() <= 0) {
|
||||
@ -529,7 +538,7 @@ bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb
|
||||
}
|
||||
|
||||
for (TagParser::Tag *tag : taginfo.tags()) {
|
||||
SaveSongRatingToFile(tag, song);
|
||||
SaveSongRatingToFile(tag, rating);
|
||||
}
|
||||
|
||||
taginfo.applyChanges(diag, progress);
|
||||
@ -539,10 +548,10 @@ bool TagReaderTagParser::SaveSongRatingToFile(const QString &filename, const spb
|
||||
qLog(Debug) << QString::fromStdString(msg.message());
|
||||
}
|
||||
|
||||
return true;
|
||||
return Result::ErrorCode::Success;
|
||||
}
|
||||
catch(...) {}
|
||||
|
||||
return false;
|
||||
return Result::ErrorCode::FileParseError;
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||
Copyright 2021-2024, 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
|
||||
@ -41,18 +41,18 @@ class TagReaderTagParser : public TagReaderBase {
|
||||
|
||||
bool IsMediaFile(const QString &filename) const override;
|
||||
|
||||
bool ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||
bool SaveFile(const spb::tagreader::SaveFileRequest &request) const override;
|
||||
Result ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||
Result WriteFile(const QString &filename, const spb::tagreader::WriteFileRequest &request) const override;
|
||||
|
||||
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
||||
bool SaveEmbeddedArt(const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||
Result LoadEmbeddedArt(const QString &filename, QByteArray &data) const override;
|
||||
Result SaveEmbeddedArt(const QString &filename, const spb::tagreader::SaveEmbeddedArtRequest &request) const override;
|
||||
|
||||
bool SaveSongPlaycountToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||
bool SaveSongRatingToFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||
Result SaveSongPlaycountToFile(const QString &filename, const uint playcount) const override;
|
||||
Result SaveSongRatingToFile(const QString &filename, const float rating) const override;
|
||||
|
||||
private:
|
||||
void SaveSongPlaycountToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SaveSongRatingToFile(TagParser::Tag *tag, const spb::tagreader::SongMetadata &song) const;
|
||||
void SaveSongPlaycountToFile(TagParser::Tag *tag, const uint playcount) const;
|
||||
void SaveSongRatingToFile(TagParser::Tag *tag, const float rating) const;
|
||||
void SaveEmbeddedArt(TagParser::Tag *tag, const QByteArray &data) const;
|
||||
|
||||
public:
|
||||
|
@ -9,11 +9,11 @@ qt_wrap_cpp(MOC ${HEADERS})
|
||||
|
||||
link_directories(${GLIB_LIBRARY_DIRS})
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
if(HAVE_TAGLIB)
|
||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
if(HAVE_TAGPARSER)
|
||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
@ -39,12 +39,12 @@ target_link_libraries(strawberry-tagreader PRIVATE
|
||||
libstrawberry-tagreader
|
||||
)
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
if(HAVE_TAGLIB)
|
||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry-tagreader PRIVATE ${TAGLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
if(HAVE_TAGPARSER)
|
||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
||||
target_link_libraries(strawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
||||
endif()
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||
Copyright 2018-2024, 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
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <QCoreApplication>
|
||||
@ -27,20 +28,37 @@
|
||||
|
||||
#include "tagreaderworker.h"
|
||||
|
||||
#ifdef HAVE_TAGLIB
|
||||
# include "tagreadertaglib.h"
|
||||
# include "tagreadergme.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TAGPARSER
|
||||
# include "tagreadertagparser.h"
|
||||
#endif
|
||||
|
||||
using std::make_shared;
|
||||
using std::shared_ptr;
|
||||
|
||||
TagReaderWorker::TagReaderWorker(QIODevice *socket, QObject *parent)
|
||||
: AbstractMessageHandler<spb::tagreader::Message>(socket, parent) {}
|
||||
: AbstractMessageHandler<spb::tagreader::Message>(socket, parent) {
|
||||
|
||||
#ifdef HAVE_TAGLIB
|
||||
tagreaders_ << make_shared<TagReaderTagLib>();
|
||||
tagreaders_ << make_shared<TagReaderGME>();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TAGPARSER
|
||||
tagreaders_ << make_shared<TagReaderTagParser>();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void TagReaderWorker::MessageArrived(const spb::tagreader::Message &message) {
|
||||
|
||||
spb::tagreader::Message reply;
|
||||
|
||||
bool success = HandleMessage(message, reply, &tag_reader_);
|
||||
if (!success) {
|
||||
#if defined(USE_TAGLIB)
|
||||
HandleMessage(message, reply, &tag_reader_gme_);
|
||||
#endif
|
||||
}
|
||||
|
||||
HandleMessage(message, reply);
|
||||
SendReply(message, &reply);
|
||||
|
||||
}
|
||||
@ -53,48 +71,123 @@ void TagReaderWorker::DeviceClosed() {
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderWorker::HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply, TagReaderBase *reader) {
|
||||
void TagReaderWorker::HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply) {
|
||||
|
||||
if (message.has_is_media_file_request()) {
|
||||
const QString filename = QString::fromUtf8(message.is_media_file_request().filename().data(), static_cast<qint64>(message.is_media_file_request().filename().size()));
|
||||
bool success = reader->IsMediaFile(filename);
|
||||
reply.mutable_is_media_file_response()->set_success(success);
|
||||
return success;
|
||||
}
|
||||
if (message.has_read_file_request()) {
|
||||
const QString filename = QString::fromUtf8(message.read_file_request().filename().data(), static_cast<qint64>(message.read_file_request().filename().size()));
|
||||
bool success = reader->ReadFile(filename, reply.mutable_read_file_response()->mutable_metadata());
|
||||
return success;
|
||||
}
|
||||
if (message.has_save_file_request()) {
|
||||
bool success = reader->SaveFile(message.save_file_request());
|
||||
reply.mutable_save_file_response()->set_success(success);
|
||||
return success;
|
||||
}
|
||||
if (message.has_load_embedded_art_request()) {
|
||||
const QString filename = QString::fromUtf8(message.load_embedded_art_request().filename().data(), static_cast<qint64>(message.load_embedded_art_request().filename().size()));
|
||||
QByteArray data = reader->LoadEmbeddedArt(filename);
|
||||
reply.mutable_load_embedded_art_response()->set_data(data.constData(), data.size());
|
||||
return true;
|
||||
}
|
||||
if (message.has_save_embedded_art_request()) {
|
||||
bool success = reader->SaveEmbeddedArt(message.save_embedded_art_request());
|
||||
reply.mutable_save_embedded_art_response()->set_success(success);
|
||||
return success;
|
||||
}
|
||||
if (message.has_save_song_playcount_to_file_request()) {
|
||||
const QString filename = QString::fromUtf8(message.save_song_playcount_to_file_request().filename().data(), static_cast<qint64>(message.save_song_playcount_to_file_request().filename().size()));
|
||||
bool success = reader->SaveSongPlaycountToFile(filename, message.save_song_playcount_to_file_request().metadata());
|
||||
reply.mutable_save_song_playcount_to_file_response()->set_success(success);
|
||||
return success;
|
||||
}
|
||||
if (message.has_save_song_rating_to_file_request()) {
|
||||
const QString filename = QString::fromUtf8(message.save_song_rating_to_file_request().filename().data(), static_cast<qint64>(message.save_song_rating_to_file_request().filename().size()));
|
||||
bool success = reader->SaveSongRatingToFile(filename, message.save_song_rating_to_file_request().metadata());
|
||||
reply.mutable_save_song_rating_to_file_response()->set_success(success);
|
||||
return success;
|
||||
}
|
||||
for (shared_ptr<TagReaderBase> reader : tagreaders_) {
|
||||
|
||||
return false;
|
||||
if (message.has_is_media_file_request()) {
|
||||
const QString filename = QString::fromStdString(message.is_media_file_request().filename());
|
||||
const bool success = reader->IsMediaFile(filename);
|
||||
reply.mutable_is_media_file_response()->set_success(success);
|
||||
if (success) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (message.has_read_file_request()) {
|
||||
const QString filename = QString::fromStdString(message.read_file_request().filename());
|
||||
spb::tagreader::ReadFileResponse *response = reply.mutable_read_file_response();
|
||||
const TagReaderBase::Result result = reader->ReadFile(filename, response->mutable_metadata());
|
||||
response->set_success(result.success());
|
||||
if (result.success()) {
|
||||
if (response->has_error()) {
|
||||
response->clear_error();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!response->has_error()) {
|
||||
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.has_write_file_request()) {
|
||||
const QString filename = QString::fromStdString(message.write_file_request().filename());
|
||||
const TagReaderBase::Result result = reader->WriteFile(filename, message.write_file_request());
|
||||
spb::tagreader::WriteFileResponse *response = reply.mutable_write_file_response();
|
||||
response->set_success(result.success());
|
||||
if (result.success()) {
|
||||
if (response->has_error()) {
|
||||
response->clear_error();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!response->has_error()) {
|
||||
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.has_load_embedded_art_request()) {
|
||||
const QString filename = QString::fromStdString(message.load_embedded_art_request().filename());
|
||||
QByteArray data;
|
||||
const TagReaderBase::Result result = reader->LoadEmbeddedArt(filename, data);
|
||||
spb::tagreader::LoadEmbeddedArtResponse *response = reply.mutable_load_embedded_art_response();
|
||||
response->set_success(result.success());
|
||||
if (result.success()) {
|
||||
response->set_data(data.toStdString());
|
||||
if (response->has_error()) {
|
||||
response->clear_error();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!response->has_error()) {
|
||||
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.has_save_embedded_art_request()) {
|
||||
const QString filename = QString::fromStdString(message.save_embedded_art_request().filename());
|
||||
const TagReaderBase::Result result = reader->SaveEmbeddedArt(filename, message.save_embedded_art_request());
|
||||
spb::tagreader::SaveEmbeddedArtResponse *response = reply.mutable_save_embedded_art_response();
|
||||
response->set_success(result.success());
|
||||
if (result.success()) {
|
||||
if (response->has_error()) {
|
||||
response->clear_error();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!response->has_error()) {
|
||||
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.has_save_song_playcount_to_file_request()) {
|
||||
const QString filename = QString::fromStdString(message.save_song_playcount_to_file_request().filename());
|
||||
const TagReaderBase::Result result = reader->SaveSongPlaycountToFile(filename, message.save_song_playcount_to_file_request().playcount());
|
||||
spb::tagreader::SaveSongPlaycountToFileResponse *response = reply.mutable_save_song_playcount_to_file_response();
|
||||
response->set_success(result.success());
|
||||
if (result.success()) {
|
||||
if (response->has_error()) {
|
||||
response->clear_error();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!response->has_error()) {
|
||||
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.has_save_song_rating_to_file_request()) {
|
||||
const QString filename = QString::fromStdString(message.save_song_rating_to_file_request().filename());
|
||||
const TagReaderBase::Result result = reader->SaveSongRatingToFile(filename, message.save_song_rating_to_file_request().rating());
|
||||
spb::tagreader::SaveSongRatingToFileResponse *response = reply.mutable_save_song_rating_to_file_response();
|
||||
response->set_success(result.success());
|
||||
if (result.success()) {
|
||||
if (response->has_error()) {
|
||||
response->clear_error();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (!response->has_error()) {
|
||||
response->set_error(TagReaderBase::ErrorString(result).toStdString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* This file is part of Strawberry.
|
||||
Copyright 2011, David Sansome <me@davidsansome.com>
|
||||
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||
Copyright 2018-2024, 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
|
||||
@ -21,19 +21,19 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
|
||||
#include "core/messagehandler.h"
|
||||
#if defined(USE_TAGLIB)
|
||||
# include "tagreadertaglib.h"
|
||||
# include "tagreadergme.h"
|
||||
#elif defined(USE_TAGPARSER)
|
||||
# include "tagreadertagparser.h"
|
||||
#endif
|
||||
|
||||
#include "tagreadermessages.pb.h"
|
||||
|
||||
class QIODevice;
|
||||
class TagReaderBase;
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
|
||||
Q_OBJECT
|
||||
@ -46,15 +46,9 @@ class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
|
||||
void DeviceClosed() override;
|
||||
|
||||
private:
|
||||
// Handle message using specific TagReaderBase implementation. Returns true on successful message handle.
|
||||
bool HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply, TagReaderBase* reader);
|
||||
void HandleMessage(const spb::tagreader::Message &message, spb::tagreader::Message &reply);
|
||||
|
||||
#if defined(USE_TAGLIB)
|
||||
TagReaderTagLib tag_reader_;
|
||||
TagReaderGME tag_reader_gme_;
|
||||
#elif defined(USE_TAGPARSER)
|
||||
TagReaderTagParser tag_reader_;
|
||||
#endif
|
||||
QList<shared_ptr<TagReaderBase>> tagreaders_;
|
||||
};
|
||||
|
||||
#endif // TAGREADERWORKER_H
|
||||
|
@ -1075,11 +1075,11 @@ if(HAVE_LIBMTP)
|
||||
link_directories(${LIBMTP_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||
if(HAVE_TAGLIB)
|
||||
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||
if(HAVE_TAGPARSER)
|
||||
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
|
@ -206,8 +206,8 @@ void SCollection::SyncPlaycountAndRatingToFiles() {
|
||||
const qint64 nb_songs = songs.size();
|
||||
int i = 0;
|
||||
for (const Song &song : songs) {
|
||||
TagReaderClient::Instance()->UpdateSongPlaycountBlocking(song);
|
||||
TagReaderClient::Instance()->UpdateSongRatingBlocking(song);
|
||||
(void)TagReaderClient::Instance()->SaveSongPlaycountBlocking(song.url().toLocalFile(), song.playcount());
|
||||
(void)TagReaderClient::Instance()->SaveSongRatingBlocking(song.url().toLocalFile(), song.rating());
|
||||
app_->task_manager()->SetTaskProgress(task_id, ++i, nb_songs);
|
||||
}
|
||||
app_->task_manager()->SetTaskFinished(task_id);
|
||||
@ -217,7 +217,7 @@ void SCollection::SyncPlaycountAndRatingToFiles() {
|
||||
void SCollection::SongsPlaycountChanged(const SongList &songs, const bool save_tags) {
|
||||
|
||||
if (save_tags || save_playcounts_to_files_) {
|
||||
app_->tag_reader_client()->UpdateSongsPlaycount(songs);
|
||||
app_->tag_reader_client()->SaveSongsPlaycount(songs);
|
||||
}
|
||||
|
||||
}
|
||||
@ -225,7 +225,7 @@ void SCollection::SongsPlaycountChanged(const SongList &songs, const bool save_t
|
||||
void SCollection::SongsRatingChanged(const SongList &songs, const bool save_tags) {
|
||||
|
||||
if (save_tags || save_ratings_to_files_) {
|
||||
app_->tag_reader_client()->UpdateSongsRating(songs);
|
||||
app_->tag_reader_client()->SaveSongsRating(songs);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,7 +41,11 @@ QUrl CollectionPlaylistItem::Url() const { return song_.url(); }
|
||||
|
||||
void CollectionPlaylistItem::Reload() {
|
||||
|
||||
TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
|
||||
if (!result.success()) {
|
||||
qLog(Error) << "Could not reload file" << song_.url() << result.error;
|
||||
return;
|
||||
}
|
||||
UpdateTemporaryMetadata(song_);
|
||||
|
||||
}
|
||||
|
@ -817,8 +817,8 @@ void CollectionWatcher::UpdateNonCueAssociatedSong(const QString &file,
|
||||
}
|
||||
|
||||
Song song_on_disk(source_);
|
||||
TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk);
|
||||
if (song_on_disk.is_valid()) {
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(file, &song_on_disk);
|
||||
if (result.success() && song_on_disk.is_valid()) {
|
||||
song_on_disk.set_source(source_);
|
||||
song_on_disk.set_directory_id(t->dir());
|
||||
song_on_disk.set_id(matching_song.id());
|
||||
@ -870,8 +870,8 @@ SongList CollectionWatcher::ScanNewFile(const QString &file, const QString &path
|
||||
}
|
||||
else { // It's a normal media file
|
||||
Song song(source_);
|
||||
TagReaderClient::Instance()->ReadFileBlocking(file, &song);
|
||||
if (song.is_valid()) {
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(file, &song);
|
||||
if (result.success() && song.is_valid()) {
|
||||
song.set_source(source_);
|
||||
PerformEBUR128Analysis(song);
|
||||
song.set_fingerprint(fingerprint);
|
||||
|
@ -37,8 +37,10 @@
|
||||
#cmakedefine HAVE_KEYSYMDEF_H
|
||||
#cmakedefine HAVE_XF86KEYSYM_H
|
||||
|
||||
#cmakedefine HAVE_TAGLIB
|
||||
#cmakedefine HAVE_TAGLIB_DSFFILE
|
||||
#cmakedefine HAVE_TAGLIB_DSDIFFFILE
|
||||
#cmakedefine HAVE_TAGPARSER
|
||||
|
||||
#cmakedefine USE_BUNDLE
|
||||
|
||||
@ -51,9 +53,6 @@
|
||||
|
||||
#cmakedefine ENABLE_WIN32_CONSOLE
|
||||
|
||||
#cmakedefine USE_TAGLIB
|
||||
#cmakedefine USE_TAGPARSER
|
||||
|
||||
#cmakedefine HAVE_QX11APPLICATION
|
||||
|
||||
#cmakedefine HAVE_EBUR128
|
||||
|
@ -2249,7 +2249,7 @@ void MainWindow::RenumberTracks() {
|
||||
Song song = item->OriginalMetadata();
|
||||
if (song.IsEditable()) {
|
||||
song.set_track(track);
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->WriteFile(song.url().toLocalFile(), song);
|
||||
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
|
||||
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
|
||||
}
|
||||
@ -2280,7 +2280,7 @@ void MainWindow::SelectionSetValue() {
|
||||
Song song = item->OriginalMetadata();
|
||||
if (!song.is_valid()) continue;
|
||||
if (song.url().isLocalFile() && Playlist::set_column_value(song, column, column_value)) {
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->WriteFile(song.url().toLocalFile(), song);
|
||||
QPersistentModelIndex persistent_index = QPersistentModelIndex(source_index);
|
||||
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index]() { SongSaveComplete(reply, persistent_index); }, Qt::QueuedConnection);
|
||||
}
|
||||
|
@ -1302,27 +1302,27 @@ void Song::InitFromProtobuf(const spb::tagreader::SongMetadata &pb) {
|
||||
|
||||
d->init_from_file_ = true;
|
||||
d->valid_ = pb.valid();
|
||||
set_title(QString::fromUtf8(pb.title().data(), static_cast<qint64>(pb.title().size())));
|
||||
set_album(QString::fromUtf8(pb.album().data(), static_cast<qint64>(pb.album().size())));
|
||||
set_artist(QString::fromUtf8(pb.artist().data(), static_cast<qint64>(pb.artist().size())));
|
||||
set_albumartist(QString::fromUtf8(pb.albumartist().data(), static_cast<qint64>(pb.albumartist().size())));
|
||||
set_title(QString::fromStdString(pb.title()));
|
||||
set_album(QString::fromStdString(pb.album()));
|
||||
set_artist(QString::fromStdString(pb.artist()));
|
||||
set_albumartist(QString::fromStdString(pb.albumartist()));
|
||||
d->track_ = pb.track();
|
||||
d->disc_ = pb.disc();
|
||||
d->year_ = pb.year();
|
||||
d->originalyear_ = pb.originalyear();
|
||||
d->genre_ = QString::fromUtf8(pb.genre().data(), static_cast<qint64>(pb.genre().size()));
|
||||
d->genre_ = QString::fromStdString(pb.genre());
|
||||
d->compilation_ = pb.compilation();
|
||||
d->composer_ = QString::fromUtf8(pb.composer().data(), static_cast<qint64>(pb.composer().size()));
|
||||
d->performer_ = QString::fromUtf8(pb.performer().data(), static_cast<qint64>(pb.performer().size()));
|
||||
d->grouping_ = QString::fromUtf8(pb.grouping().data(), static_cast<qint64>(pb.grouping().size()));
|
||||
d->comment_ = QString::fromUtf8(pb.comment().data(), static_cast<qint64>(pb.comment().size()));
|
||||
d->lyrics_ = QString::fromUtf8(pb.lyrics().data(), static_cast<qint64>(pb.lyrics().size()));
|
||||
d->composer_ = QString::fromStdString(pb.composer());
|
||||
d->performer_ = QString::fromStdString(pb.performer());
|
||||
d->grouping_ = QString::fromStdString(pb.grouping());
|
||||
d->comment_ = QString::fromStdString(pb.comment());
|
||||
d->lyrics_ = QString::fromStdString(pb.lyrics());
|
||||
set_length_nanosec(static_cast<qint64>(pb.length_nanosec()));
|
||||
d->bitrate_ = pb.bitrate();
|
||||
d->samplerate_ = pb.samplerate();
|
||||
d->bitdepth_ = pb.bitdepth();
|
||||
set_url(QUrl::fromEncoded(QByteArray(pb.url().data(), static_cast<qint64>(pb.url().size()))));
|
||||
d->basefilename_ = QString::fromUtf8(pb.basefilename().data(), static_cast<qint64>(pb.basefilename().size()));
|
||||
set_url(QUrl::fromEncoded(QString::fromStdString(pb.url()).toUtf8()));
|
||||
d->basefilename_ = QString::fromStdString(pb.basefilename());
|
||||
d->filetype_ = static_cast<FileType>(pb.filetype());
|
||||
d->filesize_ = pb.filesize();
|
||||
d->mtime_ = pb.mtime();
|
||||
@ -1340,19 +1340,19 @@ void Song::InitFromProtobuf(const spb::tagreader::SongMetadata &pb) {
|
||||
|
||||
d->art_embedded_ = pb.has_art_embedded();
|
||||
|
||||
d->acoustid_id_ = QString::fromUtf8(pb.acoustid_id().data(), static_cast<qint64>(pb.acoustid_id().size()));
|
||||
d->acoustid_fingerprint_ = QString::fromUtf8(pb.acoustid_fingerprint().data(), static_cast<qint64>(pb.acoustid_fingerprint().size()));
|
||||
d->acoustid_id_ = QString::fromStdString(pb.acoustid_id());
|
||||
d->acoustid_fingerprint_ = QString::fromStdString(pb.acoustid_fingerprint());
|
||||
|
||||
d->musicbrainz_album_artist_id_ = QString::fromUtf8(pb.musicbrainz_album_artist_id().data(), static_cast<qint64>(pb.musicbrainz_album_artist_id().size()));
|
||||
d->musicbrainz_artist_id_ = QString::fromUtf8(pb.musicbrainz_artist_id().data(), static_cast<qint64>(pb.musicbrainz_artist_id().size()));
|
||||
d->musicbrainz_original_artist_id_ = QString::fromUtf8(pb.musicbrainz_original_artist_id().data(), static_cast<qint64>(pb.musicbrainz_original_artist_id().size()));
|
||||
d->musicbrainz_album_id_ = QString::fromUtf8(pb.musicbrainz_album_id().data(), static_cast<qint64>(pb.musicbrainz_album_id().size()));
|
||||
d->musicbrainz_original_album_id_ = QString::fromUtf8(pb.musicbrainz_original_album_id().data(), static_cast<qint64>(pb.musicbrainz_original_album_id().size()));
|
||||
d->musicbrainz_recording_id_ = QString::fromUtf8(pb.musicbrainz_recording_id().data(), static_cast<qint64>(pb.musicbrainz_recording_id().size()));
|
||||
d->musicbrainz_track_id_ = QString::fromUtf8(pb.musicbrainz_track_id().data(), static_cast<qint64>(pb.musicbrainz_track_id().size()));
|
||||
d->musicbrainz_disc_id_ = QString::fromUtf8(pb.musicbrainz_disc_id().data(), static_cast<qint64>(pb.musicbrainz_disc_id().size()));
|
||||
d->musicbrainz_release_group_id_ = QString::fromUtf8(pb.musicbrainz_release_group_id().data(), static_cast<qint64>(pb.musicbrainz_release_group_id().size()));
|
||||
d->musicbrainz_work_id_ = QString::fromUtf8(pb.musicbrainz_work_id().data(), static_cast<qint64>(pb.musicbrainz_work_id().size()));
|
||||
d->musicbrainz_album_artist_id_ = QString::fromStdString(pb.musicbrainz_album_artist_id());
|
||||
d->musicbrainz_artist_id_ = QString::fromStdString(pb.musicbrainz_artist_id().data());
|
||||
d->musicbrainz_original_artist_id_ = QString::fromStdString(pb.musicbrainz_original_artist_id());
|
||||
d->musicbrainz_album_id_ = QString::fromStdString(pb.musicbrainz_album_id());
|
||||
d->musicbrainz_original_album_id_ = QString::fromStdString(pb.musicbrainz_original_album_id());
|
||||
d->musicbrainz_recording_id_ = QString::fromStdString(pb.musicbrainz_recording_id());
|
||||
d->musicbrainz_track_id_ = QString::fromStdString(pb.musicbrainz_track_id());
|
||||
d->musicbrainz_disc_id_ = QString::fromStdString(pb.musicbrainz_disc_id());
|
||||
d->musicbrainz_release_group_id_ = QString::fromStdString(pb.musicbrainz_release_group_id());
|
||||
d->musicbrainz_work_id_ = QString::fromStdString(pb.musicbrainz_work_id());
|
||||
|
||||
d->suspicious_tags_ = pb.suspicious_tags();
|
||||
|
||||
|
@ -354,8 +354,11 @@ void SongLoader::EffectiveSongLoad(Song *song) {
|
||||
}
|
||||
else {
|
||||
// It's a normal media file
|
||||
QString filename = song->url().toLocalFile();
|
||||
TagReaderClient::Instance()->ReadFileBlocking(filename, song);
|
||||
const QString filename = song->url().toLocalFile();
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(filename, song);
|
||||
if (!result.success()) {
|
||||
qLog(Error) << "Could not read file" << song->url() << result.error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2019-2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||
* Copyright 2019-2024, 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
|
||||
@ -73,11 +73,7 @@ void TagReaderClient::WorkerFailedToStart() {
|
||||
TagReaderReply *TagReaderClient::IsMediaFile(const QString &filename) {
|
||||
|
||||
spb::tagreader::Message message;
|
||||
spb::tagreader::IsMediaFileRequest *request = message.mutable_is_media_file_request();
|
||||
|
||||
const QByteArray filename_data = filename.toUtf8();
|
||||
request->set_filename(filename_data.constData(), filename_data.length());
|
||||
|
||||
message.mutable_is_media_file_request()->set_filename(filename.toStdString());
|
||||
return worker_pool_->SendMessageWithReply(&message);
|
||||
|
||||
}
|
||||
@ -85,39 +81,33 @@ TagReaderReply *TagReaderClient::IsMediaFile(const QString &filename) {
|
||||
TagReaderReply *TagReaderClient::ReadFile(const QString &filename) {
|
||||
|
||||
spb::tagreader::Message message;
|
||||
spb::tagreader::ReadFileRequest *request = message.mutable_read_file_request();
|
||||
|
||||
const QByteArray filename_data = filename.toUtf8();
|
||||
request->set_filename(filename_data.constData(), filename_data.length());
|
||||
|
||||
message.mutable_read_file_request()->set_filename(filename.toStdString());
|
||||
return worker_pool_->SendMessageWithReply(&message);
|
||||
|
||||
}
|
||||
|
||||
TagReaderReply *TagReaderClient::SaveFile(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
|
||||
TagReaderReply *TagReaderClient::WriteFile(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
|
||||
|
||||
spb::tagreader::Message message;
|
||||
spb::tagreader::SaveFileRequest *request = message.mutable_save_file_request();
|
||||
spb::tagreader::WriteFileRequest *request = message.mutable_write_file_request();
|
||||
|
||||
const QByteArray filename_data = filename.toUtf8();
|
||||
request->set_filename(filename_data.constData(), filename_data.length());
|
||||
request->set_filename(filename.toStdString());
|
||||
|
||||
request->set_save_tags(save_types.testFlag(SaveType::Tags));
|
||||
request->set_save_playcount(save_types.testFlag(SaveType::PlayCount));
|
||||
request->set_save_rating(save_types.testFlag(SaveType::Rating));
|
||||
request->set_save_cover(save_types.testFlag(SaveType::Cover));
|
||||
|
||||
if (save_cover_options.cover_filename.length() > 0) {
|
||||
const QByteArray cover_filename = save_cover_options.cover_filename.toUtf8();
|
||||
request->set_cover_filename(cover_filename.constData(), cover_filename.length());
|
||||
if (!save_cover_options.cover_filename.isEmpty()) {
|
||||
request->set_cover_filename(save_cover_options.cover_filename.toStdString());
|
||||
}
|
||||
if (save_cover_options.cover_data.length() > 0) {
|
||||
request->set_cover_data(save_cover_options.cover_data.constData(), save_cover_options.cover_data.length());
|
||||
if (!save_cover_options.cover_data.isEmpty()) {
|
||||
request->set_cover_data(save_cover_options.cover_data.toStdString());
|
||||
}
|
||||
if (save_cover_options.mime_type.length() > 0) {
|
||||
const QByteArray cover_mime_type = save_cover_options.mime_type.toUtf8();
|
||||
request->set_cover_mime_type(cover_mime_type.constData(), cover_mime_type.length());
|
||||
if (!save_cover_options.mime_type.isEmpty()) {
|
||||
request->set_cover_mime_type(save_cover_options.mime_type.toStdString());
|
||||
}
|
||||
|
||||
metadata.ToProtobuf(request->mutable_metadata());
|
||||
|
||||
ReplyType *reply = worker_pool_->SendMessageWithReply(&message);
|
||||
@ -131,8 +121,7 @@ TagReaderReply *TagReaderClient::LoadEmbeddedArt(const QString &filename) {
|
||||
spb::tagreader::Message message;
|
||||
spb::tagreader::LoadEmbeddedArtRequest *request = message.mutable_load_embedded_art_request();
|
||||
|
||||
const QByteArray filename_data = filename.toUtf8();
|
||||
request->set_filename(filename_data.constData(), filename_data.length());
|
||||
request->set_filename(filename.toStdString());
|
||||
|
||||
return worker_pool_->SendMessageWithReply(&message);
|
||||
|
||||
@ -143,63 +132,59 @@ TagReaderReply *TagReaderClient::SaveEmbeddedArt(const QString &filename, const
|
||||
spb::tagreader::Message message;
|
||||
spb::tagreader::SaveEmbeddedArtRequest *request = message.mutable_save_embedded_art_request();
|
||||
|
||||
const QByteArray filename_data = filename.toUtf8();
|
||||
request->set_filename(filename_data.constData(), filename_data.length());
|
||||
if (save_cover_options.cover_filename.length() > 0) {
|
||||
const QByteArray cover_filename = save_cover_options.cover_filename.toUtf8();
|
||||
request->set_cover_filename(cover_filename.constData(), cover_filename.length());
|
||||
request->set_filename(filename.toStdString());
|
||||
|
||||
if (!save_cover_options.cover_filename.isEmpty()) {
|
||||
request->set_cover_filename(save_cover_options.cover_filename.toStdString());
|
||||
}
|
||||
if (save_cover_options.cover_data.length() > 0) {
|
||||
request->set_cover_data(save_cover_options.cover_data.constData(), save_cover_options.cover_data.length());
|
||||
if (!save_cover_options.cover_data.isEmpty()) {
|
||||
request->set_cover_data(save_cover_options.cover_data.toStdString());
|
||||
}
|
||||
if (save_cover_options.mime_type.length() > 0) {
|
||||
const QByteArray cover_mime_type = save_cover_options.mime_type.toUtf8();
|
||||
request->set_cover_mime_type(cover_mime_type.constData(), cover_mime_type.length());
|
||||
if (!save_cover_options.mime_type.isEmpty()) {
|
||||
request->set_cover_mime_type(save_cover_options.mime_type.toStdString());
|
||||
}
|
||||
|
||||
return worker_pool_->SendMessageWithReply(&message);
|
||||
|
||||
}
|
||||
|
||||
TagReaderReply *TagReaderClient::UpdateSongPlaycount(const Song &metadata) {
|
||||
TagReaderReply *TagReaderClient::SaveSongPlaycount(const QString &filename, const uint playcount) {
|
||||
|
||||
spb::tagreader::Message message;
|
||||
spb::tagreader::SaveSongPlaycountToFileRequest *request = message.mutable_save_song_playcount_to_file_request();
|
||||
|
||||
const QByteArray filename_data = metadata.url().toLocalFile().toUtf8();
|
||||
request->set_filename(filename_data.constData(), filename_data.length());
|
||||
metadata.ToProtobuf(request->mutable_metadata());
|
||||
request->set_filename(filename.toStdString());
|
||||
request->set_playcount(playcount);
|
||||
|
||||
return worker_pool_->SendMessageWithReply(&message);
|
||||
|
||||
}
|
||||
|
||||
void TagReaderClient::UpdateSongsPlaycount(const SongList &songs) {
|
||||
void TagReaderClient::SaveSongsPlaycount(const SongList &songs) {
|
||||
|
||||
for (const Song &song : songs) {
|
||||
TagReaderReply *reply = UpdateSongPlaycount(song);
|
||||
TagReaderReply *reply = SaveSongPlaycount(song.url().toLocalFile(), song.playcount());
|
||||
QObject::connect(reply, &TagReaderReply::Finished, reply, &TagReaderReply::deleteLater);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TagReaderReply *TagReaderClient::UpdateSongRating(const Song &metadata) {
|
||||
TagReaderReply *TagReaderClient::SaveSongRating(const QString &filename, const float rating) {
|
||||
|
||||
spb::tagreader::Message message;
|
||||
spb::tagreader::SaveSongRatingToFileRequest *request = message.mutable_save_song_rating_to_file_request();
|
||||
|
||||
const QByteArray filename_data = metadata.url().toLocalFile().toUtf8();
|
||||
request->set_filename(filename_data.constData(), filename_data.length());
|
||||
metadata.ToProtobuf(request->mutable_metadata());
|
||||
request->set_filename(filename.toStdString());
|
||||
request->set_rating(rating);
|
||||
|
||||
return worker_pool_->SendMessageWithReply(&message);
|
||||
|
||||
}
|
||||
|
||||
void TagReaderClient::UpdateSongsRating(const SongList &songs) {
|
||||
void TagReaderClient::SaveSongsRating(const SongList &songs) {
|
||||
|
||||
for (const Song &song : songs) {
|
||||
TagReaderReply *reply = UpdateSongRating(song);
|
||||
TagReaderReply *reply = SaveSongRating(song.url().toLocalFile(), song.rating());
|
||||
QObject::connect(reply, &TagReaderReply::Finished, reply, &TagReaderReply::deleteLater);
|
||||
}
|
||||
|
||||
@ -209,124 +194,180 @@ bool TagReaderClient::IsMediaFileBlocking(const QString &filename) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
bool ret = false;
|
||||
bool success = false;
|
||||
|
||||
TagReaderReply *reply = IsMediaFile(filename);
|
||||
if (reply->WaitForFinished()) {
|
||||
ret = reply->message().is_media_file_response().success();
|
||||
const spb::tagreader::IsMediaFileResponse &response = reply->message().is_media_file_response();
|
||||
if (response.has_success()) {
|
||||
success = response.success();
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
return ret;
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
void TagReaderClient::ReadFileBlocking(const QString &filename, Song *song) {
|
||||
TagReaderClient::Result TagReaderClient::ReadFileBlocking(const QString &filename, Song *song) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
Result result(Result::ErrorCode::Failure);
|
||||
|
||||
TagReaderReply *reply = ReadFile(filename);
|
||||
if (reply->WaitForFinished()) {
|
||||
song->InitFromProtobuf(reply->message().read_file_response().metadata());
|
||||
const spb::tagreader::ReadFileResponse &response = reply->message().read_file_response();
|
||||
if (response.has_success()) {
|
||||
if (response.success()) {
|
||||
result.error_code = Result::ErrorCode::Success;
|
||||
if (response.has_metadata()) {
|
||||
song->InitFromProtobuf(response.metadata());
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.error_code = Result::ErrorCode::Failure;
|
||||
if (response.has_error()) {
|
||||
result.error = QString::fromStdString(response.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderClient::SaveFileBlocking(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
|
||||
TagReaderClient::Result TagReaderClient::WriteFileBlocking(const QString &filename, const Song &metadata, const SaveTypes save_types, const SaveCoverOptions &save_cover_options) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
bool ret = false;
|
||||
Result result(Result::ErrorCode::Failure);
|
||||
|
||||
TagReaderReply *reply = SaveFile(filename, metadata, save_types, save_cover_options);
|
||||
TagReaderReply *reply = WriteFile(filename, metadata, save_types, save_cover_options);
|
||||
if (reply->WaitForFinished()) {
|
||||
ret = reply->message().save_file_response().success();
|
||||
const spb::tagreader::WriteFileResponse &response = reply->message().write_file_response();
|
||||
if (response.has_success()) {
|
||||
result.error_code = response.success() ? Result::ErrorCode::Success : Result::ErrorCode::Failure;
|
||||
if (response.has_error()) {
|
||||
result.error = QString::fromStdString(response.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
return ret;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
QByteArray TagReaderClient::LoadEmbeddedArtBlocking(const QString &filename) {
|
||||
TagReaderClient::Result TagReaderClient::LoadEmbeddedArtBlocking(const QString &filename, QByteArray &data) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
QByteArray ret;
|
||||
Result result(Result::ErrorCode::Failure);
|
||||
|
||||
TagReaderReply *reply = LoadEmbeddedArt(filename);
|
||||
if (reply->WaitForFinished()) {
|
||||
const std::string &data_str = reply->message().load_embedded_art_response().data();
|
||||
ret = QByteArray(data_str.data(), static_cast<qint64>(data_str.size()));
|
||||
const spb::tagreader::LoadEmbeddedArtResponse &response = reply->message().load_embedded_art_response();
|
||||
if (response.has_success()) {
|
||||
if (response.success()) {
|
||||
result.error_code = Result::ErrorCode::Success;
|
||||
if (response.has_data()) {
|
||||
data = QByteArray(response.data().data(), static_cast<qint64>(response.data().size()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.error_code = Result::ErrorCode::Failure;
|
||||
if (response.has_error()) {
|
||||
result.error = QString::fromStdString(response.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
return ret;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
QImage TagReaderClient::LoadEmbeddedArtAsImageBlocking(const QString &filename) {
|
||||
TagReaderClient::Result TagReaderClient::LoadEmbeddedArtAsImageBlocking(const QString &filename, QImage &image) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
QImage ret;
|
||||
|
||||
TagReaderReply *reply = LoadEmbeddedArt(filename);
|
||||
if (reply->WaitForFinished()) {
|
||||
const std::string &data_str = reply->message().load_embedded_art_response().data();
|
||||
ret.loadFromData(QByteArray(data_str.data(), static_cast<qint64>(data_str.size())));
|
||||
QByteArray data;
|
||||
Result result = LoadEmbeddedArtBlocking(filename, data);
|
||||
if (result.error_code == Result::ErrorCode::Success && !image.loadFromData(data)) {
|
||||
result.error_code = Result::ErrorCode::Failure;
|
||||
result.error = QObject::tr("Failed to load image from data for %1").arg(filename);
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
return ret;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderClient::SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options) {
|
||||
TagReaderClient::Result TagReaderClient::SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
bool success = false;
|
||||
Result result(Result::ErrorCode::Failure);
|
||||
|
||||
TagReaderReply *reply = SaveEmbeddedArt(filename, save_cover_options);
|
||||
if (reply->WaitForFinished()) {
|
||||
success = reply->message().save_embedded_art_response().success();
|
||||
const spb::tagreader::SaveEmbeddedArtResponse &response = reply->message().save_embedded_art_response();
|
||||
if (response.has_success()) {
|
||||
result.error_code = response.success() ? Result::ErrorCode::Success : Result::ErrorCode::Failure;
|
||||
if (response.has_error()) {
|
||||
result.error = QString::fromStdString(response.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
return success;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderClient::UpdateSongPlaycountBlocking(const Song &metadata) {
|
||||
TagReaderClient::Result TagReaderClient::SaveSongPlaycountBlocking(const QString &filename, const uint playcount) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
bool success = false;
|
||||
Result result(Result::ErrorCode::Failure);
|
||||
|
||||
TagReaderReply *reply = UpdateSongPlaycount(metadata);
|
||||
TagReaderReply *reply = SaveSongPlaycount(filename, playcount);
|
||||
if (reply->WaitForFinished()) {
|
||||
success = reply->message().save_song_playcount_to_file_response().success();
|
||||
const spb::tagreader::SaveSongPlaycountToFileResponse &response = reply->message().save_song_playcount_to_file_response();
|
||||
if (response.has_success()) {
|
||||
result.error_code = response.success() ? Result::ErrorCode::Success : Result::ErrorCode::Failure;
|
||||
if (response.has_error()) {
|
||||
result.error = QString::fromStdString(response.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
return success;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
bool TagReaderClient::UpdateSongRatingBlocking(const Song &metadata) {
|
||||
TagReaderClient::Result TagReaderClient::SaveSongRatingBlocking(const QString &filename, const float rating) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
bool success = false;
|
||||
Result result(Result::ErrorCode::Failure);
|
||||
|
||||
TagReaderReply *reply = UpdateSongRating(metadata);
|
||||
TagReaderReply *reply = SaveSongRating(filename, rating);
|
||||
if (reply->WaitForFinished()) {
|
||||
success = reply->message().save_song_rating_to_file_response().success();
|
||||
const spb::tagreader::SaveSongRatingToFileResponse &response = reply->message().save_song_rating_to_file_response();
|
||||
if (response.has_success()) {
|
||||
result.error_code = response.success() ? Result::ErrorCode::Success : Result::ErrorCode::Failure;
|
||||
if (response.has_error()) {
|
||||
result.error = QString::fromStdString(response.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
return success;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2011, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2019-2023, Jonas Kvinge <jonas@jkvinge.net>
|
||||
* Copyright 2019-2024, 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
|
||||
@ -70,24 +70,45 @@ class TagReaderClient : public QObject {
|
||||
QString mime_type;
|
||||
};
|
||||
|
||||
class Result {
|
||||
public:
|
||||
enum class ErrorCode {
|
||||
Success,
|
||||
Unsupported,
|
||||
Failure,
|
||||
};
|
||||
Result(const ErrorCode _error_code, const QString &_error = QString()) : error_code(_error_code), error(_error) {}
|
||||
ErrorCode error_code;
|
||||
QString error;
|
||||
bool success() const { return error_code == TagReaderClient::Result::ErrorCode::Success; }
|
||||
};
|
||||
|
||||
class Cover {
|
||||
public:
|
||||
explicit Cover(const QByteArray &_data = QByteArray(), const QString &_mime_type = QString()) : data(_data), mime_type(_mime_type) {}
|
||||
QByteArray data;
|
||||
QString mime_type;
|
||||
QString error;
|
||||
};
|
||||
|
||||
ReplyType *IsMediaFile(const QString &filename);
|
||||
ReplyType *ReadFile(const QString &filename);
|
||||
ReplyType *SaveFile(const QString &filename, const Song &metadata, const SaveTypes types = SaveType::Tags, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
|
||||
ReplyType *WriteFile(const QString &filename, const Song &metadata, const SaveTypes types = SaveType::Tags, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
|
||||
ReplyType *LoadEmbeddedArt(const QString &filename);
|
||||
ReplyType *SaveEmbeddedArt(const QString &filename, const SaveCoverOptions &save_cover_options);
|
||||
ReplyType *UpdateSongPlaycount(const Song &metadata);
|
||||
ReplyType *UpdateSongRating(const Song &metadata);
|
||||
ReplyType *SaveSongPlaycount(const QString &filename, const uint playcount);
|
||||
ReplyType *SaveSongRating(const QString &filename, const float rating);
|
||||
|
||||
// Convenience functions that call the above functions and wait for a response.
|
||||
// These block the calling thread with a semaphore, and must NOT be called from the TagReaderClient's thread.
|
||||
void ReadFileBlocking(const QString &filename, Song *song);
|
||||
bool SaveFileBlocking(const QString &filename, const Song &metadata, const SaveTypes types = SaveType::Tags, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
|
||||
Result ReadFileBlocking(const QString &filename, Song *song);
|
||||
Result WriteFileBlocking(const QString &filename, const Song &metadata, const SaveTypes types = SaveType::Tags, const SaveCoverOptions &save_cover_options = SaveCoverOptions());
|
||||
bool IsMediaFileBlocking(const QString &filename);
|
||||
QByteArray LoadEmbeddedArtBlocking(const QString &filename);
|
||||
QImage LoadEmbeddedArtAsImageBlocking(const QString &filename);
|
||||
bool SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options);
|
||||
bool UpdateSongPlaycountBlocking(const Song &metadata);
|
||||
bool UpdateSongRatingBlocking(const Song &metadata);
|
||||
Result LoadEmbeddedArtBlocking(const QString &filename, QByteArray &data);
|
||||
Result LoadEmbeddedArtAsImageBlocking(const QString &filename, QImage &image);
|
||||
Result SaveEmbeddedArtBlocking(const QString &filename, const SaveCoverOptions &save_cover_options);
|
||||
Result SaveSongPlaycountBlocking(const QString &filename, const uint playcount);
|
||||
Result SaveSongRatingBlocking(const QString &filename, const float rating);
|
||||
|
||||
// TODO: Make this not a singleton
|
||||
static TagReaderClient *Instance() { return sInstance; }
|
||||
@ -100,8 +121,8 @@ class TagReaderClient : public QObject {
|
||||
void WorkerFailedToStart();
|
||||
|
||||
public slots:
|
||||
void UpdateSongsPlaycount(const SongList &songs);
|
||||
void UpdateSongsRating(const SongList &songs);
|
||||
void SaveSongsPlaycount(const SongList &songs);
|
||||
void SaveSongsRating(const SongList &songs);
|
||||
|
||||
private:
|
||||
static TagReaderClient *sInstance;
|
||||
|
@ -440,8 +440,9 @@ void AlbumCoverChoiceController::ShowCover(const Song &song, const QImage &image
|
||||
}
|
||||
case AlbumCoverLoaderOptions::Type::Embedded:{
|
||||
if (song.art_embedded() && !song.url().isEmpty() && song.url().isValid() && song.url().isLocalFile()) {
|
||||
const QImage image_embedded_cover = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(song.url().toLocalFile());
|
||||
if (!image_embedded_cover.isNull()) {
|
||||
QImage image_embedded_cover;
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(song.url().toLocalFile(), image_embedded_cover);
|
||||
if (result.success() && !image_embedded_cover.isNull()) {
|
||||
QPixmap pixmap = QPixmap::fromImage(image_embedded_cover);
|
||||
if (!pixmap.isNull()) {
|
||||
pixmap.setDevicePixelRatio(devicePixelRatioF());
|
||||
|
@ -290,8 +290,8 @@ AlbumCoverLoader::LoadImageResult AlbumCoverLoader::LoadImage(TaskPtr task, cons
|
||||
AlbumCoverLoader::LoadImageResult AlbumCoverLoader::LoadEmbeddedImage(TaskPtr task) {
|
||||
|
||||
if (task->art_embedded && task->song_url.isValid() && task->song_url.isLocalFile()) {
|
||||
task->album_cover.image_data = TagReaderClient::Instance()->LoadEmbeddedArtBlocking(task->song_url.toLocalFile());
|
||||
if (!task->album_cover.image_data.isEmpty() && task->album_cover.image.loadFromData(task->album_cover.image_data)) {
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->LoadEmbeddedArtBlocking(task->song_url.toLocalFile(), task->album_cover.image_data);
|
||||
if (result.success() && !task->album_cover.image_data.isEmpty() && task->album_cover.image.loadFromData(task->album_cover.image_data)) {
|
||||
return LoadImageResult(AlbumCoverLoaderResult::Type::Embedded, LoadImageResult::Status::Success);
|
||||
}
|
||||
}
|
||||
|
@ -717,7 +717,10 @@ void AlbumCoverManager::SaveCoverToFile() {
|
||||
return;
|
||||
case AlbumCoverLoaderOptions::Type::Embedded:
|
||||
if (song.art_embedded()) {
|
||||
result.image_data = TagReaderClient::Instance()->LoadEmbeddedArtBlocking(song.url().toLocalFile());
|
||||
const TagReaderClient::Result tagreaderclient_result = TagReaderClient::Instance()->LoadEmbeddedArtBlocking(song.url().toLocalFile(), result.image_data);
|
||||
if (!tagreaderclient_result.success()) {
|
||||
qLog(Error) << "Could not load embedded art from" << song.url() << tagreaderclient_result.error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AlbumCoverLoaderOptions::Type::Automatic:
|
||||
|
@ -76,8 +76,8 @@ void CoverExportRunnable::ProcessAndExportCover() {
|
||||
break;
|
||||
case AlbumCoverLoaderOptions::Type::Embedded:
|
||||
if (song_.art_embedded() && dialog_result_.export_embedded_) {
|
||||
image = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(song_.url().toLocalFile());
|
||||
if (!image.isNull()) {
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(song_.url().toLocalFile(), image);
|
||||
if (result.success() && !image.isNull()) {
|
||||
extension = QLatin1String("jpg");
|
||||
}
|
||||
}
|
||||
@ -168,8 +168,8 @@ void CoverExportRunnable::ExportCover() {
|
||||
break;
|
||||
case AlbumCoverLoaderOptions::Type::Embedded:
|
||||
if (song_.art_embedded() && dialog_result_.export_embedded_) {
|
||||
image = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(song_.url().toLocalFile());
|
||||
if (!image.isNull()) {
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(song_.url().toLocalFile(), image);
|
||||
if (result.success() && !image.isNull()) {
|
||||
embedded_cover = true;
|
||||
extension = QLatin1String("jpg");
|
||||
}
|
||||
|
@ -399,8 +399,8 @@ QList<EditTagDialog::Data> EditTagDialog::LoadData(const SongList &songs) {
|
||||
if (song.IsEditable()) {
|
||||
// Try reloading the tags from file
|
||||
Song copy(song);
|
||||
TagReaderClient::Instance()->ReadFileBlocking(copy.url().toLocalFile(), ©);
|
||||
if (copy.is_valid()) {
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(copy.url().toLocalFile(), ©);
|
||||
if (result.success() && copy.is_valid()) {
|
||||
copy.MergeUserSetData(song, false, false);
|
||||
ret << Data(copy);
|
||||
}
|
||||
@ -1295,7 +1295,7 @@ void EditTagDialog::SaveData() {
|
||||
if (save_embedded_cover) {
|
||||
save_types |= TagReaderClient::SaveType::Cover;
|
||||
}
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(ref.current_.url().toLocalFile(), ref.current_, save_types, savecover_options);
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->WriteFile(ref.current_.url().toLocalFile(), ref.current_, save_types, savecover_options);
|
||||
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, ref]() { SongSaveTagsComplete(reply, ref.current_.url().toLocalFile(), ref.current_, ref.cover_action_); }, Qt::QueuedConnection);
|
||||
}
|
||||
// If the cover was changed, but no tags written, make sure to update the collection.
|
||||
@ -1450,7 +1450,11 @@ void EditTagDialog::UpdateLyrics(const quint64 id, const QString &provider, cons
|
||||
void EditTagDialog::SongSaveTagsComplete(TagReaderReply *reply, const QString &filename, Song song, const UpdateCoverAction cover_action) {
|
||||
|
||||
--save_tag_pending_;
|
||||
const bool success = reply->message().save_file_response().success();
|
||||
const bool success = reply->message().write_file_response().success();
|
||||
QString error;
|
||||
if (!success && reply->message().write_file_response().has_error()) {
|
||||
error = QString::fromStdString(reply->message().write_file_response().error());
|
||||
}
|
||||
reply->deleteLater();
|
||||
|
||||
if (success) {
|
||||
@ -1483,7 +1487,12 @@ void EditTagDialog::SongSaveTagsComplete(TagReaderReply *reply, const QString &f
|
||||
}
|
||||
}
|
||||
else {
|
||||
emit Error(tr("An error occurred writing metadata to '%1'").arg(filename));
|
||||
if (error.isEmpty()) {
|
||||
emit Error(tr("Could not write metadata to %1").arg(filename));
|
||||
}
|
||||
else {
|
||||
emit Error(tr("Could not write metadata to %1: %2").arg(filename, error));
|
||||
}
|
||||
}
|
||||
|
||||
if (save_tag_pending_ <= 0) SaveDataFinished();
|
||||
|
@ -278,8 +278,9 @@ void TrackSelectionDialog::SaveData(const QList<Data> &data) {
|
||||
copy.set_track(new_metadata.track());
|
||||
copy.set_year(new_metadata.year());
|
||||
|
||||
if (!TagReaderClient::Instance()->SaveFileBlocking(copy.url().toLocalFile(), copy)) {
|
||||
qLog(Warning) << "Failed to write new auto-tags to" << copy.url().toLocalFile();
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->WriteFileBlocking(copy.url().toLocalFile(), copy, TagReaderClient::SaveType::Tags, TagReaderClient::SaveCoverOptions());
|
||||
if (!result.success()) {
|
||||
qLog(Error) << "Failed to write new auto-tags to" << copy.url().toLocalFile() << result.error;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,7 +245,10 @@ void Organize::ProcessSomeFiles() {
|
||||
}
|
||||
}
|
||||
else if (destination_->source() == Song::Source::Device) {
|
||||
job.cover_image_ = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(task.song_info_.song_.url().toLocalFile());
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->LoadEmbeddedArtAsImageBlocking(task.song_info_.song_.url().toLocalFile(), job.cover_image_);
|
||||
if (!result.success()) {
|
||||
qLog(Error) << "Could not save embedded art to" << task.song_info_.song_.url() << result.error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!job.cover_source_.isEmpty()) {
|
||||
|
@ -415,8 +415,13 @@ SongList OrganizeDialog::LoadSongsBlocking(const QStringList &filenames) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TagReaderClient::Instance()->ReadFileBlocking(filename, &song);
|
||||
if (song.is_valid()) songs << song;
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(filename, &song);
|
||||
if (result.success() && song.is_valid()) {
|
||||
songs << song;
|
||||
}
|
||||
else {
|
||||
qLog(Error) << "Could not read file" << filename << result.error;
|
||||
}
|
||||
}
|
||||
|
||||
return songs;
|
||||
|
@ -415,7 +415,7 @@ bool Playlist::setData(const QModelIndex &idx, const QVariant &value, const int
|
||||
if (!set_column_value(song, static_cast<Column>(idx.column()), value)) return false;
|
||||
|
||||
if (song.url().isLocalFile()) {
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->SaveFile(song.url().toLocalFile(), song);
|
||||
TagReaderReply *reply = TagReaderClient::Instance()->WriteFile(song.url().toLocalFile(), song);
|
||||
QPersistentModelIndex persistent_index = QPersistentModelIndex(idx);
|
||||
QObject::connect(reply, &TagReaderReply::Finished, this, [this, reply, persistent_index, item]() { SongSaveComplete(reply, persistent_index, item->OriginalMetadata()); }, Qt::QueuedConnection);
|
||||
}
|
||||
@ -431,11 +431,16 @@ bool Playlist::setData(const QModelIndex &idx, const QVariant &value, const int
|
||||
void Playlist::SongSaveComplete(TagReaderReply *reply, const QPersistentModelIndex &idx, const Song &old_metadata) {
|
||||
|
||||
if (reply->is_successful() && idx.isValid()) {
|
||||
if (reply->message().save_file_response().success()) {
|
||||
if (reply->message().write_file_response().success()) {
|
||||
ItemReload(idx, old_metadata, true);
|
||||
}
|
||||
else {
|
||||
emit Error(tr("An error occurred writing metadata to '%1'").arg(QString::fromStdString(reply->request_message().save_file_request().filename())));
|
||||
if (reply->request_message().write_file_response().has_error()) {
|
||||
emit Error(tr("Could not write metadata to %1: %2").arg(QString::fromStdString(reply->request_message().write_file_request().filename()), QString::fromStdString(reply->request_message().write_file_response().error())));
|
||||
}
|
||||
else {
|
||||
emit Error(tr("Could not write metadata to %1").arg(QString::fromStdString(reply->request_message().write_file_request().filename())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,12 @@ QUrl SongPlaylistItem::Url() const { return song_.url(); }
|
||||
void SongPlaylistItem::Reload() {
|
||||
|
||||
if (!song_.url().isLocalFile()) return;
|
||||
TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
|
||||
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(song_.url().toLocalFile(), &song_);
|
||||
if (!result.success()) {
|
||||
qLog(Error) << "Could not reload file" << song_.url() << result.error;
|
||||
}
|
||||
|
||||
UpdateTemporaryMetadata(song_);
|
||||
|
||||
}
|
||||
|
@ -102,7 +102,10 @@ void ParserBase::LoadSong(const QString &filename_or_url, const qint64 beginning
|
||||
}
|
||||
}
|
||||
|
||||
TagReaderClient::Instance()->ReadFileBlocking(filename, song);
|
||||
const TagReaderClient::Result result = TagReaderClient::Instance()->ReadFileBlocking(filename, song);
|
||||
if (!result.success()) {
|
||||
qLog(Error) << "Could not read file" << filename << result.error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -188,12 +188,15 @@ SongList PlaylistParser::LoadFromFile(const QString &filename) const {
|
||||
|
||||
// Open the file
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly)) return SongList();
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
emit Error(tr("Could not open file %1").arg(filename));
|
||||
return SongList();
|
||||
}
|
||||
|
||||
SongList ret = parser->Load(&file, filename, fileinfo.absolutePath());
|
||||
const SongList songs = parser->Load(&file, filename, fileinfo.absolutePath(), true);
|
||||
file.close();
|
||||
|
||||
return ret;
|
||||
return songs;
|
||||
|
||||
}
|
||||
|
||||
|
@ -29,9 +29,9 @@
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
#if defined(USE_TAGLIB)
|
||||
#if defined(HAVE_TAGLIB)
|
||||
# include "tagreadertaglib.h"
|
||||
#elif defined(USE_TAGPARSER)
|
||||
#elif defined(HAVE_TAGPARSER)
|
||||
# include "tagreadertagparser.h"
|
||||
#endif
|
||||
|
||||
@ -45,15 +45,15 @@ class TagReaderTest : public ::testing::Test {
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
// Return something from uninteresting mock functions.
|
||||
#if defined(USE_TAGLIB)
|
||||
#if defined(HAVE_TAGLIB)
|
||||
testing::DefaultValue<TagLib::String>::Set("foobarbaz");
|
||||
#endif
|
||||
}
|
||||
|
||||
static Song ReadSongFromFile(const QString& filename) {
|
||||
#if defined(USE_TAGLIB)
|
||||
#if defined(HAVE_TAGLIB)
|
||||
TagReaderTagLib tag_reader;
|
||||
#elif defined(USE_TAGPARSER)
|
||||
#elif defined(HAVE_TAGPARSER)
|
||||
TagReaderTagParser tag_reader;
|
||||
#endif
|
||||
Song song;
|
||||
@ -67,17 +67,16 @@ class TagReaderTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
static void WriteSongToFile(const Song &song, const QString &filename) {
|
||||
#if defined(USE_TAGLIB)
|
||||
#if defined(HAVE_TAGLIB)
|
||||
TagReaderTagLib tag_reader;
|
||||
#elif defined(USE_TAGPARSER)
|
||||
#elif defined(HAVE_TAGPARSER)
|
||||
TagReaderTagParser tag_reader;
|
||||
#endif
|
||||
::spb::tagreader::SaveFileRequest request;
|
||||
const QByteArray filename_data = filename.toUtf8();
|
||||
request.set_filename(filename_data.constData(), filename_data.length());
|
||||
::spb::tagreader::WriteFileRequest request;
|
||||
request.set_filename(filename.toStdString());
|
||||
request.set_save_tags(true);
|
||||
song.ToProtobuf(request.mutable_metadata());
|
||||
tag_reader.SaveFile(request);
|
||||
tag_reader.WriteFile(filename, request);
|
||||
}
|
||||
|
||||
static QString SHA256SUM(const QString &filename) {
|
||||
@ -98,25 +97,25 @@ class TagReaderTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
static void WriteSongPlaycountToFile(const Song &song, const QString &filename) {
|
||||
#if defined(USE_TAGLIB)
|
||||
#if defined(HAVE_TAGLIB)
|
||||
TagReaderTagLib tag_reader;
|
||||
#elif defined(USE_TAGPARSER)
|
||||
#elif defined(HAVE_TAGPARSER)
|
||||
TagReaderTagParser tag_reader;
|
||||
#endif
|
||||
spb::tagreader::SongMetadata pb_song;
|
||||
song.ToProtobuf(&pb_song);
|
||||
tag_reader.SaveSongPlaycountToFile(filename, pb_song);
|
||||
tag_reader.SaveSongPlaycountToFile(filename, pb_song.playcount());
|
||||
}
|
||||
|
||||
static void WriteSongRatingToFile(const Song &song, const QString &filename) {
|
||||
#if defined(USE_TAGLIB)
|
||||
#if defined(HAVE_TAGLIB)
|
||||
TagReaderTagLib tag_reader;
|
||||
#elif defined(USE_TAGPARSER)
|
||||
#elif defined(HAVE_TAGPARSER)
|
||||
TagReaderTagParser tag_reader;
|
||||
#endif
|
||||
spb::tagreader::SongMetadata pb_song;
|
||||
song.ToProtobuf(&pb_song);
|
||||
tag_reader.SaveSongRatingToFile(filename, pb_song);
|
||||
tag_reader.SaveSongRatingToFile(filename, pb_song.rating());
|
||||
}
|
||||
|
||||
};
|
||||
@ -1940,7 +1939,7 @@ TEST_F(TagReaderTest, TestMP4AudioFileCompilation) {
|
||||
|
||||
}
|
||||
|
||||
#ifndef USE_TAGPARSER
|
||||
#ifndef HAVE_TAGPARSER
|
||||
|
||||
TEST_F(TagReaderTest, TestFLACAudioFilePlaycount) {
|
||||
|
||||
@ -2095,7 +2094,7 @@ TEST_F(TagReaderTest, TestMP4AudioFilePlaycount) {
|
||||
|
||||
}
|
||||
|
||||
#endif // USE_TAGPARSER
|
||||
#endif // HAVE_TAGPARSER
|
||||
|
||||
TEST_F(TagReaderTest, TestFLACAudioFileRating) {
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user