diff --git a/data/data.qrc b/data/data.qrc index a944cff24..f69eccfe3 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -388,6 +388,7 @@ schema/schema-49.sql schema/schema-4.sql schema/schema-5.sql + schema/schema-50.sql schema/schema-6.sql schema/schema-7.sql schema/schema-8.sql diff --git a/data/schema/device-schema.sql b/data/schema/device-schema.sql index 3ed4d21c0..9b24f94f6 100644 --- a/data/schema/device-schema.sql +++ b/data/schema/device-schema.sql @@ -59,7 +59,10 @@ CREATE TABLE device_%deviceid_songs ( performer TEXT, grouping TEXT, - lyrics TEXT + lyrics TEXT, + + originalyear INTEGER, + effective_originalyear INTEGER ); CREATE INDEX idx_device_%deviceid_songs_album ON device_%deviceid_songs (album); diff --git a/data/schema/jamendo.sql b/data/schema/jamendo.sql index bdc308ece..97467c725 100644 --- a/data/schema/jamendo.sql +++ b/data/schema/jamendo.sql @@ -46,7 +46,10 @@ CREATE TABLE jamendo.songs ( performer TEXT, grouping TEXT, - lyrics TEXT + lyrics TEXT, + + originalyear INTEGER, + effective_originalyear INTEGER ); CREATE VIRTUAL TABLE jamendo.songs_fts USING fts3( diff --git a/data/schema/schema-50.sql b/data/schema/schema-50.sql new file mode 100644 index 000000000..5ca552f25 --- /dev/null +++ b/data/schema/schema-50.sql @@ -0,0 +1,9 @@ +ALTER TABLE %allsongstables ADD COLUMN originalyear INTEGER; + +ALTER TABLE %allsongstables ADD COLUMN effective_originalyear INTEGER; + +UPDATE songs SET originalyear = -1; + +UPDATE songs SET effective_originalyear = -1; + +UPDATE schema_version SET version=50; diff --git a/ext/libclementine-tagreader/tagreader.cpp b/ext/libclementine-tagreader/tagreader.cpp index b01f4dd6a..af9b93ab2 100644 --- a/ext/libclementine-tagreader/tagreader.cpp +++ b/ext/libclementine-tagreader/tagreader.cpp @@ -190,6 +190,9 @@ void TagReader::ReadFile(const QString& filename, compilation = TStringToQString(map["TCMP"].front()->toString()).trimmed(); + if(!map["TDOR"].isEmpty()) + song->set_originalyear(map["TDOR"].front()->toString().substr(0, 4).toInt()); + if (!map["USLT"].isEmpty()) { Decode(map["USLT"].front()->toString(), nullptr, song->mutable_lyrics()); @@ -489,6 +492,9 @@ void TagReader::ParseOggTag(const TagLib::Ogg::FieldListMap& map, Decode(map["ALBUM ARTIST"].front(), codec, song->mutable_albumartist()); } + if (!map["ORIGINALDATE"].isEmpty()) + song->set_originalyear(TStringToQString(map["ORIGINALDATE"].front()).left(4).toInt()); + if (!map["BPM"].isEmpty()) song->set_bpm(TStringToQString(map["BPM"].front()).trimmed().toFloat()); diff --git a/ext/libclementine-tagreader/tagreadermessages.proto b/ext/libclementine-tagreader/tagreadermessages.proto index 225e03403..0e9322ec2 100644 --- a/ext/libclementine-tagreader/tagreadermessages.proto +++ b/ext/libclementine-tagreader/tagreadermessages.proto @@ -52,6 +52,7 @@ message SongMetadata { optional string performer = 31; optional string grouping = 32; optional string lyrics = 33; + optional int32 originalyear = 34; } message ReadFileRequest { diff --git a/src/core/database.cpp b/src/core/database.cpp index 3daab7a75..1f5e15f7d 100644 --- a/src/core/database.cpp +++ b/src/core/database.cpp @@ -47,7 +47,7 @@ #include const char* Database::kDatabaseFilename = "clementine.db"; -const int Database::kSchemaVersion = 49; +const int Database::kSchemaVersion = 50; const char* Database::kMagicAllSongsTables = "%allsongstables"; int Database::sNextConnectionId = 1; diff --git a/src/core/organiseformat.cpp b/src/core/organiseformat.cpp index 8be7b730e..2dcddeb25 100644 --- a/src/core/organiseformat.cpp +++ b/src/core/organiseformat.cpp @@ -51,7 +51,8 @@ const QStringList OrganiseFormat::kKnownTags = QStringList() << "title" << "extension" << "performer" << "grouping" - << "lyrics"; + << "lyrics" + << "originalyear"; // From http://en.wikipedia.org/wiki/8.3_filename#Directory_table const char OrganiseFormat::kInvalidFatCharacters[] = "\"*/\\:<>?|"; @@ -200,6 +201,8 @@ QString OrganiseFormat::TagValue(const QString& tag, const Song& song) const { value = song.comment(); else if (tag == "year") value = QString::number(song.year()); + else if (tag == "originalyear") + value = QString::number(song.effective_originalyear()); else if (tag == "track") value = QString::number(song.track()); else if (tag == "disc") diff --git a/src/core/song.cpp b/src/core/song.cpp index c990fb3ae..dc6387c6a 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -112,7 +112,9 @@ const QStringList Song::kColumns = QStringList() << "title" << "etag" << "performer" << "grouping" - << "lyrics"; + << "lyrics" + << "originalyear" + << "effective_originalyear"; const QString Song::kColumnSpec = Song::kColumns.join(", "); const QString Song::kBindSpec = @@ -157,6 +159,7 @@ struct Song::Private : public QSharedData { int disc_; float bpm_; int year_; + int originalyear_; QString genre_; QString comment_; bool compilation_; // From the file tag @@ -230,6 +233,7 @@ Song::Private::Private() disc_(-1), bpm_(-1), year_(-1), + originalyear_(-1), compilation_(false), sampler_(false), forced_compilation_on_(false), @@ -285,6 +289,10 @@ int Song::track() const { return d->track_; } int Song::disc() const { return d->disc_; } float Song::bpm() const { return d->bpm_; } int Song::year() const { return d->year_; } +int Song::originalyear() const { return d->originalyear_; } +int Song::effective_originalyear() const { + return d->originalyear_ < 0 ? d->year_ : d->originalyear_; +} const QString& Song::genre() const { return d->genre_; } const QString& Song::comment() const { return d->comment_; } bool Song::is_compilation() const { @@ -342,6 +350,7 @@ 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; } void Song::set_year(int v) { d->year_ = v; } +void Song::set_originalyear(int v) { d->originalyear_ = v; } void Song::set_genre(const QString& v) { d->genre_ = v; } void Song::set_comment(const QString& v) { d->comment_ = v; } void Song::set_compilation(bool v) { d->compilation_ = v; } @@ -499,6 +508,7 @@ void Song::InitFromProtobuf(const pb::tagreader::SongMetadata& pb) { d->disc_ = pb.disc(); d->bpm_ = pb.bpm(); d->year_ = pb.year(); + d->originalyear_ = pb.originalyear(); d->genre_ = QStringFromStdString(pb.genre()); d->comment_ = QStringFromStdString(pb.comment()); d->compilation_ = pb.compilation(); @@ -585,6 +595,7 @@ void Song::InitFromQuery(const SqlRow& q, bool reliable_metadata, int col) { d->disc_ = toint(col + 7); d->bpm_ = tofloat(col + 8); d->year_ = toint(col + 9); + d->originalyear_ = toint(col + 41); d->genre_ = tostr(col + 10); d->comment_ = tostr(col + 11); d->compilation_ = q.value(col + 12).toBool(); @@ -957,6 +968,8 @@ void Song::BindToQuery(QSqlQuery* query) const { query->bindValue(":performer", strval(d->performer_)); query->bindValue(":grouping", strval(d->grouping_)); query->bindValue(":lyrics", strval(d->lyrics_)); + query->bindValue(":originalyear", intval(d->originalyear_)); + query->bindValue(":effective_originalyear", intval(this->effective_originalyear())); #undef intval #undef notnullintval @@ -1056,7 +1069,8 @@ bool Song::IsMetadataEqual(const Song& other) const { 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_) && - d->year_ == other.d->year_ && d->genre_ == other.d->genre_ && + d->year_ == other.d->year_ && d->originalyear_ == other.d->originalyear_ && + d->genre_ == other.d->genre_ && d->comment_ == other.d->comment_ && d->compilation_ == other.d->compilation_ && d->beginning_ == other.d->beginning_ && diff --git a/src/core/song.h b/src/core/song.h index d3c08b71e..3b8e4fca7 100644 --- a/src/core/song.h +++ b/src/core/song.h @@ -176,6 +176,8 @@ class Song { int disc() const; float bpm() const; int year() const; + int originalyear() const; + int effective_originalyear() const; const QString& genre() const; const QString& comment() const; bool is_compilation() const; @@ -255,6 +257,7 @@ class Song { void set_disc(int v); void set_bpm(float v); void set_year(int v); + void set_originalyear(int v); void set_genre(const QString& v); void set_genre_id3(int id); void set_comment(const QString& v); diff --git a/src/globalsearch/globalsearchmodel.cpp b/src/globalsearch/globalsearchmodel.cpp index 2c84da856..bd0f8becc 100644 --- a/src/globalsearch/globalsearchmodel.cpp +++ b/src/globalsearch/globalsearchmodel.cpp @@ -120,12 +120,26 @@ QStandardItem* GlobalSearchModel::BuildContainers(const Song& s, has_album_icon = true; break; + case LibraryModel::GroupBy_OriginalYearAlbum: + year = qMax(0, s.effective_originalyear()); + display_text = LibraryModel::PrettyYearAlbum(year, s.album()); + sort_text = LibraryModel::SortTextForNumber(year) + s.album(); + unique_tag = s.album_id(); + has_album_icon = true; + break; + case LibraryModel::GroupBy_Year: year = qMax(0, s.year()); display_text = QString::number(year); sort_text = LibraryModel::SortTextForNumber(year) + " "; break; + case LibraryModel::GroupBy_OriginalYear: + year = qMax(0, s.effective_originalyear()); + display_text = QString::number(year); + sort_text = LibraryModel::SortTextForNumber(year) + " "; + break; + case LibraryModel::GroupBy_Composer: display_text = s.composer(); case LibraryModel::GroupBy_Performer: diff --git a/src/globalsearch/globalsearchview.cpp b/src/globalsearch/globalsearchview.cpp index 6cc99ca04..067d1e003 100644 --- a/src/globalsearch/globalsearchview.cpp +++ b/src/globalsearch/globalsearchview.cpp @@ -337,7 +337,8 @@ void GlobalSearchView::LazyLoadArt(const QModelIndex& proxy_index) { proxy_index.data(LibraryModel::Role_ContainerType).toInt()); if (container_type != LibraryModel::GroupBy_Album && container_type != LibraryModel::GroupBy_AlbumArtist && - container_type != LibraryModel::GroupBy_YearAlbum) { + container_type != LibraryModel::GroupBy_YearAlbum && + container_type != LibraryModel::GroupBy_OriginalYearAlbum) { return; } diff --git a/src/library/groupbydialog.cpp b/src/library/groupbydialog.cpp index 1b3e84571..5efdc9f36 100644 --- a/src/library/groupbydialog.cpp +++ b/src/library/groupbydialog.cpp @@ -76,11 +76,13 @@ GroupByDialog::GroupByDialog(QWidget* parent) p_->mapping_.insert(Mapping(LibraryModel::GroupBy_FileType, 5)); p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Genre, 6)); p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Year, 7)); - p_->mapping_.insert(Mapping(LibraryModel::GroupBy_YearAlbum, 8)); - p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Bitrate, 9)); - p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Disc, 10)); - p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Performer, 11)); - p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Grouping, 12)); + p_->mapping_.insert(Mapping(LibraryModel::GroupBy_OriginalYear, 8)); + p_->mapping_.insert(Mapping(LibraryModel::GroupBy_YearAlbum, 9)); + p_->mapping_.insert(Mapping(LibraryModel::GroupBy_OriginalYearAlbum, 10)); + p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Bitrate, 11)); + p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Disc, 12)); + p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Performer, 13)); + p_->mapping_.insert(Mapping(LibraryModel::GroupBy_Grouping, 14)); connect(ui_->button_box->button(QDialogButtonBox::Reset), SIGNAL(clicked()), SLOT(Reset())); diff --git a/src/library/groupbydialog.ui b/src/library/groupbydialog.ui index e648850d7..609b4b80f 100644 --- a/src/library/groupbydialog.ui +++ b/src/library/groupbydialog.ui @@ -83,11 +83,21 @@ Year + + + Original year + + Year - Album + + + Original year - Album + + Bitrate @@ -159,11 +169,21 @@ Year + + + Original year + + Year - Album + + + Original year - Album + + Bitrate @@ -235,11 +255,21 @@ Year + + + Original year + + Year - Album + + + Original year - Album + + Bitrate diff --git a/src/library/librarymodel.cpp b/src/library/librarymodel.cpp index 51e9cedde..d26081dde 100644 --- a/src/library/librarymodel.cpp +++ b/src/library/librarymodel.cpp @@ -221,9 +221,15 @@ void LibraryModel::SongsDiscovered(const SongList& songs) { case GroupBy_Year: key = QString::number(qMax(0, song.year())); break; + case GroupBy_OriginalYear: + key = QString::number(qMax(0, song.effective_originalyear())); + break; case GroupBy_YearAlbum: key = PrettyYearAlbum(qMax(0, song.year()), song.album()); break; + case GroupBy_OriginalYearAlbum: + key = PrettyYearAlbum(qMax(0, song.effective_originalyear()), song.album()); + break; case GroupBy_FileType: key = song.filetype(); break; @@ -313,11 +319,15 @@ QString LibraryModel::DividerKey(GroupBy type, LibraryItem* item) const { } case GroupBy_Year: + case GroupBy_OriginalYear: return SortTextForNumber(item->sort_text.toInt() / 10 * 10); case GroupBy_YearAlbum: return SortTextForNumber(item->metadata.year()); + case GroupBy_OriginalYearAlbum: + return SortTextForNumber(item->metadata.effective_originalyear()); + case GroupBy_Bitrate: return SortTextForNumber(item->metadata.bitrate()); @@ -347,10 +357,12 @@ QString LibraryModel::DividerDisplayText(GroupBy type, return key.toUpper(); case GroupBy_YearAlbum: + case GroupBy_OriginalYearAlbum: if (key == "0000") return tr("Unknown"); return key.toUpper(); case GroupBy_Year: + case GroupBy_OriginalYear: if (key == "0000") return tr("Unknown"); return QString::number(key.toInt()); // To remove leading 0s @@ -543,7 +555,8 @@ QVariant LibraryModel::data(const QModelIndex& index, int role) const { item->type == LibraryItem::Type_Container) { GroupBy container_type = group_by_[item->container_level]; is_album_node = container_type == GroupBy_Album || - container_type == GroupBy_YearAlbum; + container_type == GroupBy_YearAlbum || + container_type == GroupBy_OriginalYearAlbum; } if (is_album_node) { // It has const behaviour some of the time - that's ok right? @@ -572,6 +585,7 @@ QVariant LibraryModel::data(const LibraryItem* item, int role) const { switch (container_type) { case GroupBy_Album: case GroupBy_YearAlbum: + case GroupBy_OriginalYearAlbum: return album_icon_; case GroupBy_Artist: case GroupBy_AlbumArtist: @@ -797,9 +811,15 @@ void LibraryModel::InitQuery(GroupBy type, LibraryQuery* q) { case GroupBy_YearAlbum: q->SetColumnSpec("DISTINCT year, album, grouping"); break; + case GroupBy_OriginalYearAlbum: + q->SetColumnSpec("DISTINCT year, originalyear, album, grouping"); + break; case GroupBy_Year: q->SetColumnSpec("DISTINCT year"); break; + case GroupBy_OriginalYear: + q->SetColumnSpec("DISTINCT effective_originalyear"); + break; case GroupBy_Genre: q->SetColumnSpec("DISTINCT genre"); break; @@ -841,9 +861,19 @@ void LibraryModel::FilterQuery(GroupBy type, LibraryItem* item, q->AddWhere("album", item->metadata.album()); q->AddWhere("grouping", item->metadata.grouping()); break; + case GroupBy_OriginalYearAlbum: + q->AddWhere("year", item->metadata.year()); + q->AddWhere("originalyear", item->metadata.originalyear()); + q->AddWhere("album", item->metadata.album()); + q->AddWhere("grouping", item->metadata.grouping()); + break; + case GroupBy_Year: q->AddWhere("year", item->key); break; + case GroupBy_OriginalYear: + q->AddWhere("effective_originalyear", item->key); + break; case GroupBy_Composer: q->AddWhere("composer", item->key); break; @@ -903,6 +933,7 @@ LibraryItem* LibraryModel::ItemFromQuery(GroupBy type, bool signal, int container_level) { LibraryItem* item = InitItem(type, signal, parent, container_level); int year = 0; + int effective_originalyear = 0; int bitrate = 0; int disc = 0; @@ -923,12 +954,29 @@ LibraryItem* LibraryModel::ItemFromQuery(GroupBy type, bool signal, item->metadata.album(); break; + case GroupBy_OriginalYearAlbum: + item->metadata.set_year(row.value(0).toInt()); + item->metadata.set_originalyear(row.value(1).toInt()); + item->metadata.set_album(row.value(2).toString()); + item->metadata.set_grouping(row.value(3).toString()); + effective_originalyear = qMax(0, item->metadata.effective_originalyear()); + item->key = PrettyYearAlbum(effective_originalyear, item->metadata.album()); + item->sort_text = SortTextForNumber(effective_originalyear) + item->metadata.grouping() + + item->metadata.album(); + break; + case GroupBy_Year: year = qMax(0, row.value(0).toInt()); item->key = QString::number(year); item->sort_text = SortTextForNumber(year) + " "; break; + case GroupBy_OriginalYear: + year = qMax(0, row.value(0).toInt()); + item->key = QString::number(year); + item->sort_text = SortTextForNumber(year) + " "; + break; + case GroupBy_Composer: case GroupBy_Performer: case GroupBy_Grouping: @@ -975,6 +1023,8 @@ LibraryItem* LibraryModel::ItemFromSong(GroupBy type, bool signal, int container_level) { LibraryItem* item = InitItem(type, signal, parent, container_level); int year = 0; + int originalyear = 0; + int effective_originalyear = 0; int bitrate = 0; switch (type) { @@ -992,12 +1042,29 @@ LibraryItem* LibraryModel::ItemFromSong(GroupBy type, bool signal, item->sort_text = SortTextForNumber(year) + s.grouping() + s.album(); break; + case GroupBy_OriginalYearAlbum: + year = qMax(0, s.year()); + originalyear = qMax(0, s.originalyear()); + effective_originalyear = qMax(0, s.effective_originalyear()); + item->metadata.set_year(year); + item->metadata.set_originalyear(originalyear); + item->metadata.set_album(s.album()); + item->key = PrettyYearAlbum(effective_originalyear, s.album()); + item->sort_text = SortTextForNumber(effective_originalyear) + s.grouping() + s.album(); + break; + case GroupBy_Year: year = qMax(0, s.year()); item->key = QString::number(year); item->sort_text = SortTextForNumber(year) + " "; break; + case GroupBy_OriginalYear: + year = qMax(0, s.effective_originalyear()); + item->key = QString::number(year); + item->sort_text = SortTextForNumber(year) + " "; + break; + case GroupBy_Composer: item->key = s.composer(); case GroupBy_Performer: diff --git a/src/library/librarymodel.h b/src/library/librarymodel.h index 71771eb2e..9933b5680 100644 --- a/src/library/librarymodel.h +++ b/src/library/librarymodel.h @@ -85,6 +85,8 @@ class LibraryModel : public SimpleTreeModel { GroupBy_Grouping = 10, GroupBy_Bitrate = 11, GroupBy_Disc = 12, + GroupBy_OriginalYearAlbum = 13, + GroupBy_OriginalYear = 14, }; struct Grouping { diff --git a/src/ui/organisedialog.cpp b/src/ui/organisedialog.cpp index b744d0439..29fcd5b02 100644 --- a/src/ui/organisedialog.cpp +++ b/src/ui/organisedialog.cpp @@ -70,6 +70,7 @@ OrganiseDialog::OrganiseDialog(TaskManager* task_manager, QWidget* parent) tags[tr("Disc")] = "disc"; tags[tr("BPM")] = "bpm"; tags[tr("Year")] = "year"; + tags[tr("Original year")] = "originalyear"; tags[tr("Genre")] = "genre"; tags[tr("Comment")] = "comment"; tags[tr("Length")] = "length";