Added support for reading lyrics from tags
Also fixed tagreader crash when saving tags to MP3 files
This commit is contained in:
parent
0a64a2a394
commit
1562585561
|
@ -20,6 +20,8 @@ Unreleased:
|
|||
* Made xine enabled only for window debug
|
||||
* Removed dead code
|
||||
* Added DSF and DSDIFF/DFF support
|
||||
* Fixed tagreader crash when saving tags to MP3 files
|
||||
* Added support for reading/writing lyrics to tags
|
||||
|
||||
Version 0.2.1:
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>schema/schema.sql</file>
|
||||
<file>schema/schema-1.sql</file>
|
||||
<file>schema/schema-1.sql</file>
|
||||
<file>schema/schema-2.sql</file>
|
||||
<file>schema/device-schema.sql</file>
|
||||
<file>style/strawberry.css</file>
|
||||
<file>misc/playing_tooltip.txt</file>
|
||||
|
|
|
@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
|||
|
||||
DELETE FROM schema_version;
|
||||
|
||||
INSERT INTO schema_version (version) VALUES (1);
|
||||
INSERT INTO schema_version (version) VALUES (2);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS directories (
|
||||
path TEXT NOT NULL,
|
||||
|
@ -35,6 +35,7 @@ CREATE TABLE IF NOT EXISTS songs (
|
|||
performer TEXT NOT NULL,
|
||||
grouping TEXT NOT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
lyrics TEXT NOT NULL,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
@ -109,6 +110,7 @@ CREATE TABLE IF NOT EXISTS playlist_items (
|
|||
performer TEXT NOT NULL,
|
||||
grouping TEXT NOT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
lyrics TEXT NOT NULL,
|
||||
|
||||
beginning INTEGER NOT NULL DEFAULT 0,
|
||||
length INTEGER NOT NULL DEFAULT 0,
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <taglib/textidentificationframe.h>
|
||||
#include <taglib/xiphcomment.h>
|
||||
#include <taglib/commentsframe.h>
|
||||
#include <taglib/unsynchronizedlyricsframe.h>
|
||||
#include <taglib/tag.h>
|
||||
#include <taglib/id3v2tag.h>
|
||||
#include "taglib/id3v2frame.h"
|
||||
|
@ -157,15 +158,14 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||
return;
|
||||
}
|
||||
|
||||
song->set_filetype(GuessFileType(fileref.get()));
|
||||
|
||||
if (fileref->audioProperties()) {
|
||||
song->set_bitrate(fileref->audioProperties()->bitrate());
|
||||
song->set_samplerate(fileref->audioProperties()->sampleRate());
|
||||
song->set_length_nanosec(fileref->audioProperties()->length() * kNsecPerSec);
|
||||
}
|
||||
|
||||
// Get the filetype if we can
|
||||
song->set_filetype(GuessFileType(fileref.get()));
|
||||
|
||||
TagLib::Tag *tag = fileref->tag();
|
||||
if (tag) {
|
||||
Decode(tag->title(), nullptr, song->mutable_title());
|
||||
|
@ -179,6 +179,7 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||
|
||||
QString disc;
|
||||
QString compilation;
|
||||
QString lyrics;
|
||||
|
||||
// Handle all the files which have VorbisComments (Ogg, OPUS, ...) in the same way;
|
||||
// apart, so we keep specific behavior for some formats by adding another "else if" block below.
|
||||
|
@ -189,9 +190,29 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||
if (!tag->pictureList().isEmpty()) song->set_art_automatic(kEmbeddedCover);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File *>(fileref->file())) {
|
||||
|
||||
song->set_bitdepth(file->audioProperties()->bitsPerSample());
|
||||
|
||||
if ( file->xiphComment() ) {
|
||||
ParseOggTag(file->xiphComment()->fieldListMap(), nullptr, &disc, &compilation, song);
|
||||
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
|
||||
if (!file->pictureList().isEmpty()) {
|
||||
song->set_art_automatic(kEmbeddedCover);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (TagLib::WavPack::File *file = dynamic_cast<TagLib::WavPack::File *>(fileref->file())) {
|
||||
song->set_bitdepth(file->audioProperties()->bitsPerSample());
|
||||
Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
|
||||
else if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
|
||||
if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
|
||||
if (file->ID3v2Tag()) {
|
||||
const TagLib::ID3v2::FrameListMap &map = file->ID3v2Tag()->frameListMap();
|
||||
|
||||
|
@ -245,22 +266,9 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||
|
||||
}
|
||||
}
|
||||
else if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File *>(fileref->file())) {
|
||||
|
||||
song->set_bitdepth(file->audioProperties()->bitsPerSample());
|
||||
|
||||
if ( file->xiphComment() ) {
|
||||
ParseOggTag(file->xiphComment()->fieldListMap(), nullptr, &disc, &compilation, song);
|
||||
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
|
||||
if (!file->pictureList().isEmpty()) {
|
||||
song->set_art_automatic(kEmbeddedCover);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Decode(tag->comment(), nullptr, song->mutable_comment());
|
||||
}
|
||||
else if (TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File*>(fileref->file())) {
|
||||
|
||||
|
||||
song->set_bitdepth(file->audioProperties()->bitsPerSample());
|
||||
|
||||
if (file->tag()) {
|
||||
|
@ -347,6 +355,8 @@ void TagReader::ReadFile(const QString &filename, pb::tagreader::SongMetadata *s
|
|||
song->set_compilation(compilation.toInt() == 1);
|
||||
}
|
||||
|
||||
if (!lyrics.isEmpty()) song->set_lyrics(lyrics.toStdString());
|
||||
|
||||
// Set integer fields to -1 if they're not valid
|
||||
#define SetDefault(field) if (song->field() <= 0) { song->set_##field(-1); }
|
||||
SetDefault(track);
|
||||
|
@ -438,6 +448,11 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap &map, const QTextCod
|
|||
|
||||
if (!map["FMPS_PLAYCOUNT"].isEmpty() && song->playcount() <= 0) song->set_playcount(TStringToQString( map["FMPS_PLAYCOUNT"].front() ).trimmed().toFloat());
|
||||
|
||||
if (!map["LYRICS"].isEmpty())
|
||||
Decode(map["LYRICS"].front(), codec, song->mutable_lyrics());
|
||||
else if (!map["UNSYNCEDLYRICS"].isEmpty())
|
||||
Decode(map["UNSYNCEDLYRICS"].front(), codec, song->mutable_lyrics());
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, const pb::tagreader::SongMetadata &song) const {
|
||||
|
@ -453,6 +468,8 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment *vorbis_comments, con
|
|||
vorbis_comments->addField("ALBUMARTIST", StdStringToTaglibString(song.albumartist()), true);
|
||||
vorbis_comments->removeField("ALBUM ARTIST");
|
||||
|
||||
vorbis_comments->addField("LYRICS", StdStringToTaglibString(song.lyrics()), true);
|
||||
vorbis_comments->removeField("UNSYNCEDLYRICS");
|
||||
|
||||
}
|
||||
|
||||
|
@ -481,15 +498,11 @@ pb::tagreader::SongMetadata_Type TagReader::GuessFileType(TagLib::FileRef *filer
|
|||
}
|
||||
|
||||
bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetadata &song) const {
|
||||
|
||||
if (filename.isNull()) return false;
|
||||
|
||||
if (filename.isNull() || filename.isEmpty()) return false;
|
||||
qLog(Debug) << "Saving tags to" << filename;
|
||||
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
|
||||
if (!fileref || fileref->isNull()) // The file probably doesn't exist
|
||||
return false;
|
||||
std::unique_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));;
|
||||
if (!fileref || fileref->isNull()) return false;
|
||||
|
||||
fileref->tag()->setTitle(StdStringToTaglibString(song.title()));
|
||||
fileref->tag()->setArtist(StdStringToTaglibString(song.artist()));
|
||||
|
@ -501,6 +514,7 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
|||
|
||||
if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag *tag = file->ID3v2Tag(true);
|
||||
if (!tag) return false;
|
||||
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
|
||||
SetTextFrame("TCOM", song.composer(), tag);
|
||||
SetTextFrame("TIT1", song.grouping(), tag);
|
||||
|
@ -508,6 +522,7 @@ bool TagReader::SaveFile(const QString &filename, const pb::tagreader::SongMetad
|
|||
// Skip TPE1 (which is the artist) here because we already set it
|
||||
SetTextFrame("TPE2", song.albumartist(), tag);
|
||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
||||
SetUnsyncLyricsFrame(song.lyrics(), tag);
|
||||
}
|
||||
else if (TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||
TagLib::Ogg::XiphComment *tag = file->xiphComment();
|
||||
|
@ -589,10 +604,15 @@ void TagReader::SetTextFrame(const char *id, const std::string &value, TagLib::I
|
|||
frames_buffer.push_back(frame.render());
|
||||
}
|
||||
|
||||
// add frame takes ownership and clears the memory
|
||||
TagLib::ID3v2::TextIdentificationFrame *frame;
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
tag->addFrame(frame);
|
||||
// Update and add the frames
|
||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
||||
TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame(frames_buffer.at(lyrics_index));
|
||||
if (lyrics_index == 0) {
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
}
|
||||
// add frame takes ownership and clears the memory
|
||||
tag->addFrame(frame);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -619,23 +639,26 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
|||
|
||||
if (ref.isNull() || !ref.file()) return QByteArray();
|
||||
|
||||
// MP3
|
||||
TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File*>(ref.file());
|
||||
if (file && file->ID3v2Tag()) {
|
||||
TagLib::ID3v2::FrameList apic_frames = file->ID3v2Tag()->frameListMap()["APIC"];
|
||||
if (apic_frames.isEmpty())
|
||||
return QByteArray();
|
||||
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
|
||||
// FLAC
|
||||
TagLib::FLAC::File *flac_file = dynamic_cast<TagLib::FLAC::File*>(ref.file());
|
||||
if (flac_file && flac_file->xiphComment()) {
|
||||
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
||||
if (!pics.isEmpty()) {
|
||||
// Use the first picture in the file - this could be made cleverer and
|
||||
// pick the front cover if it's present.
|
||||
|
||||
TagLib::ID3v2::AttachedPictureFrame *pic = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(apic_frames.front());
|
||||
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
||||
TagLib::FLAC::Picture *picture = *it;
|
||||
|
||||
return QByteArray((const char*) pic->picture().data(), pic->picture().size());
|
||||
return QByteArray(picture->data().data(), picture->data().size());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ogg vorbis/speex
|
||||
// Ogg Vorbis / Speex
|
||||
TagLib::Ogg::XiphComment *xiph_comment = dynamic_cast<TagLib::Ogg::XiphComment*>(ref.file()->tag());
|
||||
|
||||
if (xiph_comment) {
|
||||
|
||||
TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap();
|
||||
|
||||
#if TAGLIB_MAJOR_VERSION <= 1 && TAGLIB_MINOR_VERSION < 11
|
||||
|
@ -678,22 +701,17 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
|||
return QByteArray::fromBase64(map["COVERART"].toString().toCString());
|
||||
}
|
||||
|
||||
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
|
||||
// Flac
|
||||
TagLib::FLAC::File *flac_file = dynamic_cast<TagLib::FLAC::File*>(ref.file());
|
||||
if (flac_file && flac_file->xiphComment()) {
|
||||
TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
|
||||
if (!pics.isEmpty()) {
|
||||
// Use the first picture in the file - this could be made cleverer and
|
||||
// pick the front cover if it's present.
|
||||
// MP3
|
||||
TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File*>(ref.file());
|
||||
if (file && file->ID3v2Tag()) {
|
||||
TagLib::ID3v2::FrameList apic_frames = file->ID3v2Tag()->frameListMap()["APIC"];
|
||||
if (apic_frames.isEmpty())
|
||||
return QByteArray();
|
||||
|
||||
std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
|
||||
TagLib::FLAC::Picture *picture = *it;
|
||||
TagLib::ID3v2::AttachedPictureFrame *pic = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(apic_frames.front());
|
||||
|
||||
return QByteArray(picture->data().data(), picture->data().size());
|
||||
}
|
||||
return QByteArray((const char*) pic->picture().data(), pic->picture().size());
|
||||
}
|
||||
#endif
|
||||
|
||||
// MP4/AAC
|
||||
TagLib::MP4::File *aac_file = dynamic_cast<TagLib::MP4::File*>(ref.file());
|
||||
|
@ -715,3 +733,33 @@ QByteArray TagReader::LoadEmbeddedArt(const QString &filename) const {
|
|||
return QByteArray();
|
||||
|
||||
}
|
||||
|
||||
void TagReader::SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const {
|
||||
|
||||
TagLib::ByteVector id_vector("USLT");
|
||||
QVector<TagLib::ByteVector> frames_buffer;
|
||||
|
||||
// Store and clear existing frames
|
||||
while (tag->frameListMap().contains(id_vector) && tag->frameListMap()[id_vector].size() != 0) {
|
||||
frames_buffer.push_back(tag->frameListMap()[id_vector].front()->render());
|
||||
tag->removeFrame(tag->frameListMap()[id_vector].front());
|
||||
}
|
||||
|
||||
// If no frames stored create empty frame
|
||||
if (frames_buffer.isEmpty()) {
|
||||
TagLib::ID3v2::UnsynchronizedLyricsFrame frame(TagLib::String::UTF8);
|
||||
frame.setDescription("Clementine editor");
|
||||
frames_buffer.push_back(frame.render());
|
||||
}
|
||||
|
||||
// Update and add the frames
|
||||
for (int lyrics_index = 0; lyrics_index < frames_buffer.size(); lyrics_index++) {
|
||||
TagLib::ID3v2::UnsynchronizedLyricsFrame* frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(frames_buffer.at(lyrics_index));
|
||||
if (lyrics_index == 0) {
|
||||
frame->setText(StdStringToTaglibString(value));
|
||||
}
|
||||
// add frame takes ownership and clears the memory
|
||||
tag->addFrame(frame);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ class TagReader {
|
|||
|
||||
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 SetUnsyncLyricsFrame(const std::string& value, TagLib::ID3v2::Tag* tag) const;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ message SongMetadata {
|
|||
}
|
||||
|
||||
optional bool valid = 1;
|
||||
|
||||
|
||||
optional string title = 2;
|
||||
optional string album = 3;
|
||||
optional string artist = 4;
|
||||
|
@ -41,26 +41,27 @@ message SongMetadata {
|
|||
optional string performer = 13;
|
||||
optional string grouping = 14;
|
||||
optional string comment = 15;
|
||||
|
||||
optional uint64 length_nanosec = 16;
|
||||
|
||||
optional int32 bitrate = 17;
|
||||
optional int32 samplerate = 18;
|
||||
optional int32 bitdepth = 19;
|
||||
optional string lyrics = 16;
|
||||
|
||||
optional string url = 20;
|
||||
optional string basefilename = 21;
|
||||
optional Type filetype = 22;
|
||||
optional int32 filesize = 23;
|
||||
optional int32 mtime = 24;
|
||||
optional int32 ctime = 25;
|
||||
optional uint64 length_nanosec = 17;
|
||||
|
||||
optional int32 playcount = 26;
|
||||
optional int32 skipcount = 27;
|
||||
optional int32 lastplayed = 28;
|
||||
optional int32 bitrate = 18;
|
||||
optional int32 samplerate = 19;
|
||||
optional int32 bitdepth = 20;
|
||||
|
||||
optional bool suspicious_tags = 29;
|
||||
optional string art_automatic = 30;
|
||||
optional string url = 21;
|
||||
optional string basefilename = 22;
|
||||
optional Type filetype = 23;
|
||||
optional int32 filesize = 24;
|
||||
optional int32 mtime = 25;
|
||||
optional int32 ctime = 26;
|
||||
|
||||
optional int32 playcount = 27;
|
||||
optional int32 skipcount = 28;
|
||||
optional int32 lastplayed = 29;
|
||||
|
||||
optional bool suspicious_tags = 30;
|
||||
optional string art_automatic = 31;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ void ContextView::SongChanged(const Song &song) {
|
|||
|
||||
image_previous_ = image_original_;
|
||||
prev_artist_ = song_playing_.artist();
|
||||
lyrics_ = QString();
|
||||
lyrics_ = song.lyrics();
|
||||
song_playing_ = song;
|
||||
song_ = song;
|
||||
UpdateSong();
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
#include "scopedtransaction.h"
|
||||
|
||||
const char *Database::kDatabaseFilename = "strawberry.db";
|
||||
const int Database::kSchemaVersion = 1;
|
||||
const int Database::kSchemaVersion = 2;
|
||||
const char *Database::kMagicAllSongsTables = "%allsongstables";
|
||||
|
||||
int Database::sNextConnectionId = 1;
|
||||
|
|
|
@ -65,6 +65,7 @@ const QStringList OrganiseFormat::kKnownTags = QStringList() << "title"
|
|||
<< "extension"
|
||||
<< "performer"
|
||||
<< "grouping"
|
||||
<< "lyrics"
|
||||
<< "originalyear";
|
||||
|
||||
// From http://en.wikipedia.org/wiki/8.3_filename#Directory_table
|
||||
|
@ -200,6 +201,8 @@ QString OrganiseFormat::TagValue(const QString &tag, const Song &song) const {
|
|||
value = song.performer();
|
||||
else if (tag == "grouping")
|
||||
value = song.grouping();
|
||||
else if (tag == "lyrics")
|
||||
value = song.lyrics();
|
||||
else if (tag == "genre")
|
||||
value = song.genre();
|
||||
else if (tag == "comment")
|
||||
|
|
|
@ -77,6 +77,7 @@ const QStringList Song::kColumns = QStringList() << "title"
|
|||
<< "performer"
|
||||
<< "grouping"
|
||||
<< "comment"
|
||||
<< "lyrics"
|
||||
|
||||
<< "beginning"
|
||||
<< "length"
|
||||
|
@ -157,6 +158,7 @@ struct Song::Private : public QSharedData {
|
|||
QString performer_;
|
||||
QString grouping_;
|
||||
QString comment_;
|
||||
QString lyrics_;
|
||||
|
||||
qint64 beginning_;
|
||||
qint64 end_;
|
||||
|
@ -265,6 +267,7 @@ int Song::effective_originalyear() const {
|
|||
}
|
||||
const QString &Song::genre() const { return d->genre_; }
|
||||
const QString &Song::comment() const { return d->comment_; }
|
||||
const QString &Song::lyrics() const { return d->lyrics_; }
|
||||
bool Song::is_compilation() const {
|
||||
return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && ! d->compilation_off_;
|
||||
}
|
||||
|
@ -318,6 +321,7 @@ void Song::set_composer(const QString &v) { d->composer_ = v; }
|
|||
void Song::set_performer(const QString &v) { d->performer_ = v; }
|
||||
void Song::set_grouping(const QString &v) { d->grouping_ = v; }
|
||||
void Song::set_comment(const QString &v) { d->comment_ = v; }
|
||||
void Song::set_lyrics(const QString &v) { d->lyrics_ = v; }
|
||||
|
||||
void Song::set_beginning_nanosec(qint64 v) { d->beginning_ = qMax(0ll, v); }
|
||||
void Song::set_end_nanosec(qint64 v) { d->end_ = v; }
|
||||
|
@ -463,6 +467,7 @@ void Song::InitFromProtobuf(const pb::tagreader::SongMetadata &pb) {
|
|||
d->originalyear_ = pb.originalyear();
|
||||
d->genre_ = QStringFromStdString(pb.genre());
|
||||
d->comment_ = QStringFromStdString(pb.comment());
|
||||
d->lyrics_ = QStringFromStdString(pb.lyrics());
|
||||
d->compilation_ = pb.compilation();
|
||||
d->skipcount_ = pb.skipcount();
|
||||
d->lastplayed_ = pb.lastplayed();
|
||||
|
@ -502,6 +507,7 @@ void Song::ToProtobuf(pb::tagreader::SongMetadata *pb) const {
|
|||
pb->set_composer(DataCommaSizeFromQString(d->composer_));
|
||||
pb->set_performer(DataCommaSizeFromQString(d->performer_));
|
||||
pb->set_grouping(DataCommaSizeFromQString(d->grouping_));
|
||||
pb->set_lyrics(DataCommaSizeFromQString(d->lyrics_));
|
||||
pb->set_track(d->track_);
|
||||
pb->set_disc(d->disc_);
|
||||
pb->set_year(d->year_);
|
||||
|
@ -590,6 +596,9 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) {
|
|||
else if (Song::kColumns.value(i) == "comment") {
|
||||
d->comment_ = tostr(x);
|
||||
}
|
||||
else if (Song::kColumns.value(i) == "lyrics") {
|
||||
d->comment_ = tostr(x);
|
||||
}
|
||||
|
||||
else if (Song::kColumns.value(i) == "beginning") {
|
||||
d->beginning_ = q.value(x).isNull() ? 0 : q.value(x).toLongLong();
|
||||
|
@ -929,6 +938,7 @@ void Song::BindToQuery(QSqlQuery *query) const {
|
|||
query->bindValue(":performer", strval(d->performer_));
|
||||
query->bindValue(":grouping", strval(d->grouping_));
|
||||
query->bindValue(":comment", strval(d->comment_));
|
||||
query->bindValue(":lyrics", strval(d->lyrics_));
|
||||
|
||||
query->bindValue(":beginning", d->beginning_);
|
||||
query->bindValue(":length", intval(length_nanosec()));
|
||||
|
@ -1062,6 +1072,7 @@ bool Song::IsMetadataEqual(const Song &other) const {
|
|||
d->originalyear_ == other.d->originalyear_ &&
|
||||
d->genre_ == other.d->genre_ &&
|
||||
d->comment_ == other.d->comment_ &&
|
||||
d->lyrics_ == other.d->lyrics_ &&
|
||||
d->compilation_ == other.d->compilation_ &&
|
||||
d->beginning_ == other.d->beginning_ &&
|
||||
length_nanosec() == other.length_nanosec() &&
|
||||
|
|
|
@ -167,6 +167,7 @@ class Song {
|
|||
const QString &performer() const;
|
||||
const QString &grouping() const;
|
||||
const QString &comment() const;
|
||||
const QString &lyrics() const;
|
||||
|
||||
int playcount() const;
|
||||
int skipcount() const;
|
||||
|
@ -250,7 +251,8 @@ class Song {
|
|||
void set_performer(const QString &v);
|
||||
void set_grouping(const QString &v);
|
||||
void set_comment(const QString &v);
|
||||
|
||||
void set_lyrics(const QString &v);
|
||||
|
||||
void set_beginning_nanosec(qint64 v);
|
||||
void set_end_nanosec(qint64 v);
|
||||
void set_length_nanosec(qint64 v);
|
||||
|
|
Loading…
Reference in New Issue