mirror of
https://github.com/strawberrymusicplayer/strawberry
synced 2025-01-29 00:30:01 +01:00
parent
b6d219e232
commit
4bccb1ab47
@ -138,6 +138,15 @@ TagReader::~TagReader() {
|
|||||||
delete factory_;
|
delete factory_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TagReader::IsMediaFile(const QString &filename) const {
|
||||||
|
|
||||||
|
qLog(Debug) << "Checking for valid file" << filename;
|
||||||
|
|
||||||
|
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||||
|
return !fileref->isNull() && fileref->tag();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
pb::tagreader::SongMetadata_FileType TagReader::GuessFileType(TagLib::FileRef *fileref) const {
|
||||||
|
|
||||||
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
|
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file())) return pb::tagreader::SongMetadata_FileType_WAV;
|
||||||
@ -289,7 +298,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
if (!map["APIC"].isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||||
|
|
||||||
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
// Find a suitable comment tag. For now we ignore iTunNORM comments.
|
||||||
for (uint i = 0; i < map["COMM"].size(); ++i) {
|
for (uint i = 0 ; i < map["COMM"].size() ; ++i) {
|
||||||
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
const TagLib::ID3v2::CommentsFrame *frame = dynamic_cast<const TagLib::ID3v2::CommentsFrame*>(map["COMM"][i]);
|
||||||
|
|
||||||
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
if (frame && TStringToQString(frame->description()) != "iTunNORM") {
|
||||||
@ -367,7 +376,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::MPC::File* file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
else if (TagLib::MPC::File *file_mpc = dynamic_cast<TagLib::MPC::File*>(fileref->file())) {
|
||||||
if (file_mpc->APETag()) {
|
if (file_mpc->APETag()) {
|
||||||
ParseAPETag(file_mpc->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
ParseAPETag(file_mpc->APETag()->itemListMap(), nullptr, &disc, &compilation, song);
|
||||||
}
|
}
|
||||||
@ -517,8 +526,8 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
|||||||
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);
|
||||||
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true);
|
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true);
|
||||||
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 -1 ? QString() : QString::number(song.disc())), true);
|
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||||
vorbis_comments->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true);
|
vorbis_comments->addField("COMPILATION", QStringToTaglibString(song.compilation() ? "1" : QString()), true);
|
||||||
|
|
||||||
// Try to be coherent, the two forms are used but the first one is preferred
|
// Try to be coherent, the two forms are used but the first one is preferred
|
||||||
|
|
||||||
@ -538,13 +547,16 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
|||||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
|
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
|
||||||
if (!fileref || fileref->isNull()) return false;
|
if (!fileref || fileref->isNull()) return false;
|
||||||
|
|
||||||
fileref->tag()->setTitle(StdStringToTaglibString(song.title()));
|
fileref->tag()->setTitle(song.title().empty() ? TagLib::String() : StdStringToTaglibString(song.title()));
|
||||||
fileref->tag()->setArtist(StdStringToTaglibString(song.artist()));
|
fileref->tag()->setArtist(song.artist().empty() ? TagLib::String() : StdStringToTaglibString(song.artist()));
|
||||||
fileref->tag()->setAlbum(StdStringToTaglibString(song.album()));
|
fileref->tag()->setAlbum(song.album().empty() ? TagLib::String() : StdStringToTaglibString(song.album()));
|
||||||
fileref->tag()->setGenre(StdStringToTaglibString(song.genre()));
|
fileref->tag()->setGenre(song.genre().empty() ? TagLib::String() : StdStringToTaglibString(song.genre()));
|
||||||
fileref->tag()->setComment(StdStringToTaglibString(song.comment()));
|
fileref->tag()->setComment(song.comment().empty() ? TagLib::String() : StdStringToTaglibString(song.comment()));
|
||||||
fileref->tag()->setYear(song.year());
|
fileref->tag()->setYear(song.year() <= 0 ? 0 : song.year());
|
||||||
fileref->tag()->setTrack(song.track());
|
fileref->tag()->setTrack(song.track() <= 0 ? 0 : song.track());
|
||||||
|
|
||||||
|
bool saved = false;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||||
TagLib::Ogg::XiphComment *tag = file->xiphComment();
|
TagLib::Ogg::XiphComment *tag = file->xiphComment();
|
||||||
@ -572,14 +584,16 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
|||||||
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
else if (TagLib::MPEG::File *file_mpeg = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||||
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
TagLib::ID3v2::Tag *tag = file_mpeg->ID3v2Tag(true);
|
||||||
if (!tag) return false;
|
if (!tag) return false;
|
||||||
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
|
SetTextFrame("TPOS", song.disc() <= 0 ? QString() : QString::number(song.disc()), tag);
|
||||||
SetTextFrame("TCOM", song.composer(), tag);
|
SetTextFrame("TCOM", song.composer().empty() ? std::string() : song.composer(), tag);
|
||||||
SetTextFrame("TIT1", song.grouping(), tag);
|
SetTextFrame("TIT1", song.grouping().empty() ? std::string() : song.grouping(), tag);
|
||||||
SetTextFrame("TOPE", song.performer(), tag);
|
SetTextFrame("TOPE", song.performer().empty() ? std::string() : song.performer(), tag);
|
||||||
// Skip TPE1 (which is the artist) here because we already set it
|
// Skip TPE1 (which is the artist) here because we already set it
|
||||||
SetTextFrame("TPE2", song.albumartist(), tag);
|
SetTextFrame("TPE2", song.albumartist().empty() ? std::string() : song.albumartist(), tag);
|
||||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
SetTextFrame("TCMP", song.compilation() ? QString::number(1) : QString(), tag);
|
||||||
SetUnsyncLyricsFrame(song.lyrics(), tag);
|
SetUnsyncLyricsFrame(song.lyrics().empty() ? std::string() : song.lyrics(), tag);
|
||||||
|
result = file_mpeg->save(TagLib::MPEG::File::ID3v2);
|
||||||
|
saved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (TagLib::MP4::File *file_mp4 = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
else if (TagLib::MP4::File *file_mp4 = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||||
@ -598,53 +612,29 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
|||||||
SetVorbisComments(tag, song);
|
SetVorbisComments(tag, song);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ret = fileref->save();
|
if (!saved) {
|
||||||
|
result = fileref->save();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
if (ret) {
|
if (result) {
|
||||||
// Linux: inotify doesn't seem to notice the change to the file unless we change the timestamps as well. (this is what touch does)
|
// 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);
|
utimensat(0, QFile::encodeName(filename).constData(), nullptr, 0);
|
||||||
}
|
}
|
||||||
#endif // Q_OS_LINUX
|
#endif // Q_OS_LINUX
|
||||||
|
|
||||||
return ret;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const {
|
void TagReader::SaveAPETag(TagLib::APE::Tag *tag, const pb::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->setItem("disc", TagLib::APE::Item("disc", TagLib::String::number(song.disc() <= 0 - 1 ? 0 : song.disc())));
|
tag->addValue("disc", QStringToTaglibString(song.disc() <= 0 ? QString() : QString::number(song.disc())), true);
|
||||||
tag->setItem("composer", TagLib::APE::Item("composer", TagLib::StringList(song.composer().c_str())));
|
tag->setItem("composer", TagLib::APE::Item("composer", TagLib::StringList(song.composer().c_str())));
|
||||||
tag->setItem("grouping", TagLib::APE::Item("grouping", TagLib::StringList(song.grouping().c_str())));
|
tag->setItem("grouping", TagLib::APE::Item("grouping", TagLib::StringList(song.grouping().c_str())));
|
||||||
tag->setItem("performer", TagLib::APE::Item("performer", TagLib::StringList(song.performer().c_str())));
|
tag->setItem("performer", TagLib::APE::Item("performer", TagLib::StringList(song.performer().c_str())));
|
||||||
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
|
tag->setItem("lyrics", TagLib::APE::Item("lyrics", TagLib::String(song.lyrics())));
|
||||||
tag->setItem("compilation", TagLib::APE::Item("compilation", TagLib::StringList(song.compilation() ? "1" : "0")));
|
tag->addValue("compilation", QStringToTaglibString(song.compilation() ? QString::number(1) : QString()), true);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TagReader::SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const {
|
|
||||||
|
|
||||||
const QByteArray descr_utf8(description.toUtf8());
|
|
||||||
const QByteArray value_utf8(value.toUtf8());
|
|
||||||
qLog(Debug) << "Setting FMPSFrame:" << description << ", " << value;
|
|
||||||
SetUserTextFrame(std::string(descr_utf8.constData(), descr_utf8.length()), std::string(value_utf8.constData(), value_utf8.length()), tag);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TagReader::SetUserTextFrame(const std::string &description, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
|
||||||
|
|
||||||
const TagLib::String t_description = StdStringToTaglibString(description);
|
|
||||||
// Remove the frame if it already exists
|
|
||||||
TagLib::ID3v2::UserTextIdentificationFrame *frame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, t_description);
|
|
||||||
if (frame) {
|
|
||||||
tag->removeFrame(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and add a new frame
|
|
||||||
frame = new TagLib::ID3v2::UserTextIdentificationFrame(TagLib::String::UTF8);
|
|
||||||
|
|
||||||
frame->setDescription(t_description);
|
|
||||||
frame->setText(StdStringToTaglibString(value));
|
|
||||||
tag->addFrame(frame);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,6 +642,7 @@ void TagReader::SetTextFrame(const char *id, const QString &value, TagLib::ID3v2
|
|||||||
|
|
||||||
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 TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::ID3v2::Tag *tag) const {
|
||||||
@ -665,6 +656,8 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
|||||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.empty()) return;
|
||||||
|
|
||||||
// If no frames stored create empty frame
|
// If no frames stored create empty frame
|
||||||
if (frames_buffer.isEmpty()) {
|
if (frames_buffer.isEmpty()) {
|
||||||
TagLib::ID3v2::TextIdentificationFrame frame(id_vector, TagLib::String::UTF8);
|
TagLib::ID3v2::TextIdentificationFrame frame(id_vector, TagLib::String::UTF8);
|
||||||
@ -672,9 +665,9 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update and add the frames
|
// Update and add the frames
|
||||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
|
||||||
TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(lyrics_index));
|
TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(i));
|
||||||
if (lyrics_index == 0) {
|
if (i == 0) {
|
||||||
frame->setText(StdStringToTaglibString(value));
|
frame->setText(StdStringToTaglibString(value));
|
||||||
}
|
}
|
||||||
// add frame takes ownership and clears the memory
|
// add frame takes ownership and clears the memory
|
||||||
@ -683,15 +676,6 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TagReader::IsMediaFile(const QString &filename) const {
|
|
||||||
|
|
||||||
qLog(Debug) << "Checking for valid file" << filename;
|
|
||||||
|
|
||||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
|
||||||
return !fileref->isNull() && fileref->tag();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
||||||
|
|
||||||
if (filename.isEmpty()) return QByteArray();
|
if (filename.isEmpty()) return QByteArray();
|
||||||
@ -711,8 +695,7 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
|||||||
if (flac_file && flac_file->xiphComment()) {
|
if (flac_file && flac_file->xiphComment()) {
|
||||||
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
||||||
if (!pics.isEmpty()) {
|
if (!pics.isEmpty()) {
|
||||||
// Use the first picture in the file - this could be made cleverer and
|
// Use the first picture in the file - this could be made cleverer and pick the front cover if it's present.
|
||||||
// pick the front cover if it's present.
|
|
||||||
|
|
||||||
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
||||||
TagLib::FLAC::Picture *picture = *it;
|
TagLib::FLAC::Picture *picture = *it;
|
||||||
@ -817,7 +800,7 @@ QByteArray TagReader::LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) co
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const {
|
void TagReader::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;
|
||||||
@ -828,6 +811,8 @@ void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Ta
|
|||||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.empty()) return;
|
||||||
|
|
||||||
// If no frames stored create empty frame
|
// If no frames stored create empty frame
|
||||||
if (frames_buffer.isEmpty()) {
|
if (frames_buffer.isEmpty()) {
|
||||||
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
|
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
|
||||||
@ -836,9 +821,9 @@ void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Ta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update and add the frames
|
// Update and add the frames
|
||||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
for (int i = 0 ; i < frames_buffer.size() ; ++i) {
|
||||||
TagLib::ID3v2::UnsynchronizedLyricsFrame* frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(lyrics_index));
|
TagLib::ID3v2::UnsynchronizedLyricsFrame *frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(i));
|
||||||
if (lyrics_index == 0) {
|
if (i == 0) {
|
||||||
frame->setText(StdStringToTaglibString(value));
|
frame->setText(StdStringToTaglibString(value));
|
||||||
}
|
}
|
||||||
// add frame takes ownership and clears the memory
|
// add frame takes ownership and clears the memory
|
||||||
|
@ -52,12 +52,12 @@ class TagReader {
|
|||||||
explicit TagReader();
|
explicit TagReader();
|
||||||
~TagReader();
|
~TagReader();
|
||||||
|
|
||||||
|
bool IsMediaFile(const QString &filename) const;
|
||||||
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
pb::tagreader::SongMetadata_FileType GuessFileType(TagLib::FileRef *fileref) const;
|
||||||
|
|
||||||
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
|
void ReadFile(const QString &filename, pb::tagreader::SongMetadata *song) const;
|
||||||
bool SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const;
|
bool SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const;
|
||||||
|
|
||||||
bool IsMediaFile(const QString &filename) const;
|
|
||||||
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
QByteArray LoadEmbeddedArt(const QString &filename) const;
|
||||||
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
QByteArray LoadEmbeddedAPEArt(const TagLib::APE::ItemListMap &map) const;
|
||||||
|
|
||||||
@ -70,9 +70,6 @@ class TagReader {
|
|||||||
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
|
void SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const;
|
||||||
void SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const;
|
void SaveAPETag(TagLib::APE::Tag *tag, const pb::tagreader::SongMetadata &song) const;
|
||||||
|
|
||||||
void SetUserTextFrame(const QString &description, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
|
||||||
void SetUserTextFrame(const std::string &description, const std::string& value, TagLib::ID3v2::Tag *tag) const;
|
|
||||||
|
|
||||||
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
void SetTextFrame(const char *id, const QString &value, TagLib::ID3v2::Tag *tag) const;
|
||||||
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user