29
src/tagreader/tagid3v2version.h
Normal file
29
src/tagreader/tagid3v2version.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2025, 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 TAGID3V2VERSION_H
|
||||
#define TAGID3V2VERSION_H
|
||||
|
||||
enum class TagID3v2Version {
|
||||
Default = 0, // Use existing version or library default
|
||||
V3 = 3,
|
||||
V4 = 4
|
||||
};
|
||||
|
||||
#endif // TAGID3V2VERSION_H
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "savetagsoptions.h"
|
||||
#include "savetagcoverdata.h"
|
||||
#include "albumcovertagdata.h"
|
||||
#include "tagid3v2version.h"
|
||||
|
||||
class TagReaderBase {
|
||||
public:
|
||||
@@ -45,7 +46,7 @@ class TagReaderBase {
|
||||
virtual TagReaderResult ReadStream(const QUrl &url, const QString &filename, const quint64 size, const quint64 mtime, const QString &token_type, const QString &access_token, Song *song) const = 0;
|
||||
#endif
|
||||
|
||||
virtual TagReaderResult WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data) const = 0;
|
||||
virtual TagReaderResult WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data, const TagID3v2Version id3v2_version) const = 0;
|
||||
|
||||
virtual TagReaderResult LoadEmbeddedCover(const QString &filename, QByteArray &data) const = 0;
|
||||
virtual TagReaderResult SaveEmbeddedCover(const QString &filename, const SaveTagCoverData &save_tag_cover_data) const = 0;
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "tagreaderreadstreamreply.h"
|
||||
#include "tagreaderloadcoverdatareply.h"
|
||||
#include "tagreaderloadcoverimagereply.h"
|
||||
#include "tagid3v2version.h"
|
||||
|
||||
using std::dynamic_pointer_cast;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
@@ -189,7 +190,7 @@ void TagReaderClient::ProcessRequest(TagReaderRequestPtr request) {
|
||||
}
|
||||
#endif // HAVE_STREAMTAGREADER
|
||||
else if (TagReaderWriteFileRequestPtr write_file_request = dynamic_pointer_cast<TagReaderWriteFileRequest>(request)) {
|
||||
result = WriteFileBlocking(write_file_request->filename, write_file_request->song, write_file_request->save_tags_options, write_file_request->save_tag_cover_data);
|
||||
result = WriteFileBlocking(write_file_request->filename, write_file_request->song, write_file_request->save_tags_options, write_file_request->save_tag_cover_data, write_file_request->tag_id3v2_version);
|
||||
}
|
||||
else if (TagReaderLoadCoverDataRequestPtr load_cover_data_request = dynamic_pointer_cast<TagReaderLoadCoverDataRequest>(request)) {
|
||||
QByteArray cover_data;
|
||||
@@ -303,13 +304,13 @@ TagReaderReadStreamReplyPtr TagReaderClient::ReadStreamAsync(const QUrl &url, co
|
||||
}
|
||||
#endif // HAVE_STREAMTAGREADER
|
||||
|
||||
TagReaderResult TagReaderClient::WriteFileBlocking(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data) {
|
||||
TagReaderResult TagReaderClient::WriteFileBlocking(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data, const TagID3v2Version tag_id3v2_version) {
|
||||
|
||||
return tagreader_.WriteFile(filename, song, save_tags_options, save_tag_cover_data);
|
||||
return tagreader_.WriteFile(filename, song, save_tags_options, save_tag_cover_data, tag_id3v2_version);
|
||||
|
||||
}
|
||||
|
||||
TagReaderReplyPtr TagReaderClient::WriteFileAsync(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data) {
|
||||
TagReaderReplyPtr TagReaderClient::WriteFileAsync(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data, const TagID3v2Version tag_id3v2_version) {
|
||||
|
||||
Q_ASSERT(QThread::currentThread() != thread());
|
||||
|
||||
@@ -321,6 +322,7 @@ TagReaderReplyPtr TagReaderClient::WriteFileAsync(const QString &filename, const
|
||||
request->song = song;
|
||||
request->save_tags_options = save_tags_options;
|
||||
request->save_tag_cover_data = save_tag_cover_data;
|
||||
request->tag_id3v2_version = tag_id3v2_version;
|
||||
|
||||
EnqueueRequest(request);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "tagreaderloadcoverimagereply.h"
|
||||
#include "savetagsoptions.h"
|
||||
#include "savetagcoverdata.h"
|
||||
#include "tagid3v2version.h"
|
||||
|
||||
class QThread;
|
||||
class Song;
|
||||
@@ -72,8 +73,8 @@ class TagReaderClient : public QObject {
|
||||
[[nodiscard]] TagReaderReadStreamReplyPtr ReadStreamAsync(const QUrl &url, const QString &filename, const quint64 size, const quint64 mtime, const QString &token_type, const QString &access_token);
|
||||
#endif
|
||||
|
||||
TagReaderResult WriteFileBlocking(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options = SaveTagsOption::Tags, const SaveTagCoverData &save_tag_cover_data = SaveTagCoverData());
|
||||
[[nodiscard]] TagReaderReplyPtr WriteFileAsync(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options = SaveTagsOption::Tags, const SaveTagCoverData &save_tag_cover_data = SaveTagCoverData());
|
||||
TagReaderResult WriteFileBlocking(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options = SaveTagsOption::Tags, const SaveTagCoverData &save_tag_cover_data = SaveTagCoverData(), const TagID3v2Version tag_id3v2_version = TagID3v2Version::Default);
|
||||
[[nodiscard]] TagReaderReplyPtr WriteFileAsync(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options = SaveTagsOption::Tags, const SaveTagCoverData &save_tag_cover_data = SaveTagCoverData(), const TagID3v2Version tag_id3v2_version = TagID3v2Version::Default);
|
||||
|
||||
TagReaderResult LoadCoverDataBlocking(const QString &filename, QByteArray &data);
|
||||
TagReaderResult LoadCoverImageBlocking(const QString &filename, QImage &image);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "core/logging.h"
|
||||
#include "tagreaderbase.h"
|
||||
#include "tagreadertaglib.h"
|
||||
#include "tagid3v2version.h"
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
@@ -317,12 +318,13 @@ TagReaderResult TagReaderGME::ReadStream(const QUrl &url, const QString &filenam
|
||||
}
|
||||
#endif
|
||||
|
||||
TagReaderResult TagReaderGME::WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data) const {
|
||||
TagReaderResult TagReaderGME::WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data, const TagID3v2Version id3v2_version) const {
|
||||
|
||||
Q_UNUSED(filename);
|
||||
Q_UNUSED(song);
|
||||
Q_UNUSED(save_tags_options);
|
||||
Q_UNUSED(save_tag_cover_data);
|
||||
Q_UNUSED(id3v2_version);
|
||||
|
||||
return TagReaderResult::ErrorCode::Unsupported;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "tagreaderbase.h"
|
||||
#include "tagid3v2version.h"
|
||||
|
||||
namespace GME {
|
||||
bool IsSupportedFormat(const QFileInfo &fileinfo);
|
||||
@@ -107,7 +108,7 @@ class TagReaderGME : public TagReaderBase {
|
||||
TagReaderResult ReadStream(const QUrl &url, const QString &filename, const quint64 size, const quint64 mtime, const QString &token_type, const QString &access_token, Song *song) const override;
|
||||
#endif
|
||||
|
||||
TagReaderResult WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data) const override;
|
||||
TagReaderResult WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data, const TagID3v2Version id3v2_version) const override;
|
||||
|
||||
TagReaderResult LoadEmbeddedCover(const QString &filename, QByteArray &data) const override;
|
||||
TagReaderResult SaveEmbeddedCover(const QString &filename, const SaveTagCoverData &save_tag_cover_data) const override;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <taglib/apeproperties.h>
|
||||
#include <taglib/id3v2tag.h>
|
||||
#include <taglib/id3v2frame.h>
|
||||
#include <taglib/id3v2header.h>
|
||||
#include <taglib/attachedpictureframe.h>
|
||||
#include <taglib/textidentificationframe.h>
|
||||
#include <taglib/unsynchronizedlyricsframe.h>
|
||||
@@ -104,6 +105,7 @@
|
||||
#include "constants/timeconstants.h"
|
||||
|
||||
#include "albumcovertagdata.h"
|
||||
#include "tagid3v2version.h"
|
||||
|
||||
using std::make_unique;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
@@ -149,6 +151,10 @@ constexpr char kID3v2_MusicBrainz_DiscId[] = "MusicBrainz Disc Id";
|
||||
constexpr char kID3v2_MusicBrainz_ReleaseGroupId[] = "MusicBrainz Release Group Id";
|
||||
constexpr char kID3v2_MusicBrainz_WorkId[] = "MusicBrainz Work Id";
|
||||
|
||||
// ID3v2 version constants
|
||||
constexpr int kID3v2_Version_3 = 3;
|
||||
constexpr int kID3v2_Version_4 = 4;
|
||||
|
||||
constexpr char kVorbisComment_AlbumArtist1[] = "ALBUMARTIST";
|
||||
constexpr char kVorbisComment_AlbumArtist2[] = "ALBUM ARTIST";
|
||||
constexpr char kVorbisComment_AlbumArtistSort[] = "ALBUMARTISTSORT";
|
||||
@@ -604,8 +610,14 @@ TagReaderResult TagReaderTagLib::ReadStream(const QUrl &url,
|
||||
|
||||
void TagReaderTagLib::ParseID3v2Tags(TagLib::ID3v2::Tag *tag, QString *disc, QString *compilation, Song *song) const {
|
||||
|
||||
if (!tag) return;
|
||||
|
||||
TagLib::ID3v2::FrameListMap map = tag->frameListMap();
|
||||
|
||||
if (tag->header()) {
|
||||
song->set_id3v2_version(tag->header()->majorVersion());
|
||||
}
|
||||
|
||||
if (map.contains(kID3v2_Disc)) *disc = TagLibStringToQString(map[kID3v2_Disc].front()->toString()).trimmed();
|
||||
if (map.contains(kID3v2_Composer)) song->set_composer(map[kID3v2_Composer].front()->toString());
|
||||
if (map.contains(kID3v2_ComposerSort)) song->set_composersort(map[kID3v2_ComposerSort].front()->toString());
|
||||
@@ -1042,7 +1054,7 @@ void TagReaderTagLib::ParseASFAttribute(const TagLib::ASF::AttributeListMap &att
|
||||
|
||||
}
|
||||
|
||||
TagReaderResult TagReaderTagLib::WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data) const {
|
||||
TagReaderResult TagReaderTagLib::WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data, const TagID3v2Version tag_id3v2_version) const {
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
return TagReaderResult::ErrorCode::FilenameMissing;
|
||||
@@ -1265,7 +1277,34 @@ TagReaderResult TagReaderTagLib::WriteFile(const QString &filename, const Song &
|
||||
}
|
||||
}
|
||||
|
||||
const bool success = fileref->save();
|
||||
// Determine ID3v2 version to use and convert to TagLib type
|
||||
TagLib::ID3v2::Version taglib_id3v2_version = TagLib::ID3v2::v4;
|
||||
if (tag_id3v2_version == TagID3v2Version::V3) {
|
||||
taglib_id3v2_version = TagLib::ID3v2::v3;
|
||||
}
|
||||
else if (tag_id3v2_version == TagID3v2Version::V4) {
|
||||
taglib_id3v2_version = TagLib::ID3v2::v4;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
// For MPEG files, use save with ID3v2 version parameter
|
||||
if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
success = file_mpeg->save(TagLib::MPEG::File::AllTags, TagLib::File::StripOthers, taglib_id3v2_version);
|
||||
}
|
||||
// For WAV files with ID3v2 tags
|
||||
else if (TagLib::RIFF::WAV::File *file_wav = dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) {
|
||||
success = file_wav->save(TagLib::RIFF::WAV::File::AllTags, TagLib::File::StripOthers, taglib_id3v2_version);
|
||||
}
|
||||
// For AIFF files with ID3v2 tags
|
||||
else if (TagLib::RIFF::AIFF::File *file_aiff = dynamic_cast<TagLib::RIFF::AIFF::File*>(fileref->file())) {
|
||||
success = file_aiff->save(taglib_id3v2_version);
|
||||
}
|
||||
// For all other file types, use default save
|
||||
else {
|
||||
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)
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
|
||||
#include "tagreaderbase.h"
|
||||
#include "savetagcoverdata.h"
|
||||
#include "tagid3v2version.h"
|
||||
|
||||
#undef TStringToQString
|
||||
#undef QStringToTString
|
||||
@@ -72,7 +73,7 @@ class TagReaderTagLib : public TagReaderBase {
|
||||
TagReaderResult ReadStream(const QUrl &url, const QString &filename, const quint64 size, const quint64 mtime, const QString &token_type, const QString &access_token, Song *song) const override;
|
||||
#endif
|
||||
|
||||
TagReaderResult WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data) const override;
|
||||
TagReaderResult WriteFile(const QString &filename, const Song &song, const SaveTagsOptions save_tags_options, const SaveTagCoverData &save_tag_cover_data, const TagID3v2Version tag_id3v2_version) const override;
|
||||
|
||||
TagReaderResult LoadEmbeddedCover(const QString &filename, QByteArray &data) const override;
|
||||
TagReaderResult SaveEmbeddedCover(const QString &filename, const SaveTagCoverData &save_tag_cover_data) const override;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "tagreaderrequest.h"
|
||||
#include "savetagsoptions.h"
|
||||
#include "savetagcoverdata.h"
|
||||
#include "tagid3v2version.h"
|
||||
|
||||
using std::make_shared;
|
||||
|
||||
@@ -39,6 +40,7 @@ class TagReaderWriteFileRequest : public TagReaderRequest {
|
||||
SaveTagsOptions save_tags_options;
|
||||
Song song;
|
||||
SaveTagCoverData save_tag_cover_data;
|
||||
TagID3v2Version tag_id3v2_version;
|
||||
};
|
||||
|
||||
using TagReaderWriteFileRequestPtr = SharedPtr<TagReaderWriteFileRequest>;
|
||||
|
||||
Reference in New Issue
Block a user