diff --git a/data/data.qrc b/data/data.qrc index f447b5c36..94180a664 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -5,6 +5,7 @@ schema/schema-2.sql schema/schema-3.sql schema/schema-4.sql + schema/schema-5.sql schema/device-schema.sql style/strawberry.css html/playing-tooltip-plain.html diff --git a/data/schema/device-schema.sql b/data/schema/device-schema.sql index 09594d074..855597329 100644 --- a/data/schema/device-schema.sql +++ b/data/schema/device-schema.sql @@ -27,6 +27,10 @@ CREATE TABLE device_%deviceid_songs ( comment TEXT NOT NULL, lyrics TEXT NOT NULL, + artist_id INTEGER NOT NULL DEFAULT -1; + album_id INTEGER NOT NULL DEFAULT -1; + song_id INTEGER NOT NULL DEFAULT -1; + beginning INTEGER NOT NULL DEFAULT 0, length INTEGER NOT NULL DEFAULT 0, @@ -38,9 +42,9 @@ CREATE TABLE device_%deviceid_songs ( directory_id INTEGER NOT NULL, filename TEXT NOT NULL, filetype INTEGER NOT NULL DEFAULT 0, - filesize INTEGER NOT NULL, - mtime INTEGER NOT NULL, - ctime INTEGER NOT NULL, + filesize INTEGER NOT NULL DEFAULT 0, + mtime INTEGER NOT NULL DEFAULT 0, + ctime INTEGER NOT NULL DEFAULT 0, unavailable INTEGER DEFAULT 0, playcount INTEGER NOT NULL DEFAULT 0, diff --git a/data/schema/schema-5.sql b/data/schema/schema-5.sql new file mode 100644 index 000000000..4575a1c65 --- /dev/null +++ b/data/schema/schema-5.sql @@ -0,0 +1,31 @@ +ALTER TABLE songs ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE songs ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE songs ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_artists_songs ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_artists_songs ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_artists_songs ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_albums_songs ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_albums_songs ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_albums_songs ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_songs ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_songs ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE tidal_songs ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE playlist_items ADD COLUMN artist_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE playlist_items ADD COLUMN album_id INTEGER NOT NULL DEFAULT -1; + +ALTER TABLE playlist_items ADD COLUMN song_id INTEGER NOT NULL DEFAULT -1; + +UPDATE schema_version SET version=5; diff --git a/data/schema/schema.sql b/data/schema/schema.sql index d2a2db337..29a5e172e 100644 --- a/data/schema/schema.sql +++ b/data/schema/schema.sql @@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS schema_version ( DELETE FROM schema_version; -INSERT INTO schema_version (version) VALUES (4); +INSERT INTO schema_version (version) VALUES (5); CREATE TABLE IF NOT EXISTS directories ( path TEXT NOT NULL, @@ -35,6 +35,10 @@ CREATE TABLE IF NOT EXISTS songs ( comment TEXT NOT NULL, lyrics TEXT NOT NULL, + artist_id INTEGER NOT NULL DEFAULT -1; + album_id INTEGER NOT NULL DEFAULT -1; + song_id INTEGER NOT NULL DEFAULT -1; + beginning INTEGER NOT NULL DEFAULT 0, length INTEGER NOT NULL DEFAULT 0, @@ -88,6 +92,10 @@ CREATE TABLE IF NOT EXISTS tidal_artists_songs ( comment TEXT NOT NULL, lyrics TEXT NOT NULL, + artist_id INTEGER NOT NULL DEFAULT -1; + album_id INTEGER NOT NULL DEFAULT -1; + song_id INTEGER NOT NULL DEFAULT -1; + beginning INTEGER NOT NULL DEFAULT 0, length INTEGER NOT NULL DEFAULT 0, @@ -141,6 +149,10 @@ CREATE TABLE IF NOT EXISTS tidal_albums_songs ( comment TEXT NOT NULL, lyrics TEXT NOT NULL, + artist_id INTEGER NOT NULL DEFAULT -1; + album_id INTEGER NOT NULL DEFAULT -1; + song_id INTEGER NOT NULL DEFAULT -1; + beginning INTEGER NOT NULL DEFAULT 0, length INTEGER NOT NULL DEFAULT 0, @@ -194,6 +206,10 @@ CREATE TABLE IF NOT EXISTS tidal_songs ( comment TEXT NOT NULL, lyrics TEXT NOT NULL, + artist_id INTEGER NOT NULL DEFAULT -1; + album_id INTEGER NOT NULL DEFAULT -1; + song_id INTEGER NOT NULL DEFAULT -1; + beginning INTEGER NOT NULL DEFAULT 0, length INTEGER NOT NULL DEFAULT 0, @@ -263,6 +279,10 @@ CREATE TABLE IF NOT EXISTS playlist_items ( comment TEXT NOT NULL, lyrics TEXT NOT NULL, + artist_id INTEGER NOT NULL DEFAULT -1; + album_id INTEGER NOT NULL DEFAULT -1; + song_id INTEGER NOT NULL DEFAULT -1; + beginning INTEGER NOT NULL DEFAULT 0, length INTEGER NOT NULL DEFAULT 0, diff --git a/src/core/database.cpp b/src/core/database.cpp index 2981fb4f6..3c2c8dd23 100644 --- a/src/core/database.cpp +++ b/src/core/database.cpp @@ -52,7 +52,7 @@ #include "scopedtransaction.h" const char *Database::kDatabaseFilename = "strawberry.db"; -const int Database::kSchemaVersion = 4; +const int Database::kSchemaVersion = 5; const char *Database::kMagicAllSongsTables = "%allsongstables"; int Database::sNextConnectionId = 1; diff --git a/src/core/song.cpp b/src/core/song.cpp index 1aa8f31d9..eacdd026e 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -89,6 +89,10 @@ const QStringList Song::kColumns = QStringList() << "title" << "comment" << "lyrics" + << "artist_id" + << "album_id" + << "song_id" + << "beginning" << "length" @@ -155,7 +159,6 @@ struct Song::Private : public QSharedData { bool valid_; int id_; - int album_id_; // A unique album ID QString title_; QString album_; @@ -173,6 +176,10 @@ struct Song::Private : public QSharedData { QString comment_; QString lyrics_; + int artist_id_; + int album_id_; + int song_id_; + qint64 beginning_; qint64 end_; @@ -215,15 +222,20 @@ struct Song::Private : public QSharedData { Song::Private::Private(Song::Source source) : valid_(false), id_(-1), - album_id_(-1), + track_(-1), disc_(-1), year_(-1), originalyear_(-1), compilation_(false), + artist_id_(-1), + album_id_(-1), + song_id_(-1), + beginning_(0), end_(-1), + bitrate_(-1), samplerate_(-1), bitdepth_(-1), @@ -261,60 +273,53 @@ Song &Song::operator=(const Song &other) { bool Song::is_valid() const { return d->valid_; } bool Song::is_unavailable() const { return d->unavailable_; } int Song::id() const { return d->id_; } + +int Song::artist_id() const { return d->artist_id_; } +int Song::album_id() const { return d->album_id_; } +int Song::song_id() const { return d->song_id_; } + const QString &Song::title() const { return d->title_; } const QString &Song::album() const { return d->album_; } -const QString &Song::effective_album() const { - // This value is useful for singles, which are one-track albums on their own. - return d->album_.isEmpty() ? d->title_ : d->album_; -} +// This value is useful for singles, which are one-track albums on their own. +const QString &Song::effective_album() const { return d->album_.isEmpty() ? d->title_ : d->album_; } const QString &Song::artist() const { return d->artist_; } 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_; } 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_; -} +int Song::effective_originalyear() const { return d->originalyear_ < 0 ? d->year_ : d->originalyear_; } const QString &Song::genre() const { return d->genre_; } +bool Song::is_compilation() const { return (d->compilation_ || d->compilation_detected_ || d->compilation_on_) && ! d->compilation_off_; } +const QString &Song::composer() const { return d->composer_; } +const QString &Song::performer() const { return d->performer_; } +const QString &Song::grouping() const { return d->grouping_; } 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_; -} -int Song::playcount() const { return d->playcount_; } -int Song::skipcount() const { return d->skipcount_; } -int Song::lastplayed() const { return d->lastplayed_; } -const QString &Song::cue_path() const { return d->cue_path_; } -bool Song::has_cue() const { return !d->cue_path_.isEmpty(); } -int Song::album_id() const { return d->album_id_; } + qint64 Song::beginning_nanosec() const { return d->beginning_; } qint64 Song::end_nanosec() const { return d->end_; } qint64 Song::length_nanosec() const { return d->end_ - d->beginning_; } + int Song::bitrate() const { return d->bitrate_; } int Song::samplerate() const { return d->samplerate_; } int Song::bitdepth() const { return d->bitdepth_; } + Song::Source Song::source() const { return d->source_; } int Song::directory_id() const { return d->directory_id_; } const QUrl &Song::url() const { return d->url_; } const QString &Song::basefilename() const { return d->basefilename_; } +Song::FileType Song::filetype() const { return d->filetype_; } +int Song::filesize() const { return d->filesize_; } uint Song::mtime() const { return d->mtime_; } uint Song::ctime() const { return d->ctime_; } -int Song::filesize() const { return d->filesize_; } -Song::FileType Song::filetype() const { return d->filetype_; } -bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal; } -bool Song::is_cdda() const { return d->source_ == Source_CDDA; } -bool Song::is_collection_song() const { - return !is_cdda() && !is_stream() && id() != -1; -} -bool Song::is_metadata_good() const { - return !d->title_.isEmpty() && !d->album_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; -} + +int Song::playcount() const { return d->playcount_; } +int Song::skipcount() const { return d->skipcount_; } +int Song::lastplayed() const { return d->lastplayed_; } + const QString &Song::art_automatic() const { return d->art_automatic_; } const QString &Song::art_manual() const { return d->art_manual_; } bool Song::has_manually_unset_cover() const { return d->art_manual_ == kManuallyUnsetCover; } @@ -322,12 +327,24 @@ void Song::manually_unset_cover() { d->art_manual_ = kManuallyUnsetCover; } bool Song::has_embedded_cover() const { return d->art_automatic_ == kEmbeddedCover; } void Song::set_embedded_cover() { d->art_automatic_ = kEmbeddedCover; } const QImage &Song::image() const { return d->image_; } + +const QString &Song::cue_path() const { return d->cue_path_; } +bool Song::has_cue() const { return !d->cue_path_.isEmpty(); } + +bool Song::is_collection_song() const { return !is_cdda() && !is_stream() && id() != -1; } +bool Song::is_metadata_good() const { return !d->title_.isEmpty() && !d->album_.isEmpty() && !d->artist_.isEmpty() && !d->url_.isEmpty() && d->end_ > 0; } +bool Song::is_stream() const { return d->source_ == Source_Stream || d->source_ == Source_Tidal; } +bool Song::is_cdda() const { return d->source_ == Source_CDDA; } + const QString &Song::error() const { return d->error_; } void Song::set_id(int id) { d->id_ = id; } -void Song::set_album_id(int v) { d->album_id_ = v; } void Song::set_valid(bool v) { d->valid_ = v; } +void Song::set_artist_id(int v) { d->artist_id_ = v; } +void Song::set_album_id(int v) { d->album_id_ = v; } +void Song::set_song_id(int v) { d->song_id_ = v; } + void Song::set_title(const QString &v) { d->title_ = v; } void Song::set_album(const QString &v) { d->album_ = v; } void Song::set_artist(const QString &v) { d->artist_ = v; } @@ -674,6 +691,7 @@ void Song::ToProtobuf(pb::tagreader::SongMetadata *pb) const { pb->set_suspicious_tags(d->suspicious_tags_); pb->set_art_automatic(DataCommaSizeFromQString(d->art_automatic_)); pb->set_filetype(static_cast(d->filetype_)); + } #define tostr(n) (q.value(n).isNull() ? QString::null : q.value(n).toString()) @@ -744,6 +762,16 @@ void Song::InitFromQuery(const SqlRow &q, bool reliable_metadata, int col) { d->comment_ = tostr(x); } + else if (Song::kColumns.value(i) == "artist_id") { + d->artist_id_ = toint(x); + } + else if (Song::kColumns.value(i) == "album_id") { + d->album_id_ = toint(x); + } + else if (Song::kColumns.value(i) == "song_id") { + d->song_id_ = toint(x); + } + else if (Song::kColumns.value(i) == "beginning") { d->beginning_ = q.value(x).isNull() ? 0 : q.value(x).toLongLong(); } @@ -1093,6 +1121,10 @@ void Song::BindToQuery(QSqlQuery *query) const { query->bindValue(":comment", strval(d->comment_)); query->bindValue(":lyrics", strval(d->lyrics_)); + query->bindValue(":artist_id", intval(d->artist_id_)); + query->bindValue(":album_id", intval(d->album_id_)); + query->bindValue(":song_id", intval(d->song_id_)); + query->bindValue(":beginning", d->beginning_); query->bindValue(":length", intval(length_nanosec())); @@ -1221,17 +1253,20 @@ bool Song::IsMetadataEqual(const Song &other) const { d->album_ == other.d->album_ && 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_ && d->year_ == other.d->year_ && d->originalyear_ == other.d->originalyear_ && d->genre_ == other.d->genre_ && + d->compilation_ == other.d->compilation_ && + d->composer_ == other.d->composer_ && + d->performer_ == other.d->performer_ && + d->grouping_ == other.d->grouping_ && d->comment_ == other.d->comment_ && d->lyrics_ == other.d->lyrics_ && - d->compilation_ == other.d->compilation_ && + d->artist_id_ == other.d->artist_id_ && + d->album_id_ == other.d->album_id_ && + d->song_id_ == other.d->song_id_ && d->beginning_ == other.d->beginning_ && length_nanosec() == other.length_nanosec() && d->bitrate_ == other.d->bitrate_ && diff --git a/src/core/song.h b/src/core/song.h index d2cb23c5e..656cef12d 100644 --- a/src/core/song.h +++ b/src/core/song.h @@ -197,10 +197,9 @@ class Song { const QString &comment() const; const QString &lyrics() const; - int playcount() const; - int skipcount() const; - int lastplayed() const; + int artist_id() const; int album_id() const; + int song_id() const; qint64 beginning_nanosec() const; qint64 end_nanosec() const; @@ -219,6 +218,10 @@ class Song { uint mtime() const; uint ctime() const; + int playcount() const; + int skipcount() const; + int lastplayed() const; + const QString &art_automatic() const; const QString &art_manual() const; @@ -265,7 +268,6 @@ class Song { bool IsEditable() const; void set_id(int id); - void set_album_id(int v); void set_valid(bool v); void set_title(const QString &v); @@ -285,6 +287,10 @@ class Song { void set_comment(const QString &v); void set_lyrics(const QString &v); + void set_artist_id(int v); + void set_album_id(int v); + void set_song_id(int v); + void set_beginning_nanosec(qint64 v); void set_end_nanosec(qint64 v); void set_length_nanosec(qint64 v);