mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-29 10:39:47 +01:00
Extend tag support: performer, grouping
The transaction handling while upgrading the database schema had to be revised. Furthermore some QSqlQuery statements needed to be finished properly. Fixes issue 2556
This commit is contained in:
parent
d083f38f54
commit
a6d3b48231
@ -343,6 +343,7 @@
|
||||
<file>schema/schema-42.sql</file>
|
||||
<file>schema/schema-43.sql</file>
|
||||
<file>schema/schema-44.sql</file>
|
||||
<file>schema/schema-45.sql</file>
|
||||
<file>schema/schema-4.sql</file>
|
||||
<file>schema/schema-5.sql</file>
|
||||
<file>schema/schema-6.sql</file>
|
||||
|
@ -42,11 +42,14 @@ CREATE TABLE jamendo.songs (
|
||||
unavailable INTEGER DEFAULT 0,
|
||||
|
||||
effective_albumartist TEXT,
|
||||
etag TEXT
|
||||
etag TEXT,
|
||||
|
||||
performer TEXT,
|
||||
grouping TEXT
|
||||
);
|
||||
|
||||
CREATE VIRTUAL TABLE jamendo.songs_fts USING fts3(
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsgenre, ftscomment,
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
||||
tokenize=unicode
|
||||
);
|
||||
|
||||
|
24
data/schema/schema-45.sql
Normal file
24
data/schema/schema-45.sql
Normal file
@ -0,0 +1,24 @@
|
||||
CREATE VIRTUAL TABLE playlist_items_fts USING fts3(
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsgenre, ftscomment,
|
||||
tokenize=unicode
|
||||
);
|
||||
|
||||
DELETE FROM %allsongstables_fts;
|
||||
|
||||
DROP TABLE %allsongstables_fts;
|
||||
|
||||
ALTER TABLE %allsongstables ADD COLUMN performer TEXT;
|
||||
|
||||
ALTER TABLE %allsongstables ADD COLUMN grouping TEXT;
|
||||
|
||||
CREATE VIRTUAL TABLE %allsongstables_fts USING fts3(
|
||||
ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment,
|
||||
tokenize=unicode
|
||||
);
|
||||
|
||||
INSERT INTO %allsongstables_fts (ROWID, ftstitle, ftsalbum, ftsartist, ftsalbumartist, ftscomposer, ftsperformer, ftsgrouping, ftsgenre, ftscomment)
|
||||
SELECT ROWID, title, album, artist, albumartist, composer, performer, grouping, genre, comment
|
||||
FROM %allsongstables;
|
||||
|
||||
UPDATE schema_version SET version=45;
|
||||
|
@ -161,6 +161,12 @@ void TagReader::ReadFile(const QString& filename,
|
||||
if (!map["TCOM"].isEmpty())
|
||||
Decode(map["TCOM"].front()->toString(), NULL, song->mutable_composer());
|
||||
|
||||
if (!map["TIT1"].isEmpty()) // content group
|
||||
Decode(map["TIT1"].front()->toString(), NULL, song->mutable_grouping());
|
||||
|
||||
if (!map["TPE1"].isEmpty()) // ID3v2: lead performer/soloist
|
||||
Decode(map["TPE1"].front()->toString(), NULL, song->mutable_performer());
|
||||
|
||||
if (!map["TPE2"].isEmpty()) // non-standard: Apple, Microsoft
|
||||
Decode(map["TPE2"].front()->toString(), NULL, song->mutable_albumartist());
|
||||
|
||||
@ -260,6 +266,9 @@ void TagReader::ReadFile(const QString& filename,
|
||||
if(items.contains("\251wrt")) {
|
||||
Decode(items["\251wrt"].toStringList().toString(", "), NULL, song->mutable_composer());
|
||||
}
|
||||
if(items.contains("\251grp")) {
|
||||
Decode(items["\251grp"].toStringList().toString(" "), NULL, song->mutable_grouping());
|
||||
}
|
||||
Decode(mp4_tag->comment(), NULL, song->mutable_comment());
|
||||
}
|
||||
}
|
||||
@ -398,6 +407,10 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap& map,
|
||||
pb::tagreader::SongMetadata* song) const {
|
||||
if (!map["COMPOSER"].isEmpty())
|
||||
Decode(map["COMPOSER"].front(), codec, song->mutable_composer());
|
||||
if (!map["PERFORMER"].isEmpty())
|
||||
Decode(map["PERFORMER"].front(), codec, song->mutable_performer());
|
||||
if (!map["CONTENT GROUP"].isEmpty())
|
||||
Decode(map["CONTENT GROUP"].front(), codec, song->mutable_grouping());
|
||||
|
||||
if (!map["ALBUMARTIST"].isEmpty()) {
|
||||
Decode(map["ALBUMARTIST"].front(), codec, song->mutable_albumartist());
|
||||
@ -428,6 +441,8 @@ void TagReader::SetVorbisComments(TagLib::Ogg::XiphComment* vorbis_comments,
|
||||
const pb::tagreader::SongMetadata& song) const {
|
||||
|
||||
vorbis_comments->addField("COMPOSER", StdStringToTaglibString(song.composer()), true);
|
||||
vorbis_comments->addField("PERFORMER", StdStringToTaglibString(song.performer()), true);
|
||||
vorbis_comments->addField("CONTENT GROUP", StdStringToTaglibString(song.grouping()), true);
|
||||
vorbis_comments->addField("BPM", QStringToTaglibString(song.bpm() <= 0 -1 ? QString() : QString::number(song.bpm())), true);
|
||||
vorbis_comments->addField("DISCNUMBER", QStringToTaglibString(song.disc() <= 0 -1 ? QString() : QString::number(song.disc())), true);
|
||||
vorbis_comments->addField("COMPILATION", StdStringToTaglibString(song.compilation() ? "1" : "0"), true);
|
||||
@ -501,6 +516,8 @@ bool TagReader::SaveFile(const QString& filename,
|
||||
SetTextFrame("TPOS", song.disc() <= 0 -1 ? QString() : QString::number(song.disc()), tag);
|
||||
SetTextFrame("TBPM", song.bpm() <= 0 -1 ? QString() : QString::number(song.bpm()), tag);
|
||||
SetTextFrame("TCOM", song.composer(), tag);
|
||||
SetTextFrame("TIT1", song.grouping(), tag);
|
||||
SetTextFrame("TPE1", song.performer(), tag);
|
||||
SetTextFrame("TPE2", song.albumartist(), tag);
|
||||
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
|
||||
} else if (TagLib::FLAC::File* file = dynamic_cast<TagLib::FLAC::File*>(fileref->file())) {
|
||||
@ -511,6 +528,7 @@ bool TagReader::SaveFile(const QString& filename,
|
||||
tag->itemListMap()["disk"] = TagLib::MP4::Item(song.disc() <= 0 -1 ? 0 : song.disc(), 0);
|
||||
tag->itemListMap()["tmpo"] = TagLib::StringList(song.bpm() <= 0 -1 ? "0" : TagLib::String::number(song.bpm()));
|
||||
tag->itemListMap()["\251wrt"] = TagLib::StringList(song.composer());
|
||||
tag->itemListMap()["\251grp"] = TagLib::StringList(song.grouping());
|
||||
tag->itemListMap()["aART"] = TagLib::StringList(song.albumartist());
|
||||
tag->itemListMap()["cpil"] = TagLib::StringList(song.compilation() ? "1" : "0");
|
||||
}
|
||||
|
@ -49,6 +49,8 @@ message SongMetadata {
|
||||
optional string art_automatic = 28;
|
||||
optional Type type = 29;
|
||||
optional string etag = 30;
|
||||
optional string performer = 31;
|
||||
optional string grouping = 32;
|
||||
}
|
||||
|
||||
message ReadFileRequest {
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include <QVariant>
|
||||
|
||||
const char* Database::kDatabaseFilename = "clementine.db";
|
||||
const int Database::kSchemaVersion = 44;
|
||||
const int Database::kSchemaVersion = 45;
|
||||
const char* Database::kMagicAllSongsTables = "%allsongstables";
|
||||
|
||||
int Database::sNextConnectionId = 1;
|
||||
@ -369,12 +369,16 @@ QSqlDatabase Database::Connect() {
|
||||
// Find Sqlite3 functions in the Qt plugin.
|
||||
StaticInit();
|
||||
|
||||
QSqlQuery set_fts_tokenizer("SELECT fts3_tokenizer(:name, :pointer)", db);
|
||||
set_fts_tokenizer.bindValue(":name", "unicode");
|
||||
set_fts_tokenizer.bindValue(":pointer", QByteArray(
|
||||
reinterpret_cast<const char*>(&sFTSTokenizer), sizeof(&sFTSTokenizer)));
|
||||
if (!set_fts_tokenizer.exec()) {
|
||||
qLog(Warning) << "Couldn't register FTS3 tokenizer";
|
||||
{
|
||||
QSqlQuery set_fts_tokenizer("SELECT fts3_tokenizer(:name, :pointer)", db);
|
||||
set_fts_tokenizer.bindValue(":name", "unicode");
|
||||
set_fts_tokenizer.bindValue(":pointer", QByteArray(
|
||||
reinterpret_cast<const char*>(&sFTSTokenizer), sizeof(&sFTSTokenizer)));
|
||||
if (!set_fts_tokenizer.exec()) {
|
||||
qLog(Warning) << "Couldn't register FTS3 tokenizer";
|
||||
}
|
||||
// Implicit invocation of ~QSqlQuery() when leaving the scope
|
||||
// to release any remaining database locks!
|
||||
}
|
||||
|
||||
if (db.tables().count() == 0) {
|
||||
@ -410,9 +414,8 @@ QSqlDatabase Database::Connect() {
|
||||
QSqlQuery q(QString("SELECT ROWID FROM %1.sqlite_master"
|
||||
" WHERE type='table'").arg(key), db);
|
||||
if (!q.exec() || !q.next()) {
|
||||
ScopedTransaction t(&db);
|
||||
ExecFromFile(attached_databases_[key].schema_, db, 0);
|
||||
t.Commit();
|
||||
q.finish();
|
||||
ExecSchemaCommandsFromFile(db, attached_databases_[key].schema_, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,10 +424,14 @@ QSqlDatabase Database::Connect() {
|
||||
|
||||
void Database::UpdateMainSchema(QSqlDatabase* db) {
|
||||
// Get the database's schema version
|
||||
QSqlQuery q("SELECT version FROM schema_version", *db);
|
||||
int schema_version = 0;
|
||||
if (q.next())
|
||||
schema_version = q.value(0).toInt();
|
||||
{
|
||||
QSqlQuery q("SELECT version FROM schema_version", *db);
|
||||
if (q.next())
|
||||
schema_version = q.value(0).toInt();
|
||||
// Implicit invocation of ~QSqlQuery() when leaving the scope
|
||||
// to release any remaining database locks!
|
||||
}
|
||||
|
||||
startup_schema_version_ = schema_version;
|
||||
|
||||
@ -478,13 +485,12 @@ void Database::UpdateDatabaseSchema(int version, QSqlDatabase &db) {
|
||||
filename = ":/schema/schema.sql";
|
||||
else
|
||||
filename = QString(":/schema/schema-%1.sql").arg(version);
|
||||
|
||||
ScopedTransaction t(&db);
|
||||
|
||||
|
||||
if (version == 31) {
|
||||
// This version used to do a bad job of converting filenames in the songs
|
||||
// table to file:// URLs. Now we do it properly here instead.
|
||||
|
||||
ScopedTransaction t(&db);
|
||||
|
||||
UrlEncodeFilenameColumn("songs", db);
|
||||
UrlEncodeFilenameColumn("playlist_items", db);
|
||||
|
||||
@ -493,30 +499,32 @@ void Database::UpdateDatabaseSchema(int version, QSqlDatabase &db) {
|
||||
UrlEncodeFilenameColumn(table, db);
|
||||
}
|
||||
}
|
||||
qLog(Debug) << "Applying database schema update" << version
|
||||
<< "from" << filename;
|
||||
ExecSchemaCommandsFromFile(db, filename, version - 1, &t);
|
||||
t.Commit();
|
||||
} else {
|
||||
qLog(Debug) << "Applying database schema update" << version
|
||||
<< "from" << filename;
|
||||
ExecSchemaCommandsFromFile(db, filename, version - 1);
|
||||
}
|
||||
|
||||
qLog(Debug) << "Applying database schema update" << version
|
||||
<< "from" << filename;
|
||||
ExecFromFile(filename, db, version - 1);
|
||||
t.Commit();
|
||||
}
|
||||
|
||||
void Database::UrlEncodeFilenameColumn(const QString& table, QSqlDatabase& db) {
|
||||
QSqlQuery select(QString("SELECT ROWID, filename FROM %1").arg(table), db);
|
||||
QSqlQuery update(QString("UPDATE %1 SET filename=:filename WHERE ROWID=:id").arg(table), db);
|
||||
|
||||
select.exec();
|
||||
if (CheckErrors(select)) return;
|
||||
while (select.next()) {
|
||||
const int rowid = select.value(0).toInt();
|
||||
const QString filename = select.value(1).toString();
|
||||
|
||||
|
||||
if (filename.isEmpty() || filename.contains("://")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const QUrl url = QUrl::fromLocalFile(filename);
|
||||
|
||||
|
||||
update.bindValue(":filename", url.toEncoded());
|
||||
update.bindValue(":id", rowid);
|
||||
update.exec();
|
||||
@ -524,30 +532,50 @@ void Database::UrlEncodeFilenameColumn(const QString& table, QSqlDatabase& db) {
|
||||
}
|
||||
}
|
||||
|
||||
void Database::ExecFromFile(const QString &filename, QSqlDatabase &db,
|
||||
int schema_version) {
|
||||
void Database::ExecSchemaCommandsFromFile(QSqlDatabase& db,
|
||||
QString const& filename,
|
||||
int schema_version,
|
||||
ScopedTransaction const* outerTransaction) {
|
||||
// Open and read the database schema
|
||||
QFile schema_file(filename);
|
||||
if (!schema_file.open(QIODevice::ReadOnly))
|
||||
qFatal("Couldn't open schema file %s", filename.toUtf8().constData());
|
||||
ExecCommands(QString::fromUtf8(schema_file.readAll()), db, schema_version);
|
||||
ExecSchemaCommands(db, QString::fromUtf8(schema_file.readAll()), schema_version, outerTransaction);
|
||||
}
|
||||
|
||||
void Database::ExecCommands(const QString& schema, QSqlDatabase& db,
|
||||
int schema_version) {
|
||||
void Database::ExecSchemaCommands(QSqlDatabase& db,
|
||||
QString const& schema,
|
||||
int schema_version,
|
||||
ScopedTransaction const* outerTransaction) {
|
||||
// Run each command
|
||||
QStringList commands(schema.split(";\n\n"));
|
||||
QStringList const schemaCommands(schema.split(";\n\n"));
|
||||
|
||||
// We don't want this list to reflect possible DB schema changes
|
||||
// so we initialize it before executing any statements.
|
||||
QStringList tables = SongsTables(db, schema_version);
|
||||
// If no outer transaction is provided the song tables need to
|
||||
// be queried before beginning an inner transaction! Otherwise
|
||||
// DROP TABLE commands on song tables may fail due to database
|
||||
// locks.
|
||||
QStringList const songTables(SongsTables(db, schema_version));
|
||||
|
||||
foreach (const QString& command, commands) {
|
||||
if (0 == outerTransaction) {
|
||||
ScopedTransaction innerTransaction(&db);
|
||||
ExecSongTablesCommands(db, songTables, schemaCommands);
|
||||
innerTransaction.Commit();
|
||||
} else {
|
||||
ExecSongTablesCommands(db, songTables, schemaCommands);
|
||||
}
|
||||
}
|
||||
|
||||
void Database::ExecSongTablesCommands(QSqlDatabase& db,
|
||||
QStringList const& songTables,
|
||||
QStringList const& commands) {
|
||||
foreach (QString const& command, commands) {
|
||||
// There are now lots of "songs" tables that need to have the same schema:
|
||||
// songs, magnatune_songs, and device_*_songs. We allow a magic value
|
||||
// in the schema files to update all songs tables at once.
|
||||
if (command.contains(kMagicAllSongsTables)) {
|
||||
foreach (const QString& table, tables) {
|
||||
foreach (QString const& table, songTables) {
|
||||
qLog(Info) << "Updating" << table << "for" << kMagicAllSongsTables;
|
||||
QString new_command(command);
|
||||
new_command.replace(kMagicAllSongsTables, table);
|
||||
|
@ -38,6 +38,7 @@ struct sqlite3_tokenizer_module;
|
||||
}
|
||||
|
||||
class Application;
|
||||
class ScopedTransaction;
|
||||
|
||||
class Database : public QObject {
|
||||
Q_OBJECT
|
||||
@ -55,8 +56,7 @@ class Database : public QObject {
|
||||
QMutex* Mutex() { return &mutex_; }
|
||||
|
||||
void RecreateAttachedDb(const QString& database_name);
|
||||
void ExecFromFile(const QString& filename, QSqlDatabase &db, int schema_version);
|
||||
void ExecCommands(const QString& commands, QSqlDatabase &db, int schema_version);
|
||||
void ExecSchemaCommands(QSqlDatabase &db, QString const& schema, int schema_version, ScopedTransaction const* outerTransaction = 0);
|
||||
|
||||
int startup_schema_version() const { return startup_schema_version_; }
|
||||
int current_schema_version() const { return kSchemaVersion; }
|
||||
@ -70,6 +70,9 @@ class Database : public QObject {
|
||||
private:
|
||||
void UpdateMainSchema(QSqlDatabase* db);
|
||||
|
||||
void ExecSchemaCommandsFromFile(QSqlDatabase &db, QString const& filename, int schema_version, ScopedTransaction const* outerTransaction = 0);
|
||||
void ExecSongTablesCommands(QSqlDatabase &db, QStringList const& songTables, QStringList const& commands);
|
||||
|
||||
void UpdateDatabaseSchema(int version, QSqlDatabase& db);
|
||||
void UrlEncodeFilenameColumn(const QString& table, QSqlDatabase& db);
|
||||
QStringList SongsTables(QSqlDatabase& db, int schema_version) const;
|
||||
|
@ -342,6 +342,8 @@ QVariantMap Mpris1::GetMetadata(const Song& song) {
|
||||
AddMetadata("audio-samplerate", song.samplerate(), &ret);
|
||||
AddMetadata("bpm", song.bpm(), &ret);
|
||||
AddMetadata("composer", song.composer(), &ret);
|
||||
AddMetadata("performer", song.performer(), &ret);
|
||||
AddMetadata("grouping", song.grouping(), &ret);
|
||||
if (song.rating() != -1.0) {
|
||||
AddMetadata("rating", song.rating() * 5, &ret);
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ const char* OrganiseFormat::kBlockPattern = "\\{([^{}]+)\\}";
|
||||
const QStringList OrganiseFormat::kKnownTags = QStringList()
|
||||
<< "title" << "album" << "artist" << "artistinitial" << "albumartist"
|
||||
<< "composer" << "track" << "disc" << "bpm" << "year" << "genre"
|
||||
<< "comment" << "length" << "bitrate" << "samplerate" << "extension";
|
||||
<< "comment" << "length" << "bitrate" << "samplerate" << "extension"
|
||||
<< "performer" << "grouping";
|
||||
|
||||
// From http://en.wikipedia.org/wiki/8.3_filename#Directory_table
|
||||
const char* OrganiseFormat::kInvalidFatCharacters = "\"*/\\:<>?|";
|
||||
@ -131,6 +132,8 @@ QString OrganiseFormat::TagValue(const QString &tag, const Song &song) const {
|
||||
else if (tag == "album") value = song.album();
|
||||
else if (tag == "artist") value = song.artist();
|
||||
else if (tag == "composer") value = song.composer();
|
||||
else if (tag == "performer") value = song.performer();
|
||||
else if (tag == "grouping") value = song.grouping();
|
||||
else if (tag == "genre") value = song.genre();
|
||||
else if (tag == "comment") value = song.comment();
|
||||
else if (tag == "year") value = QString::number(song.year());
|
||||
|
@ -73,7 +73,8 @@ const QStringList Song::kColumns = QStringList()
|
||||
<< "art_manual" << "filetype" << "playcount" << "lastplayed" << "rating"
|
||||
<< "forced_compilation_on" << "forced_compilation_off"
|
||||
<< "effective_compilation" << "skipcount" << "score" << "beginning" << "length"
|
||||
<< "cue_path" << "unavailable" << "effective_albumartist" << "etag";
|
||||
<< "cue_path" << "unavailable" << "effective_albumartist" << "etag"
|
||||
<< "performer" << "grouping";
|
||||
|
||||
const QString Song::kColumnSpec = Song::kColumns.join(", ");
|
||||
const QString Song::kBindSpec = Utilities::Prepend(":", Song::kColumns).join(", ");
|
||||
@ -82,7 +83,7 @@ const QString Song::kUpdateSpec = Utilities::Updateify(Song::kColumns).join(", "
|
||||
|
||||
const QStringList Song::kFtsColumns = QStringList()
|
||||
<< "ftstitle" << "ftsalbum" << "ftsartist" << "ftsalbumartist"
|
||||
<< "ftscomposer" << "ftsgenre" << "ftscomment";
|
||||
<< "ftscomposer" << "ftsperformer" << "ftsgrouping" << "ftsgenre" << "ftscomment";
|
||||
|
||||
const QString Song::kFtsColumnSpec = Song::kFtsColumns.join(", ");
|
||||
const QString Song::kFtsBindSpec = Utilities::Prepend(":", Song::kFtsColumns).join(", ");
|
||||
@ -103,6 +104,8 @@ struct Song::Private : public QSharedData {
|
||||
QString artist_;
|
||||
QString albumartist_;
|
||||
QString composer_;
|
||||
QString performer_;
|
||||
QString grouping_;
|
||||
int track_;
|
||||
int disc_;
|
||||
float bpm_;
|
||||
@ -234,6 +237,8 @@ const QString& Song::albumartist() const { return d->albumartist_; }
|
||||
const QString& Song::effective_albumartist() const { return d->albumartist_.isEmpty() ? d->artist_ : d->albumartist_; }
|
||||
const QString& Song::playlist_albumartist() const { return is_compilation() ? d->albumartist_ : effective_albumartist(); }
|
||||
const QString& Song::composer() const { return d->composer_; }
|
||||
const QString& Song::performer() const { return d->performer_; }
|
||||
const QString& Song::grouping() const { return d->grouping_; }
|
||||
int Song::track() const { return d->track_; }
|
||||
int Song::disc() const { return d->disc_; }
|
||||
float Song::bpm() const { return d->bpm_; }
|
||||
@ -281,6 +286,8 @@ void Song::set_album(const QString& v) { d->album_ = v; }
|
||||
void Song::set_artist(const QString& v) { d->artist_ = v; }
|
||||
void Song::set_albumartist(const QString& v) { d->albumartist_ = v; }
|
||||
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_track(int v) { d->track_ = v; }
|
||||
void Song::set_disc(int v) { d->disc_ = v; }
|
||||
void Song::set_bpm(float v) { d->bpm_ = v; }
|
||||
@ -396,6 +403,8 @@ void Song::InitFromProtobuf(const pb::tagreader::SongMetadata& pb) {
|
||||
d->artist_ = QStringFromStdString(pb.artist());
|
||||
d->albumartist_ = QStringFromStdString(pb.albumartist());
|
||||
d->composer_ = QStringFromStdString(pb.composer());
|
||||
d->performer_ = QStringFromStdString(pb.performer());
|
||||
d->grouping_ = QStringFromStdString(pb.grouping());
|
||||
d->track_ = pb.track();
|
||||
d->disc_ = pb.disc();
|
||||
d->bpm_ = pb.bpm();
|
||||
@ -437,6 +446,8 @@ void Song::ToProtobuf(pb::tagreader::SongMetadata* pb) const {
|
||||
pb->set_artist(DataCommaSizeFromQString(d->artist_));
|
||||
pb->set_albumartist(DataCommaSizeFromQString(d->albumartist_));
|
||||
pb->set_composer(DataCommaSizeFromQString(d->composer_));
|
||||
pb->set_performer(DataCommaSizeFromQString(d->performer_));
|
||||
pb->set_grouping(DataCommaSizeFromQString(d->grouping_));
|
||||
pb->set_track(d->track_);
|
||||
pb->set_disc(d->disc_);
|
||||
pb->set_bpm(d->bpm_);
|
||||
@ -523,6 +534,10 @@ void Song::InitFromQuery(const SqlRow& q, bool reliable_metadata, int col) {
|
||||
d->unavailable_ = q.value(col + 35).toBool();
|
||||
|
||||
// effective_albumartist = 36
|
||||
// etag = 37
|
||||
|
||||
d->performer_ = tostr(col + 38);
|
||||
d->grouping_ = tostr(col + 39);
|
||||
|
||||
#undef tostr
|
||||
#undef toint
|
||||
@ -571,6 +586,7 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
|
||||
d->artist_ = QString::fromUtf8(track->artist);
|
||||
d->albumartist_ = QString::fromUtf8(track->albumartist);
|
||||
d->composer_ = QString::fromUtf8(track->composer);
|
||||
d->grouping_ = QString::fromUtf8(track->grouping);
|
||||
d->track_ = track->track_nr;
|
||||
d->disc_ = track->cd_nr;
|
||||
d->bpm_ = track->BPM;
|
||||
@ -608,6 +624,7 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
|
||||
track->artist = strdup(d->artist_.toUtf8().constData());
|
||||
track->albumartist = strdup(d->albumartist_.toUtf8().constData());
|
||||
track->composer = strdup(d->composer_.toUtf8().constData());
|
||||
track->grouping = strdup(d->grouping_.toUtf8().constData());
|
||||
track->track_nr = d->track_;
|
||||
track->cd_nr = d->disc_;
|
||||
track->BPM = d->bpm_;
|
||||
@ -1012,6 +1029,9 @@ void Song::BindToQuery(QSqlQuery *query) const {
|
||||
|
||||
query->bindValue(":etag", strval(d->etag_));
|
||||
|
||||
query->bindValue(":performer", strval(d->performer_));
|
||||
query->bindValue(":grouping", strval(d->grouping_));
|
||||
|
||||
#undef intval
|
||||
#undef notnullintval
|
||||
#undef strval
|
||||
@ -1023,6 +1043,8 @@ void Song::BindToFtsQuery(QSqlQuery *query) const {
|
||||
query->bindValue(":ftsartist", d->artist_);
|
||||
query->bindValue(":ftsalbumartist", d->albumartist_);
|
||||
query->bindValue(":ftscomposer", d->composer_);
|
||||
query->bindValue(":ftsperformer", d->performer_);
|
||||
query->bindValue(":ftsgrouping", d->grouping_);
|
||||
query->bindValue(":ftsgenre", d->genre_);
|
||||
query->bindValue(":ftscomment", d->comment_);
|
||||
}
|
||||
@ -1114,6 +1136,8 @@ bool Song::IsMetadataEqual(const Song& other) const {
|
||||
d->artist_ == other.d->artist_ &&
|
||||
d->albumartist_ == other.d->albumartist_ &&
|
||||
d->composer_ == other.d->composer_ &&
|
||||
d->performer_ == other.d->performer_ &&
|
||||
d->grouping_ == other.d->grouping_ &&
|
||||
d->track_ == other.d->track_ &&
|
||||
d->disc_ == other.d->disc_ &&
|
||||
qFuzzyCompare(d->bpm_, other.d->bpm_) &&
|
||||
|
@ -161,6 +161,8 @@ class Song {
|
||||
// compilations, but you do for normal albums:
|
||||
const QString& playlist_albumartist() const;
|
||||
const QString& composer() const;
|
||||
const QString& performer() const;
|
||||
const QString& grouping() const;
|
||||
int track() const;
|
||||
int disc() const;
|
||||
float bpm() const;
|
||||
@ -235,6 +237,8 @@ class Song {
|
||||
void set_artist(const QString& v);
|
||||
void set_albumartist(const QString& v);
|
||||
void set_composer(const QString& v);
|
||||
void set_performer(const QString& v);
|
||||
void set_grouping(const QString& v);
|
||||
void set_track(int v);
|
||||
void set_disc(int v);
|
||||
void set_bpm(float v);
|
||||
|
@ -90,7 +90,7 @@ int DeviceDatabaseBackend::AddDevice(const Device& device) {
|
||||
QString schema = QString::fromUtf8(schema_file.readAll());
|
||||
schema.replace("%deviceid", QString::number(id));
|
||||
|
||||
db_->ExecCommands(schema, db, 0);
|
||||
db_->ExecSchemaCommands(db, schema, 0, &t);
|
||||
|
||||
t.Commit();
|
||||
return id;
|
||||
|
@ -125,6 +125,8 @@ QStandardItem* GlobalSearchModel::BuildContainers(
|
||||
break;
|
||||
|
||||
case LibraryModel::GroupBy_Composer: display_text = s.composer();
|
||||
case LibraryModel::GroupBy_Performer: display_text = s.performer();
|
||||
case LibraryModel::GroupBy_Grouping: display_text = s.grouping();
|
||||
case LibraryModel::GroupBy_Genre: if (display_text.isNull()) display_text = s.genre();
|
||||
case LibraryModel::GroupBy_Album:
|
||||
unique_tag = s.album_id();
|
||||
|
@ -36,6 +36,8 @@ GroupByDialog::GroupByDialog(QWidget *parent)
|
||||
mapping_.insert(Mapping(LibraryModel::GroupBy_Genre, 6));
|
||||
mapping_.insert(Mapping(LibraryModel::GroupBy_Year, 7));
|
||||
mapping_.insert(Mapping(LibraryModel::GroupBy_YearAlbum, 8));
|
||||
mapping_.insert(Mapping(LibraryModel::GroupBy_Performer, 9));
|
||||
mapping_.insert(Mapping(LibraryModel::GroupBy_Grouping, 10));
|
||||
|
||||
connect(ui_->button_box->button(QDialogButtonBox::Reset), SIGNAL(clicked()),
|
||||
SLOT(Reset()));
|
||||
|
@ -180,6 +180,8 @@ void LibraryModel::SongsDiscovered(const SongList& songs) {
|
||||
case GroupBy_Album: key = song.album(); break;
|
||||
case GroupBy_Artist: key = song.artist(); break;
|
||||
case GroupBy_Composer: key = song.composer(); break;
|
||||
case GroupBy_Performer: key = song.performer(); break;
|
||||
case GroupBy_Grouping: key = song.grouping(); break;
|
||||
case GroupBy_Genre: key = song.genre(); break;
|
||||
case GroupBy_AlbumArtist: key = song.effective_albumartist(); break;
|
||||
case GroupBy_Year:
|
||||
@ -256,6 +258,8 @@ QString LibraryModel::DividerKey(GroupBy type, LibraryItem* item) const {
|
||||
case GroupBy_Album:
|
||||
case GroupBy_Artist:
|
||||
case GroupBy_Composer:
|
||||
case GroupBy_Performer:
|
||||
case GroupBy_Grouping:
|
||||
case GroupBy_Genre:
|
||||
case GroupBy_AlbumArtist:
|
||||
case GroupBy_FileType: {
|
||||
@ -289,6 +293,8 @@ QString LibraryModel::DividerDisplayText(GroupBy type, const QString& key) const
|
||||
case GroupBy_Album:
|
||||
case GroupBy_Artist:
|
||||
case GroupBy_Composer:
|
||||
case GroupBy_Performer:
|
||||
case GroupBy_Grouping:
|
||||
case GroupBy_Genre:
|
||||
case GroupBy_AlbumArtist:
|
||||
case GroupBy_FileType:
|
||||
@ -714,6 +720,12 @@ void LibraryModel::InitQuery(GroupBy type, LibraryQuery* q) {
|
||||
case GroupBy_Composer:
|
||||
q->SetColumnSpec("DISTINCT composer");
|
||||
break;
|
||||
case GroupBy_Performer:
|
||||
q->SetColumnSpec("DISTINCT performer");
|
||||
break;
|
||||
case GroupBy_Grouping:
|
||||
q->SetColumnSpec("DISTINCT grouping");
|
||||
break;
|
||||
case GroupBy_YearAlbum:
|
||||
q->SetColumnSpec("DISTINCT year, album");
|
||||
break;
|
||||
@ -762,6 +774,12 @@ void LibraryModel::FilterQuery(GroupBy type, LibraryItem* item, LibraryQuery* q)
|
||||
case GroupBy_Composer:
|
||||
q->AddWhere("composer", item->key);
|
||||
break;
|
||||
case GroupBy_Performer:
|
||||
q->AddWhere("performer", item->key);
|
||||
break;
|
||||
case GroupBy_Grouping:
|
||||
q->AddWhere("grouping", item->key);
|
||||
break;
|
||||
case GroupBy_Genre:
|
||||
q->AddWhere("genre", item->key);
|
||||
break;
|
||||
@ -829,6 +847,8 @@ LibraryItem* LibraryModel::ItemFromQuery(GroupBy type,
|
||||
break;
|
||||
|
||||
case GroupBy_Composer:
|
||||
case GroupBy_Performer:
|
||||
case GroupBy_Grouping:
|
||||
case GroupBy_Genre:
|
||||
case GroupBy_Album:
|
||||
case GroupBy_AlbumArtist:
|
||||
@ -883,6 +903,8 @@ LibraryItem* LibraryModel::ItemFromSong(GroupBy type,
|
||||
break;
|
||||
|
||||
case GroupBy_Composer: item->key = s.composer();
|
||||
case GroupBy_Performer: item->key = s.performer();
|
||||
case GroupBy_Grouping: item->key = s.grouping();
|
||||
case GroupBy_Genre: if (item->key.isNull()) item->key = s.genre();
|
||||
case GroupBy_Album: if (item->key.isNull()) item->key = s.album();
|
||||
case GroupBy_AlbumArtist: if (item->key.isNull()) item->key = s.effective_albumartist();
|
||||
|
@ -80,6 +80,8 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
|
||||
GroupBy_Genre = 6,
|
||||
GroupBy_AlbumArtist = 7,
|
||||
GroupBy_FileType = 8,
|
||||
GroupBy_Performer = 9,
|
||||
GroupBy_Grouping = 10,
|
||||
};
|
||||
|
||||
struct Grouping {
|
||||
|
@ -174,6 +174,8 @@ bool Playlist::column_is_editable(Playlist::Column column) {
|
||||
case Column_Album:
|
||||
case Column_AlbumArtist:
|
||||
case Column_Composer:
|
||||
case Column_Performer:
|
||||
case Column_Grouping:
|
||||
case Column_Track:
|
||||
case Column_Disc:
|
||||
case Column_Year:
|
||||
@ -209,6 +211,12 @@ bool Playlist::set_column_value(Song& song, Playlist::Column column,
|
||||
case Column_Composer:
|
||||
song.set_composer(value.toString());
|
||||
break;
|
||||
case Column_Performer:
|
||||
song.set_performer(value.toString());
|
||||
break;
|
||||
case Column_Grouping:
|
||||
song.set_grouping(value.toString());
|
||||
break;
|
||||
case Column_Track:
|
||||
song.set_track(value.toInt());
|
||||
break;
|
||||
@ -271,6 +279,8 @@ QVariant Playlist::data(const QModelIndex& index, int role) const {
|
||||
case Column_Genre: return song.genre();
|
||||
case Column_AlbumArtist: return song.playlist_albumartist();
|
||||
case Column_Composer: return song.composer();
|
||||
case Column_Performer: return song.performer();
|
||||
case Column_Grouping: return song.grouping();
|
||||
|
||||
case Column_Rating: return song.rating();
|
||||
case Column_PlayCount: return song.playcount();
|
||||
@ -1160,6 +1170,8 @@ bool Playlist::CompareItems(int column, Qt::SortOrder order,
|
||||
case Column_Genre: strcmp(genre);
|
||||
case Column_AlbumArtist: strcmp(playlist_albumartist);
|
||||
case Column_Composer: strcmp(composer);
|
||||
case Column_Performer: strcmp(performer);
|
||||
case Column_Grouping: strcmp(grouping);
|
||||
|
||||
case Column_Rating: cmp(rating);
|
||||
case Column_PlayCount: cmp(playcount);
|
||||
@ -1199,6 +1211,8 @@ QString Playlist::column_name(Column column) {
|
||||
case Column_Genre: return tr("Genre");
|
||||
case Column_AlbumArtist: return tr("Album artist");
|
||||
case Column_Composer: return tr("Composer");
|
||||
case Column_Performer: return tr("Performer");
|
||||
case Column_Grouping: return tr("Grouping");
|
||||
|
||||
case Column_Rating: return tr("Rating");
|
||||
case Column_PlayCount: return tr("Play count");
|
||||
|
@ -90,6 +90,8 @@ class Playlist : public QAbstractListModel {
|
||||
Column_Album,
|
||||
Column_AlbumArtist,
|
||||
Column_Composer,
|
||||
Column_Performer,
|
||||
Column_Grouping,
|
||||
Column_Length,
|
||||
Column_Track,
|
||||
Column_Disc,
|
||||
|
@ -378,6 +378,8 @@ QString TagCompletionModel::database_column(Playlist::Column column) {
|
||||
case Playlist::Column_Album: return "album";
|
||||
case Playlist::Column_AlbumArtist: return "albumartist";
|
||||
case Playlist::Column_Composer: return "composer";
|
||||
case Playlist::Column_Performer: return "performer";
|
||||
case Playlist::Column_Grouping: return "grouping";
|
||||
case Playlist::Column_Genre: return "genre";
|
||||
default:
|
||||
qLog(Warning) << "Unknown column" << column;
|
||||
|
@ -33,6 +33,8 @@ PlaylistFilter::PlaylistFilter(QObject *parent)
|
||||
column_names_["album"] = Playlist::Column_Album;
|
||||
column_names_["albumartist"] = Playlist::Column_AlbumArtist;
|
||||
column_names_["composer"] = Playlist::Column_Composer;
|
||||
column_names_["performer"] = Playlist::Column_Performer;
|
||||
column_names_["grouping"] = Playlist::Column_Grouping;
|
||||
column_names_["length"] = Playlist::Column_Length;
|
||||
column_names_["track"] = Playlist::Column_Track;
|
||||
column_names_["disc"] = Playlist::Column_Disc;
|
||||
|
@ -192,6 +192,10 @@ void PlaylistView::SetItemDelegates(LibraryBackend* backend) {
|
||||
new TagCompletionItemDelegate(this, backend, Playlist::Column_Genre));
|
||||
setItemDelegateForColumn(Playlist::Column_Composer,
|
||||
new TagCompletionItemDelegate(this, backend, Playlist::Column_Composer));
|
||||
setItemDelegateForColumn(Playlist::Column_Performer,
|
||||
new TagCompletionItemDelegate(this, backend, Playlist::Column_Performer));
|
||||
setItemDelegateForColumn(Playlist::Column_Grouping,
|
||||
new TagCompletionItemDelegate(this, backend, Playlist::Column_Grouping));
|
||||
setItemDelegateForColumn(Playlist::Column_Length, new LengthItemDelegate(this));
|
||||
setItemDelegateForColumn(Playlist::Column_Filesize, new SizeItemDelegate(this));
|
||||
setItemDelegateForColumn(Playlist::Column_Filetype, new FileTypeItemDelegate(this));
|
||||
@ -286,6 +290,8 @@ void PlaylistView::LoadGeometry() {
|
||||
header_->HideSection(Playlist::Column_DateModified);
|
||||
header_->HideSection(Playlist::Column_AlbumArtist);
|
||||
header_->HideSection(Playlist::Column_Composer);
|
||||
header_->HideSection(Playlist::Column_Performer);
|
||||
header_->HideSection(Playlist::Column_Grouping);
|
||||
header_->HideSection(Playlist::Column_Rating);
|
||||
header_->HideSection(Playlist::Column_PlayCount);
|
||||
header_->HideSection(Playlist::Column_SkipCount);
|
||||
|
@ -250,6 +250,8 @@ QString SearchTerm::FieldColumnName(Field field) {
|
||||
case Field_Album: return "album";
|
||||
case Field_AlbumArtist: return "albumartist";
|
||||
case Field_Composer: return "composer";
|
||||
case Field_Performer: return "performer";
|
||||
case Field_Grouping: return "grouping";
|
||||
case Field_Genre: return "genre";
|
||||
case Field_Comment: return "comment";
|
||||
case Field_Filepath: return "filename";
|
||||
@ -280,6 +282,8 @@ QString SearchTerm::FieldName(Field field) {
|
||||
case Field_Album: return Playlist::column_name(Playlist::Column_Album);
|
||||
case Field_AlbumArtist: return Playlist::column_name(Playlist::Column_AlbumArtist);
|
||||
case Field_Composer: return Playlist::column_name(Playlist::Column_Composer);
|
||||
case Field_Performer: return Playlist::column_name(Playlist::Column_Performer);
|
||||
case Field_Grouping: return Playlist::column_name(Playlist::Column_Grouping);
|
||||
case Field_Genre: return Playlist::column_name(Playlist::Column_Genre);
|
||||
case Field_Comment: return QObject::tr("Comment");
|
||||
case Field_Filepath: return Playlist::column_name(Playlist::Column_Filename);
|
||||
|
@ -32,6 +32,8 @@ public:
|
||||
Field_Album,
|
||||
Field_AlbumArtist,
|
||||
Field_Composer,
|
||||
Field_Performer,
|
||||
Field_Grouping,
|
||||
Field_Length,
|
||||
Field_Track,
|
||||
Field_Disc,
|
||||
|
@ -186,6 +186,8 @@ EditTagDialog::EditTagDialog(Application* app, QWidget* parent)
|
||||
new TagCompleter(app_->library_backend(), Playlist::Column_AlbumArtist, ui_->albumartist);
|
||||
new TagCompleter(app_->library_backend(), Playlist::Column_Genre, ui_->genre);
|
||||
new TagCompleter(app_->library_backend(), Playlist::Column_Composer, ui_->composer);
|
||||
new TagCompleter(app_->library_backend(), Playlist::Column_Performer, ui_->performer);
|
||||
new TagCompleter(app_->library_backend(), Playlist::Column_Grouping, ui_->grouping);
|
||||
}
|
||||
|
||||
EditTagDialog::~EditTagDialog() {
|
||||
@ -290,6 +292,8 @@ QVariant EditTagDialog::Data::value(const Song& song, const QString& id) {
|
||||
if (id == "album") return song.album();
|
||||
if (id == "albumartist") return song.albumartist();
|
||||
if (id == "composer") return song.composer();
|
||||
if (id == "performer") return song.performer();
|
||||
if (id == "grouping") return song.grouping();
|
||||
if (id == "genre") return song.genre();
|
||||
if (id == "comment") return song.comment();
|
||||
if (id == "track") return song.track();
|
||||
@ -305,6 +309,8 @@ void EditTagDialog::Data::set_value(const QString& id, const QVariant& value) {
|
||||
if (id == "album") current_.set_album(value.toString());
|
||||
if (id == "albumartist") current_.set_albumartist(value.toString());
|
||||
if (id == "composer") current_.set_composer(value.toString());
|
||||
if (id == "performer") current_.set_performer(value.toString());
|
||||
if (id == "grouping") current_.set_grouping(value.toString());
|
||||
if (id == "genre") current_.set_genre(value.toString());
|
||||
if (id == "comment") current_.set_comment(value.toString());
|
||||
if (id == "track") current_.set_track(value.toInt());
|
||||
|
@ -779,17 +779,37 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="genre_label">
|
||||
<widget class="QLabel" name="performer_label">
|
||||
<property name="text">
|
||||
<string>Genre</string>
|
||||
<string>Performer</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>genre</cstring>
|
||||
<cstring>performer</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="LineEdit" name="genre">
|
||||
<widget class="LineEdit" name="performer">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="grouping_label">
|
||||
<property name="text">
|
||||
<string>Grouping</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>grouping</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="LineEdit" name="grouping">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -799,17 +819,17 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="comment_label">
|
||||
<widget class="QLabel" name="genre_label">
|
||||
<property name="text">
|
||||
<string>Comment</string>
|
||||
<string>Genre</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>comment</cstring>
|
||||
<cstring>genre</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1" colspan="3">
|
||||
<widget class="TextEdit" name="comment">
|
||||
<item row="7" column="1">
|
||||
<widget class="LineEdit" name="genre">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -818,7 +838,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QPushButton" name="fetch_tag">
|
||||
<property name="text">
|
||||
<string>Complete tags automatically</string>
|
||||
@ -835,6 +855,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="comment_label">
|
||||
<property name="text">
|
||||
<string>Comment</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>comment</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1" colspan="3">
|
||||
<widget class="TextEdit" name="comment">
|
||||
<property name="has_reset_button" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="has_clear_button" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
@ -2289,6 +2289,7 @@ void MainWindow::HandleNotificationPreview(OSD::Behaviour type, QString line1, Q
|
||||
fake.Init("Title", "Artist", "Album", 123);
|
||||
fake.set_genre("Classical");
|
||||
fake.set_composer("Anonymous");
|
||||
fake.set_performer("Anonymous");
|
||||
fake.set_track(1);
|
||||
fake.set_disc(1);
|
||||
fake.set_year(2011);
|
||||
|
@ -48,6 +48,8 @@ NotificationsSettingsPage::NotificationsSettingsPage(SettingsDialog* dialog)
|
||||
menu->addAction(ui_->action_albumartist);
|
||||
menu->addAction(ui_->action_year);
|
||||
menu->addAction(ui_->action_composer);
|
||||
menu->addAction(ui_->action_performer);
|
||||
menu->addAction(ui_->action_grouping);
|
||||
menu->addAction(ui_->action_length);
|
||||
menu->addAction(ui_->action_disc);
|
||||
menu->addAction(ui_->action_track);
|
||||
|
@ -378,6 +378,22 @@
|
||||
<string>Add song composer tag</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_performer">
|
||||
<property name="text">
|
||||
<string notr="true">%performer%</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add song performer tag</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_grouping">
|
||||
<property name="text">
|
||||
<string notr="true">%grouping%</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add song grouping tag</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_disc">
|
||||
<property name="text">
|
||||
<string notr="true">%disc%</string>
|
||||
|
@ -57,6 +57,8 @@ OrganiseDialog::OrganiseDialog(TaskManager* task_manager, QWidget *parent)
|
||||
tags[tr("Artist's initial")] = "artistinitial";
|
||||
tags[tr("Album artist")] = "albumartist";
|
||||
tags[tr("Composer")] = "composer";
|
||||
tags[tr("Performer")] = "performer";
|
||||
tags[tr("Grouping")] = "grouping";
|
||||
tags[tr("Track")] = "track";
|
||||
tags[tr("Disc")] = "disc";
|
||||
tags[tr("BPM")] = "bpm";
|
||||
|
@ -306,6 +306,10 @@ QString OSD::ReplaceVariable(const QString& variable, const Song& song) {
|
||||
return song.PrettyYear();
|
||||
} else if (variable == "%composer%") {
|
||||
return song.composer();
|
||||
} else if (variable == "%performer%") {
|
||||
return song.performer();
|
||||
} else if (variable == "%grouping%") {
|
||||
return song.grouping();
|
||||
} else if (variable == "%length%") {
|
||||
return song.PrettyLength();
|
||||
} else if (variable == "%disc%") {
|
||||
|
@ -38,6 +38,8 @@ TEST_F(OrganiseFormatTest, BasicReplace) {
|
||||
song_.set_bpm(4.56);
|
||||
song_.set_comment("comment");
|
||||
song_.set_composer("composer");
|
||||
song_.set_performer("performer");
|
||||
song_.set_grouping("grouping");
|
||||
song_.set_disc(789);
|
||||
song_.set_genre("genre");
|
||||
song_.set_length_nanosec(987 * kNsecPerSec);
|
||||
@ -47,10 +49,11 @@ TEST_F(OrganiseFormatTest, BasicReplace) {
|
||||
song_.set_year(2010);
|
||||
|
||||
format_.set_format("%album %albumartist %bitrate %bpm %comment %composer "
|
||||
"%performer %grouping "
|
||||
"%disc %genre %length %samplerate %title %track %year");
|
||||
|
||||
ASSERT_TRUE(format_.IsValid());
|
||||
EXPECT_EQ("album albumartist 123 4.56 comment composer 789 genre 987 654 title 321 2010",
|
||||
EXPECT_EQ("album albumartist 123 4.56 comment composer performer grouping 789 genre 987 654 title 321 2010",
|
||||
format_.GetFilenameForSong(song_));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user