Add support for TagParser as an alternative to TagLib
This commit is contained in:
parent
88819611f4
commit
239b88aa3b
|
@ -223,16 +223,36 @@ if(X11_FOUND)
|
||||||
endif()
|
endif()
|
||||||
endif(X11_FOUND)
|
endif(X11_FOUND)
|
||||||
|
|
||||||
|
option(USE_TAGLIB "Build with TagLib" OFF)
|
||||||
|
option(USE_TAGPARSER "Build with TagParser" OFF)
|
||||||
|
|
||||||
|
if(NOT USE_TAGLIB AND NOT USE_TAGPARSER)
|
||||||
|
set(USE_TAGLIB ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
# TAGLIB
|
# TAGLIB
|
||||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
|
if(USE_TAGLIB)
|
||||||
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
|
pkg_check_modules(TAGLIB REQUIRED taglib>=1.11.1)
|
||||||
find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h)
|
if(TAGLIB_FOUND)
|
||||||
if(HAVE_TAGLIB_DSFFILE_H)
|
find_path(HAVE_TAGLIB_DSFFILE_H taglib/dsffile.h)
|
||||||
set(HAVE_TAGLIB_DSFFILE ON)
|
find_path(HAVE_TAGLIB_DSDIFFFILE_H taglib/dsdifffile.h)
|
||||||
endif(HAVE_TAGLIB_DSFFILE_H)
|
if(HAVE_TAGLIB_DSFFILE_H)
|
||||||
if(HAVE_TAGLIB_DSDIFFFILE_H)
|
set(HAVE_TAGLIB_DSFFILE ON)
|
||||||
set(HAVE_TAGLIB_DSDIFFFILE ON)
|
endif(HAVE_TAGLIB_DSFFILE_H)
|
||||||
endif(HAVE_TAGLIB_DSDIFFFILE_H)
|
if(HAVE_TAGLIB_DSDIFFFILE_H)
|
||||||
|
set(HAVE_TAGLIB_DSDIFFFILE ON)
|
||||||
|
endif(HAVE_TAGLIB_DSDIFFFILE_H)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TAGPARSER
|
||||||
|
if(USE_TAGPARSER)
|
||||||
|
pkg_check_modules(TAGPARSER REQUIRED tagparser)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TAGLIB_FOUND AND NOT TAGPARSER_FOUND)
|
||||||
|
message(FATAL_ERROR "You need either TagLib or TagParser!")
|
||||||
|
endif()
|
||||||
|
|
||||||
# SingleApplication
|
# SingleApplication
|
||||||
add_subdirectory(3rdparty/singleapplication)
|
add_subdirectory(3rdparty/singleapplication)
|
||||||
|
@ -476,6 +496,6 @@ if(NOT CMAKE_CROSSCOMPILING)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT TAGLIB_VERSION VERSION_GREATER_EQUAL 1.12)
|
if(USE_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.")
|
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()
|
endif()
|
||||||
|
|
|
@ -76,7 +76,7 @@ To build Strawberry from source you need the following installed on your system
|
||||||
* [D-Bus (Linux required)](https://www.freedesktop.org/wiki/Software/dbus/)
|
* [D-Bus (Linux required)](https://www.freedesktop.org/wiki/Software/dbus/)
|
||||||
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
|
* [GStreamer](https://gstreamer.freedesktop.org/) or [VLC](https://www.videolan.org)
|
||||||
* [GnuTLS](https://www.gnutls.org/)
|
* [GnuTLS](https://www.gnutls.org/)
|
||||||
* [TagLib 1.11.1 or higher](https://www.taglib.org/)
|
* [TagLib 1.11.1 or higher](https://www.taglib.org/) or [TagParser](https://github.com/Martchus/tagparser)
|
||||||
|
|
||||||
Optional dependencies:
|
Optional dependencies:
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
|
||||||
set(MESSAGES tagreadermessages.proto)
|
set(MESSAGES tagreadermessages.proto)
|
||||||
set(SOURCES tagreader.cpp)
|
set(SOURCES tagreaderbase.cpp)
|
||||||
|
|
||||||
|
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||||
|
list(APPEND SOURCES tagreadertaglib.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||||
|
list(APPEND SOURCES tagreadertagparser.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
|
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
|
||||||
|
|
||||||
|
@ -11,12 +19,19 @@ link_directories(
|
||||||
${TAGLIB_LIBRARY_DIRS}
|
${TAGLIB_LIBRARY_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||||
|
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||||
|
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
add_library(libstrawberry-tagreader STATIC ${PROTO_SOURCES} ${SOURCES})
|
||||||
|
|
||||||
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
|
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
${TAGLIB_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(libstrawberry-tagreader PRIVATE
|
target_include_directories(libstrawberry-tagreader PRIVATE
|
||||||
|
@ -30,8 +45,17 @@ target_include_directories(libstrawberry-tagreader PRIVATE
|
||||||
target_link_libraries(libstrawberry-tagreader PRIVATE
|
target_link_libraries(libstrawberry-tagreader PRIVATE
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${PROTOBUF_LIBRARY}
|
${PROTOBUF_LIBRARY}
|
||||||
${TAGLIB_LIBRARIES}
|
|
||||||
${QtCore_LIBRARIES}
|
${QtCore_LIBRARIES}
|
||||||
${QtNetwork_LIBRARIES}
|
${QtNetwork_LIBRARIES}
|
||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||||
|
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)
|
||||||
|
target_include_directories(libstrawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(libstrawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* This file is part of Strawberry.
|
||||||
|
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
|
||||||
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "tagreaderbase.h"
|
||||||
|
|
||||||
|
const std::string TagReaderBase::kEmbeddedCover = "(embedded)";
|
||||||
|
|
||||||
|
TagReaderBase::TagReaderBase() {}
|
||||||
|
TagReaderBase::~TagReaderBase() {}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* This file is part of Strawberry.
|
||||||
|
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
|
||||||
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TAGREADERBASE_H
|
||||||
|
#define TAGREADERBASE_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "tagreadermessages.pb.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This class holds all useful methods to read and write tags from/to files.
|
||||||
|
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
|
||||||
|
*/
|
||||||
|
class TagReaderBase {
|
||||||
|
public:
|
||||||
|
explicit TagReaderBase();
|
||||||
|
~TagReaderBase();
|
||||||
|
|
||||||
|
virtual bool IsMediaFile(const QString &filename) const = 0;
|
||||||
|
|
||||||
|
virtual void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const = 0;
|
||||||
|
virtual bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const = 0;
|
||||||
|
|
||||||
|
virtual QByteArray LoadEmbeddedArt(const QString &filename) const = 0;
|
||||||
|
virtual bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static const std::string kEmbeddedCover;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TAGREADERBASE_H
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "tagreader.h"
|
#include "tagreadertaglib.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -132,25 +132,22 @@ const char *kASF_OriginalYear_ID = "WM/OriginalReleaseYear";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
TagReader::TagReader() :
|
TagReaderTagLib::TagReaderTaglib() : factory_(new TagLibFileRefFactory) {}
|
||||||
factory_(new TagLibFileRefFactory),
|
|
||||||
kEmbeddedCover("(embedded)") {
|
|
||||||
}
|
|
||||||
|
|
||||||
TagReader::~TagReader() {
|
TagReaderTagLib::~TagReaderTaglib() {
|
||||||
delete factory_;
|
delete factory_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReader::IsMediaFile(const QString &filename) const {
|
bool TagReaderTagLib::IsMediaFile(const QString &filename) const {
|
||||||
|
|
||||||
qLog(Debug) << "Checking for valid file" << filename;
|
qLog(Debug) << "Checking for valid file" << filename;
|
||||||
|
|
||||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||||
return !fileref->isNull() && fileref->tag();
|
return fileref && !fileref->isNull() && fileref->file() && fileref->tag();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
spb::tagreader::SongMetadata_FileType TagReaderTagLib::GuessFileType(TagLib::FileRef *fileref) const {
|
||||||
|
|
||||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_WAV;
|
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_WAV;
|
||||||
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_FLAC;
|
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file())) return spb::tagreader::SongMetadata_FileType_FLAC;
|
||||||
|
@ -177,7 +174,7 @@ spb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
void TagReaderTagLib::ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
||||||
const QFileInfo info(filename);
|
const QFileInfo info(filename);
|
||||||
|
@ -428,20 +425,20 @@ void TagReader::ReadFile(const QString &filename, spb::tagreader::SongMetadata *
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::Decode(const TagLib::String &tag, std::string *output) {
|
void TagReaderTagLib::Decode(const TagLib::String &tag, std::string *output) {
|
||||||
|
|
||||||
QString tmp = TStringToQString(tag).trimmed();
|
QString tmp = TStringToQString(tag).trimmed();
|
||||||
output->assign(DataCommaSizeFromQString(tmp));
|
output->assign(DataCommaSizeFromQString(tmp));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::Decode(const QString &tag, std::string *output) {
|
void TagReaderTagLib::Decode(const QString &tag, std::string *output) {
|
||||||
|
|
||||||
output->assign(DataCommaSizeFromQString(tag));
|
output->assign(DataCommaSizeFromQString(tag));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
void TagReaderTagLib::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
|
if (!map["COMPOSER"].isEmpty()) Decode(map["COMPOSER"].front(), song->mutable_composer());
|
||||||
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer());
|
if (!map["PERFORMER"].isEmpty()) Decode(map["PERFORMER"].front(), song->mutable_performer());
|
||||||
|
@ -466,7 +463,7 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, QString *disc,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
void TagReaderTagLib::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc, QString *compilation, spb::tagreader::SongMetadata *song) const {
|
||||||
|
|
||||||
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
TagLib::APE::ItemListMap::ConstIterator it = map.find("ALBUM ARTIST");
|
||||||
if (it != map.end()) {
|
if (it != map.end()) {
|
||||||
|
@ -510,7 +507,7 @@ void TagReader::ParseAPETag(const TagLib::APE::ItemListMap &map, QString *disc,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const spb::tagreader::SongMetadata &song) const {
|
void TagReaderTagLib::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const spb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
||||||
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
||||||
|
@ -528,7 +525,7 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReader::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
bool TagReaderTagLib::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
if (filename.isEmpty()) return false;
|
if (filename.isEmpty()) return false;
|
||||||
|
|
||||||
|
@ -609,7 +606,7 @@ bool TagReader::SaveFile(const QString &filename, const spb::tagreader::SongMeta
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
void TagReaderTagLib::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
tag->setItem("album artist", TagLib::APE::Item("album artist", TagLib::StringList(song.albumartist().c_str())));
|
||||||
tag->addValue("disc", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
tag->addValue("disc", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||||
|
@ -621,14 +618,14 @@ void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const spb::tagreader::SongMeta
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const {
|
void TagReaderTagLib::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const {
|
||||||
|
|
||||||
const QByteArray utf8(value.toUtf8());
|
const QByteArray utf8(value.toUtf8());
|
||||||
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
|
SetTextFrame(id, std::string(utf8.constData(), utf8.length()), tag);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
void TagReaderTagLib::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||||
|
|
||||||
TagLib::ByteVector id_vector(id);
|
TagLib::ByteVector id_vector(id);
|
||||||
QVector<TagLib::ByteVector> frames_buffer;
|
QVector<TagLib::ByteVector> frames_buffer;
|
||||||
|
@ -659,7 +656,7 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
void TagReaderTagLib::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||||
|
|
||||||
TagLib::ByteVector id_vector("USLT");
|
TagLib::ByteVector id_vector("USLT");
|
||||||
QVector<TagLib::ByteVector> frames_buffer;
|
QVector<TagLib::ByteVector> frames_buffer;
|
||||||
|
@ -691,7 +688,7 @@ void TagReader::SetUnsyncLyricsFrame(const std::string &value, TagLib::ID3v2::Ta
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
QByteArray TagReaderTagLib::LoadEmbeddedArt(const QString &filename) const {
|
||||||
|
|
||||||
if (filename.isEmpty()) return QByteArray();
|
if (filename.isEmpty()) return QByteArray();
|
||||||
|
|
||||||
|
@ -792,7 +789,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const {
|
QByteArray TagReaderTagLib::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const {
|
||||||
|
|
||||||
QByteArray ret;
|
QByteArray ret;
|
||||||
|
|
||||||
|
@ -810,7 +807,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReader::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
|
bool TagReaderTagLib::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
|
||||||
|
|
||||||
if (filename.isEmpty()) return false;
|
if (filename.isEmpty()) return false;
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TAGREADER_H
|
#ifndef TAGREADERTAGLIB_H
|
||||||
#define TAGREADER_H
|
#define TAGREADERTAGLIB_H
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
@ -33,29 +33,31 @@
|
||||||
#include <taglib/apefile.h>
|
#include <taglib/apefile.h>
|
||||||
#include <taglib/id3v2tag.h>
|
#include <taglib/id3v2tag.h>
|
||||||
|
|
||||||
|
#include "tagreaderbase.h"
|
||||||
#include "tagreadermessages.pb.h"
|
#include "tagreadermessages.pb.h"
|
||||||
|
|
||||||
class FileRefFactory;
|
class FileRefFactory;
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* This class holds all useful methods to read and write tags from/to files.
|
* This class holds all useful methods to read and write tags from/to files.
|
||||||
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
|
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
|
||||||
*/
|
*/
|
||||||
class TagReader {
|
class TagReaderTagLib : public TagReaderBase {
|
||||||
public:
|
public:
|
||||||
explicit TagReader();
|
explicit TagReaderTagLib();
|
||||||
~TagReader();
|
~TagReaderTagLib();
|
||||||
|
|
||||||
bool IsMediaFile(const QString &filename) const;
|
bool IsMediaFile(const QString &filename) const override;
|
||||||
|
|
||||||
|
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||||
|
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||||
|
|
||||||
|
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
||||||
|
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) override;
|
||||||
|
|
||||||
|
private:
|
||||||
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
spb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||||
|
|
||||||
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const;
|
|
||||||
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const;
|
|
||||||
|
|
||||||
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
|
||||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
|
||||||
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data);
|
|
||||||
|
|
||||||
static void Decode(const TagLib::String &tag, std::string *output);
|
static void Decode(const TagLib::String &tag, std::string *output);
|
||||||
static void Decode(const QString &tag, std::string *output);
|
static void Decode(const QString &tag, std::string *output);
|
||||||
|
|
||||||
|
@ -69,10 +71,10 @@ class TagReader {
|
||||||
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
void SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
|
void SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
|
||||||
|
|
||||||
|
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FileRefFactory *factory_;
|
FileRefFactory *factory_;
|
||||||
|
|
||||||
const std::string kEmbeddedCover;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TAGREADER_H
|
#endif // TAGREADERTAGLIB_H
|
|
@ -0,0 +1,413 @@
|
||||||
|
/* This file is part of Strawberry.
|
||||||
|
Copyright 2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
|
||||||
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "tagreadertagparser.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <tagparser/mediafileinfo.h>
|
||||||
|
#include <tagparser/diagnostics.h>
|
||||||
|
#include <tagparser/progressfeedback.h>
|
||||||
|
#include <tagparser/tag.h>
|
||||||
|
#include <tagparser/abstracttrack.h>
|
||||||
|
#include <tagparser/abstractattachment.h>
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/messagehandler.h"
|
||||||
|
#include "core/timeconstants.h"
|
||||||
|
|
||||||
|
TagReaderTagParser::TagReaderTagParser() {}
|
||||||
|
|
||||||
|
TagReaderTagParser::~TagReaderTagParser() {}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
try {
|
||||||
|
TagParser::MediaFileInfo taginfo;
|
||||||
|
TagParser::Diagnostics diag;
|
||||||
|
TagParser::AbortableProgressFeedback progress;
|
||||||
|
|
||||||
|
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||||
|
taginfo.open();
|
||||||
|
|
||||||
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
taginfo.parseTracks(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const TagParser::DiagMessage &msg : diag) {
|
||||||
|
qLog(Debug) << QString::fromStdString(msg.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto tracks = taginfo.tracks();
|
||||||
|
for (const auto track : tracks) {
|
||||||
|
if (track->mediaType() == TagParser::MediaType::Audio) return true;
|
||||||
|
}
|
||||||
|
taginfo.close();
|
||||||
|
}
|
||||||
|
catch(...) {}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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;
|
||||||
|
|
||||||
|
const QByteArray url(QUrl::fromLocalFile(filename).toEncoded());
|
||||||
|
|
||||||
|
song->set_basefilename(DataCommaSizeFromQString(fileinfo.fileName()));
|
||||||
|
song->set_url(url.constData(), url.size());
|
||||||
|
song->set_filesize(fileinfo.size());
|
||||||
|
song->set_mtime(fileinfo.lastModified().toSecsSinceEpoch());
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
|
||||||
|
song->set_ctime(fileinfo.birthTime().isValid() ? fileinfo.birthTime().toSecsSinceEpoch() : fileinfo.lastModified().toSecsSinceEpoch());
|
||||||
|
#else
|
||||||
|
song->set_ctime(fileinfo.created().toSecsSinceEpoch());
|
||||||
|
#endif
|
||||||
|
song->set_lastseen(QDateTime::currentDateTime().toSecsSinceEpoch());
|
||||||
|
|
||||||
|
try {
|
||||||
|
TagParser::MediaFileInfo taginfo;
|
||||||
|
TagParser::Diagnostics diag;
|
||||||
|
TagParser::AbortableProgressFeedback progress;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
taginfo.setPath(filename.toStdWString().toStdString());
|
||||||
|
#else
|
||||||
|
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
taginfo.open();
|
||||||
|
|
||||||
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
taginfo.parseTracks(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
taginfo.parseTags(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const TagParser::DiagMessage &msg : diag) {
|
||||||
|
qLog(Debug) << QString::fromStdString(msg.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto tracks = taginfo.tracks();
|
||||||
|
for (const auto track : tracks) {
|
||||||
|
switch(track->format().general) {
|
||||||
|
case TagParser::GeneralMediaFormat::Flac:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_FLAC);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::WavPack:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_WAVPACK);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::MonkeysAudio:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_APE);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::WindowsMediaAudio:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_ASF);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::Vorbis:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGVORBIS);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::Opus:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGOPUS);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::Speex:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_OGGSPEEX);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::Mpeg1Audio:
|
||||||
|
switch(track->format().sub) {
|
||||||
|
case TagParser::SubFormats::Mpeg1Layer3:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_MPEG);
|
||||||
|
break;
|
||||||
|
case TagParser::SubFormats::None:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::Mpc:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_MPC);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::Pcm:
|
||||||
|
song->set_filetype(spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_PCM);
|
||||||
|
break;
|
||||||
|
case TagParser::GeneralMediaFormat::Unknown:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
song->set_length_nanosec(track->duration().totalMilliseconds() * kNsecPerMsec);
|
||||||
|
song->set_samplerate(track->samplingFrequency());
|
||||||
|
song->set_bitdepth(track->bitsPerSample());
|
||||||
|
song->set_bitrate(std::max(track->bitrate(), track->maxBitrate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (song->filetype() == spb::tagreader::SongMetadata_FileType::SongMetadata_FileType_UNKNOWN) {
|
||||||
|
taginfo.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto tag : taginfo.tags()) {
|
||||||
|
song->set_albumartist(tag->value(TagParser::KnownField::AlbumArtist).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_artist(tag->value(TagParser::KnownField::Artist).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_album(tag->value(TagParser::KnownField::Album).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_title(tag->value(TagParser::KnownField::Title).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_genre(tag->value(TagParser::KnownField::Genre).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_composer(tag->value(TagParser::KnownField::Composer).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_performer(tag->value(TagParser::KnownField::Performers).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_grouping(tag->value(TagParser::KnownField::Grouping).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_comment(tag->value(TagParser::KnownField::Comment).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_lyrics(tag->value(TagParser::KnownField::Lyrics).toString(TagParser::TagTextEncoding::Utf8));
|
||||||
|
song->set_year(tag->value(TagParser::KnownField::ReleaseDate).toInteger());
|
||||||
|
song->set_originalyear(tag->value(TagParser::KnownField::RecordDate).toInteger());
|
||||||
|
song->set_track(tag->value(TagParser::KnownField::TrackPosition).toInteger());
|
||||||
|
song->set_disc(tag->value(TagParser::KnownField::DiskPosition).toInteger());
|
||||||
|
if (!tag->value(TagParser::KnownField::Cover).empty() && tag->value(TagParser::KnownField::Cover).dataSize() > 0) {
|
||||||
|
song->set_art_automatic(kEmbeddedCover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set integer fields to -1 if they're not valid
|
||||||
|
#define SetDefault(field) if (song->field() <= 0) { song->set_##field(-1); }
|
||||||
|
SetDefault(track);
|
||||||
|
SetDefault(disc);
|
||||||
|
SetDefault(year);
|
||||||
|
SetDefault(originalyear);
|
||||||
|
SetDefault(bitrate);
|
||||||
|
SetDefault(samplerate);
|
||||||
|
SetDefault(bitdepth);
|
||||||
|
SetDefault(lastplayed);
|
||||||
|
#undef SetDefault
|
||||||
|
|
||||||
|
song->set_valid(true);
|
||||||
|
|
||||||
|
taginfo.close();
|
||||||
|
}
|
||||||
|
catch(...) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TagReaderTagParser::SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const {
|
||||||
|
|
||||||
|
if (filename.isEmpty()) return false;
|
||||||
|
|
||||||
|
qLog(Debug) << "Saving tags to" << filename;
|
||||||
|
|
||||||
|
try {
|
||||||
|
TagParser::MediaFileInfo taginfo;
|
||||||
|
TagParser::Diagnostics diag;
|
||||||
|
TagParser::AbortableProgressFeedback progress;
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
taginfo.setPath(filename.toStdWString().toStdString());
|
||||||
|
#else
|
||||||
|
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||||
|
#endif
|
||||||
|
taginfo.open();
|
||||||
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
taginfo.parseTags(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (taginfo.tags().size() <= 0) {
|
||||||
|
taginfo.createAppropriateTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto tag : taginfo.tags()) {
|
||||||
|
tag->setValue(TagParser::KnownField::AlbumArtist, TagParser::TagValue(song.albumartist(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Artist, TagParser::TagValue(song.artist(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Album, TagParser::TagValue(song.album(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Title, TagParser::TagValue(song.title(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Genre, TagParser::TagValue(song.genre(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Composer, TagParser::TagValue(song.composer(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Performers, TagParser::TagValue(song.performer(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Grouping, TagParser::TagValue(song.grouping(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Comment, TagParser::TagValue(song.comment(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::Lyrics, TagParser::TagValue(song.lyrics(), TagParser::TagTextEncoding::Utf8, tag->proposedTextEncoding()));
|
||||||
|
tag->setValue(TagParser::KnownField::TrackPosition, TagParser::TagValue(song.track()));
|
||||||
|
tag->setValue(TagParser::KnownField::DiskPosition, TagParser::TagValue(song.disc()));
|
||||||
|
}
|
||||||
|
taginfo.applyChanges(diag, progress);
|
||||||
|
taginfo.close();
|
||||||
|
|
||||||
|
for (const TagParser::DiagMessage &msg : diag) {
|
||||||
|
qLog(Debug) << QString::fromStdString(msg.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(...) {}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray TagReaderTagParser::LoadEmbeddedArt(const QString &filename) const {
|
||||||
|
|
||||||
|
if (filename.isEmpty()) return QByteArray();
|
||||||
|
|
||||||
|
qLog(Debug) << "Loading art from" << filename;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
TagParser::MediaFileInfo taginfo;
|
||||||
|
TagParser::Diagnostics diag;
|
||||||
|
TagParser::AbortableProgressFeedback progress;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
taginfo.setPath(filename.toStdWString().toStdString());
|
||||||
|
#else
|
||||||
|
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
taginfo.open();
|
||||||
|
|
||||||
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
taginfo.parseTags(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto 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());
|
||||||
|
taginfo.close();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
taginfo.close();
|
||||||
|
|
||||||
|
for (const TagParser::DiagMessage &msg : diag) {
|
||||||
|
qLog(Debug) << QString::fromStdString(msg.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(...) {}
|
||||||
|
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TagReaderTagParser::SaveEmbeddedArt(const QString &filename, const QByteArray &data) {
|
||||||
|
|
||||||
|
if (filename.isEmpty()) return false;
|
||||||
|
|
||||||
|
qLog(Debug) << "Saving art to" << filename;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
TagParser::MediaFileInfo taginfo;
|
||||||
|
TagParser::Diagnostics diag;
|
||||||
|
TagParser::AbortableProgressFeedback progress;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
taginfo.setPath(filename.toStdWString().toStdString());
|
||||||
|
#else
|
||||||
|
taginfo.setPath(QFile::encodeName(filename).toStdString());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
taginfo.open();
|
||||||
|
|
||||||
|
taginfo.parseContainerFormat(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
taginfo.parseTags(diag, progress);
|
||||||
|
if (progress.isAborted()) {
|
||||||
|
taginfo.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (taginfo.tags().size() <= 0) {
|
||||||
|
taginfo.createAppropriateTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto tag : taginfo.tags()) {
|
||||||
|
tag->setValue(TagParser::KnownField::Cover, TagParser::TagValue(data.toStdString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
taginfo.applyChanges(diag, progress);
|
||||||
|
taginfo.close();
|
||||||
|
|
||||||
|
for (const TagParser::DiagMessage &msg : diag) {
|
||||||
|
qLog(Debug) << QString::fromStdString(msg.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(...) {}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* This file is part of Strawberry.
|
||||||
|
Copyright 2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||||
|
|
||||||
|
Strawberry is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Strawberry is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TAGREADERTAGPARSER_H
|
||||||
|
#define TAGREADERTAGPARSER_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "tagreadermessages.pb.h"
|
||||||
|
#include "tagreaderbase.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This class holds all useful methods to read and write tags from/to files.
|
||||||
|
* You should not use it directly in the main process but rather use a TagReaderWorker process (using TagReaderClient)
|
||||||
|
*/
|
||||||
|
class TagReaderTagParser : public TagReaderBase {
|
||||||
|
public:
|
||||||
|
explicit TagReaderTagParser();
|
||||||
|
~TagReaderTagParser();
|
||||||
|
|
||||||
|
bool IsMediaFile(const QString &filename) const override;
|
||||||
|
|
||||||
|
void ReadFile(const QString &filename, spb::tagreader::SongMetadata *song) const override;
|
||||||
|
bool SaveFile(const QString &filename, const spb::tagreader::SongMetadata &song) const override;
|
||||||
|
|
||||||
|
QByteArray LoadEmbeddedArt(const QString &filename) const override;
|
||||||
|
bool SaveEmbeddedArt(const QString &filename, const QByteArray &data) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TAGREADERTAGPARSER_H
|
|
@ -11,17 +11,21 @@ else()
|
||||||
qt5_wrap_cpp(MOC ${HEADERS})
|
qt5_wrap_cpp(MOC ${HEADERS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
link_directories(
|
link_directories(${GLIB_LIBRARY_DIRS})
|
||||||
${GLIB_LIBRARY_DIRS}
|
|
||||||
${TAGLIB_LIBRARY_DIRS}
|
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||||
)
|
link_directories(${TAGLIB_LIBRARY_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_TAGPARSER AND TAGPARSER_FOUND)
|
||||||
|
link_directories(${TAGPARSER_LIBRARY_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
add_executable(strawberry-tagreader ${SOURCES} ${MOC} ${QRC})
|
||||||
|
|
||||||
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
|
target_include_directories(strawberry-tagreader SYSTEM PRIVATE
|
||||||
${GLIB_INCLUDE_DIRS}
|
${GLIB_INCLUDE_DIRS}
|
||||||
${PROTOBUF_INCLUDE_DIRS}
|
${PROTOBUF_INCLUDE_DIRS}
|
||||||
${TAGLIB_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(strawberry-tagreader PRIVATE
|
target_include_directories(strawberry-tagreader PRIVATE
|
||||||
|
@ -33,13 +37,22 @@ target_include_directories(strawberry-tagreader PRIVATE
|
||||||
|
|
||||||
target_link_libraries(strawberry-tagreader PRIVATE
|
target_link_libraries(strawberry-tagreader PRIVATE
|
||||||
${GLIB_LIBRARIES}
|
${GLIB_LIBRARIES}
|
||||||
${TAGLIB_LIBRARIES}
|
|
||||||
${QtCore_LIBRARIES}
|
${QtCore_LIBRARIES}
|
||||||
${QtNetwork_LIBRARIES}
|
${QtNetwork_LIBRARIES}
|
||||||
libstrawberry-common
|
libstrawberry-common
|
||||||
libstrawberry-tagreader
|
libstrawberry-tagreader
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(USE_TAGLIB AND TAGLIB_FOUND)
|
||||||
|
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)
|
||||||
|
target_include_directories(strawberry-tagreader SYSTEM PRIVATE ${TAGPARSER_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(strawberry-tagreader PRIVATE ${TAGPARSER_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||||
target_link_libraries(strawberry-tagreader PRIVATE execinfo)
|
target_link_libraries(strawberry-tagreader PRIVATE execinfo)
|
||||||
endif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
endif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||||
|
|
|
@ -24,7 +24,11 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "core/messagehandler.h"
|
#include "core/messagehandler.h"
|
||||||
#include "tagreader.h"
|
#if defined(USE_TAGLIB)
|
||||||
|
# include "tagreadertaglib.h"
|
||||||
|
#elif defined(USE_TAGPARSER)
|
||||||
|
# include "tagreadertagparser.h"
|
||||||
|
#endif
|
||||||
#include "tagreadermessages.pb.h"
|
#include "tagreadermessages.pb.h"
|
||||||
|
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
|
@ -40,7 +44,11 @@ class TagReaderWorker : public AbstractMessageHandler<spb::tagreader::Message> {
|
||||||
void DeviceClosed() override;
|
void DeviceClosed() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TagReader tag_reader_;
|
#if defined(USE_TAGLIB)
|
||||||
|
TagReaderTagLib tag_reader_;
|
||||||
|
#elif defined(USE_TAGPARSER)
|
||||||
|
TagReaderTagParser tag_reader_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TAGREADERWORKER_H
|
#endif // TAGREADERWORKER_H
|
||||||
|
|
|
@ -49,4 +49,7 @@
|
||||||
|
|
||||||
#cmakedefine ENABLE_WIN32_CONSOLE
|
#cmakedefine ENABLE_WIN32_CONSOLE
|
||||||
|
|
||||||
|
#cmakedefine USE_TAGLIB
|
||||||
|
#cmakedefine USE_TAGPARSER
|
||||||
|
|
||||||
#endif // CONFIG_H_IN
|
#endif // CONFIG_H_IN
|
||||||
|
|
Loading…
Reference in New Issue