@@ -353,6 +353,8 @@ struct Song::Private : public QSharedData {
|
||||
std::optional<double> ebur128_integrated_loudness_lufs_;
|
||||
std::optional<double> ebur128_loudness_range_lu_;
|
||||
|
||||
int id3v2_version_; // ID3v2 tag version (3 or 4), 0 if not applicable or unknown
|
||||
|
||||
bool init_from_file_; // Whether this song was loaded from a file using taglib.
|
||||
bool suspicious_tags_; // Whether our encoding guesser thinks these tags might be incorrectly encoded.
|
||||
|
||||
@@ -400,6 +402,8 @@ Song::Private::Private(const Source source)
|
||||
rating_(-1),
|
||||
bpm_(-1),
|
||||
|
||||
id3v2_version_(0),
|
||||
|
||||
init_from_file_(false),
|
||||
suspicious_tags_(false)
|
||||
|
||||
@@ -510,6 +514,8 @@ const QString &Song::musicbrainz_work_id() const { return d->musicbrainz_work_id
|
||||
std::optional<double> Song::ebur128_integrated_loudness_lufs() const { return d->ebur128_integrated_loudness_lufs_; }
|
||||
std::optional<double> Song::ebur128_loudness_range_lu() const { return d->ebur128_loudness_range_lu_; }
|
||||
|
||||
int Song::id3v2_version() const { return d->id3v2_version_; }
|
||||
|
||||
QString *Song::mutable_title() { return &d->title_; }
|
||||
QString *Song::mutable_album() { return &d->album_; }
|
||||
QString *Song::mutable_artist() { return &d->artist_; }
|
||||
@@ -624,6 +630,8 @@ void Song::set_musicbrainz_work_id(const QString &v) { d->musicbrainz_work_id_ =
|
||||
void Song::set_ebur128_integrated_loudness_lufs(const std::optional<double> v) { d->ebur128_integrated_loudness_lufs_ = v; }
|
||||
void Song::set_ebur128_loudness_range_lu(const std::optional<double> v) { d->ebur128_loudness_range_lu_ = v; }
|
||||
|
||||
void Song::set_id3v2_version(const int v) { d->id3v2_version_ = v; }
|
||||
|
||||
void Song::set_init_from_file(const bool v) { d->init_from_file_ = v; }
|
||||
|
||||
void Song::set_stream_url(const QUrl &v) { d->stream_url_ = v; }
|
||||
@@ -833,6 +841,10 @@ bool Song::save_embedded_cover_supported(const FileType filetype) {
|
||||
|
||||
}
|
||||
|
||||
bool Song::id3v2_tags_supported() const {
|
||||
return d->filetype_ == FileType::MPEG || d->filetype_ == FileType::WAV || d->filetype_ == FileType::AIFF;
|
||||
}
|
||||
|
||||
int Song::ColumnIndex(const QString &field) {
|
||||
|
||||
return static_cast<int>(kRowIdColumns.indexOf(field));
|
||||
|
||||
@@ -234,6 +234,8 @@ class Song {
|
||||
std::optional<double> ebur128_integrated_loudness_lufs() const;
|
||||
std::optional<double> ebur128_loudness_range_lu() const;
|
||||
|
||||
int id3v2_version() const;
|
||||
|
||||
QString *mutable_title();
|
||||
QString *mutable_album();
|
||||
QString *mutable_artist();
|
||||
@@ -349,6 +351,8 @@ class Song {
|
||||
void set_ebur128_integrated_loudness_lufs(const std::optional<double> v);
|
||||
void set_ebur128_loudness_range_lu(const std::optional<double> v);
|
||||
|
||||
void set_id3v2_version(const int v);
|
||||
|
||||
void set_init_from_file(const bool v);
|
||||
|
||||
void set_stream_url(const QUrl &v);
|
||||
@@ -439,6 +443,8 @@ class Song {
|
||||
static bool save_embedded_cover_supported(const FileType filetype);
|
||||
bool save_embedded_cover_supported() const { return url().isLocalFile() && save_embedded_cover_supported(filetype()) && !has_cue(); };
|
||||
|
||||
bool id3v2_tags_supported() const;
|
||||
|
||||
static int ColumnIndex(const QString &field);
|
||||
static QString JoinSpec(const QString &table);
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#include "utilities/coverutils.h"
|
||||
#include "utilities/coveroptions.h"
|
||||
#include "tagreader/tagreaderclient.h"
|
||||
#include "tagreader/tagid3v2version.h"
|
||||
#include "widgets/busyindicator.h"
|
||||
#include "widgets/lineedit.h"
|
||||
#include "collection/collectionbackend.h"
|
||||
@@ -107,6 +108,12 @@ using namespace Qt::Literals::StringLiterals;
|
||||
namespace {
|
||||
constexpr char kSettingsGroup[] = "EditTagDialog";
|
||||
constexpr int kSmallImageSize = 128;
|
||||
|
||||
// ID3v2 version constants
|
||||
constexpr int kID3v2_Version_3 = 3;
|
||||
constexpr int kID3v2_Version_4 = 4;
|
||||
constexpr int kComboBoxIndex_ID3v2_3 = 0;
|
||||
constexpr int kComboBoxIndex_ID3v2_4 = 1;
|
||||
} // namespace
|
||||
|
||||
const char EditTagDialog::kTagsDifferentHintText[] = QT_TR_NOOP("(different across multiple songs)");
|
||||
@@ -708,6 +715,9 @@ void EditTagDialog::SelectionChanged() {
|
||||
bool titlesort_enabled = false;
|
||||
bool artistsort_enabled = false;
|
||||
bool albumsort_enabled = false;
|
||||
bool has_id3v2_support = false;
|
||||
int id3v2_version = 0;
|
||||
bool id3v2_version_different = false;
|
||||
for (const QModelIndex &idx : indexes) {
|
||||
if (data_.value(idx.row()).cover_action_ == UpdateCoverAction::None) {
|
||||
data_[idx.row()].cover_result_ = AlbumCoverImageResult();
|
||||
@@ -769,6 +779,15 @@ void EditTagDialog::SelectionChanged() {
|
||||
if (song.albumsort_supported()) {
|
||||
albumsort_enabled = true;
|
||||
}
|
||||
if (song.id3v2_tags_supported()) {
|
||||
has_id3v2_support = true;
|
||||
if (id3v2_version == 0) {
|
||||
id3v2_version = song.id3v2_version();
|
||||
}
|
||||
else if (id3v2_version != song.id3v2_version()) {
|
||||
id3v2_version_different = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString summary;
|
||||
@@ -840,6 +859,23 @@ void EditTagDialog::SelectionChanged() {
|
||||
ui_->artistsort->setEnabled(artistsort_enabled);
|
||||
ui_->albumsort->setEnabled(albumsort_enabled);
|
||||
|
||||
ui_->label_id3v2_version->setVisible(has_id3v2_support);
|
||||
ui_->combobox_id3v2_version->setVisible(has_id3v2_support);
|
||||
|
||||
if (has_id3v2_support) {
|
||||
// Set default based on existing version(s)
|
||||
if (id3v2_version_different || id3v2_version == 0) {
|
||||
// Mixed versions or unknown - default to ID3v2.4
|
||||
ui_->combobox_id3v2_version->setCurrentIndex(kComboBoxIndex_ID3v2_4);
|
||||
}
|
||||
else if (id3v2_version == kID3v2_Version_3) {
|
||||
ui_->combobox_id3v2_version->setCurrentIndex(kComboBoxIndex_ID3v2_3);
|
||||
}
|
||||
else {
|
||||
ui_->combobox_id3v2_version->setCurrentIndex(kComboBoxIndex_ID3v2_4);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EditTagDialog::UpdateUI(const QModelIndexList &indexes) {
|
||||
@@ -1371,6 +1407,13 @@ void EditTagDialog::SaveData() {
|
||||
}
|
||||
save_tag_cover_data.cover_data = ref.cover_result_.image_data;
|
||||
}
|
||||
|
||||
// Determine ID3v2 version based on user selection
|
||||
TagID3v2Version tag_id3v2_version = TagID3v2Version::Default;
|
||||
if (ref.current_.filetype() == Song::FileType::MPEG || ref.current_.filetype() == Song::FileType::WAV || ref.current_.filetype() == Song::FileType::AIFF) {
|
||||
tag_id3v2_version = ui_->combobox_id3v2_version->currentIndex() == kComboBoxIndex_ID3v2_3 ? TagID3v2Version::V3 : TagID3v2Version::V4;
|
||||
}
|
||||
|
||||
TagReaderClient::SaveOptions save_tags_options;
|
||||
if (save_tags) {
|
||||
save_tags_options |= TagReaderClient::SaveOption::Tags;
|
||||
@@ -1384,7 +1427,7 @@ void EditTagDialog::SaveData() {
|
||||
if (save_embedded_cover) {
|
||||
save_tags_options |= TagReaderClient::SaveOption::Cover;
|
||||
}
|
||||
TagReaderReplyPtr reply = tagreader_client_->WriteFileAsync(ref.current_.url().toLocalFile(), ref.current_, save_tags_options, save_tag_cover_data);
|
||||
TagReaderReplyPtr reply = tagreader_client_->WriteFileAsync(ref.current_.url().toLocalFile(), ref.current_, save_tags_options, save_tag_cover_data, tag_id3v2_version);
|
||||
SharedPtr<QMetaObject::Connection> connection = make_shared<QMetaObject::Connection>();
|
||||
*connection = QObject::connect(&*reply, &TagReaderReply::Finished, this, [this, reply, ref, connection]() {
|
||||
SongSaveTagsComplete(reply, ref.current_.url().toLocalFile(), ref.current_, ref.cover_action_);
|
||||
|
||||
@@ -650,6 +650,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="layout_id3v2_version">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_id3v2_version">
|
||||
<property name="text">
|
||||
<string>ID3v2 version:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combobox_id3v2_version">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2.3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2.4</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_id3v2_version">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_albumart_bottom">
|
||||
<property name="orientation">
|
||||
|
||||
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