From 3d34aa240c1c9405e14ca1a59b54ab05c2fe3cab Mon Sep 17 00:00:00 2001 From: David Sansome Date: Sun, 7 Mar 2010 22:46:41 +0000 Subject: [PATCH] Add albumartist, composer, file type and date columns to the playlist. Also add columns to the database for rating, playcount, and lastplayed (not used yet). Fixes issue #66 --- data/data.qrc | 1 + data/schema-3.sql | 9 ++++ src/librarybackend.cpp | 2 +- src/playlist.cpp | 10 ++++ src/playlist.h | 5 ++ src/playlistview.cpp | 46 +++++++++++++++++ src/playlistview.h | 12 +++++ src/radioplaylistitem.cpp | 2 + src/song.cpp | 66 ++++++++++++++++++++++--- src/song.h | 101 ++++++++++++++++++++++++-------------- 10 files changed, 208 insertions(+), 46 deletions(-) create mode 100644 data/schema-3.sql diff --git a/data/data.qrc b/data/data.qrc index 30bf46669..f3c96e51e 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -65,5 +65,6 @@ view-choose.png download.png zoom-in.png + schema-3.sql diff --git a/data/schema-3.sql b/data/schema-3.sql new file mode 100644 index 000000000..6fbbe08aa --- /dev/null +++ b/data/schema-3.sql @@ -0,0 +1,9 @@ +ALTER TABLE songs ADD COLUMN filetype INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE songs ADD COLUMN playcount INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE songs ADD COLUMN lastplayed INTEGER; + +ALTER TABLE songs ADD COLUMN rating INTEGER; + +UPDATE schema_version SET version=3; diff --git a/src/librarybackend.cpp b/src/librarybackend.cpp index 63f74bf01..0d9b89aca 100644 --- a/src/librarybackend.cpp +++ b/src/librarybackend.cpp @@ -12,7 +12,7 @@ #include const char* LibraryBackend::kDatabaseName = "clementine.db"; -const int LibraryBackend::kSchemaVersion = 2; +const int LibraryBackend::kSchemaVersion = 3; LibraryBackend::LibraryBackend(QObject* parent, const QString& database_name) : QObject(parent), diff --git a/src/playlist.cpp b/src/playlist.cpp index 98c882acd..7b309c2b0 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -48,12 +48,17 @@ QVariant Playlist::headerData(int section, Qt::Orientation, int role) const { case Column_Disc: return tr("Disc"); case Column_Year: return tr("Year"); case Column_Genre: return tr("Genre"); + case Column_AlbumArtist: return tr("Album artist"); + case Column_Composer: return tr("Composer"); case Column_BPM: return tr("BPM"); case Column_Bitrate: return tr("Bit rate"); case Column_Samplerate: return tr("Sample rate"); case Column_Filename: return tr("File name"); case Column_Filesize: return tr("File size"); + case Column_Filetype: return tr("File type"); + case Column_DateModified: return tr("Date modified"); + case Column_DateCreated: return tr("Date created"); } return QVariant(); @@ -83,12 +88,17 @@ QVariant Playlist::data(const QModelIndex& index, int role) const { case Column_Disc: return song.disc(); case Column_Year: return song.year(); case Column_Genre: return song.genre(); + case Column_AlbumArtist: return song.albumartist(); + case Column_Composer: return song.composer(); case Column_BPM: return song.bpm(); case Column_Bitrate: return song.bitrate(); case Column_Samplerate: return song.samplerate(); case Column_Filename: return song.filename(); case Column_Filesize: return song.filesize(); + case Column_Filetype: return song.filetype(); + case Column_DateModified: return song.mtime(); + case Column_DateCreated: return song.ctime(); } } diff --git a/src/playlist.h b/src/playlist.h index 5efce0c87..a2e40d98a 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -22,6 +22,8 @@ class Playlist : public QAbstractListModel { Column_Title = 0, Column_Artist, Column_Album, + Column_AlbumArtist, + Column_Composer, Column_Length, Column_Track, Column_Disc, @@ -33,6 +35,9 @@ class Playlist : public QAbstractListModel { Column_Samplerate, Column_Filename, Column_Filesize, + Column_Filetype, + Column_DateCreated, + Column_DateModified, ColumnCount }; diff --git a/src/playlistview.cpp b/src/playlistview.cpp index 65d80205a..de67249ca 100644 --- a/src/playlistview.cpp +++ b/src/playlistview.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -116,6 +117,45 @@ QString SizeItemDelegate::displayText(const QVariant& value, const QLocale&) con return ret; } +QString DateItemDelegate::displayText(const QVariant &value, const QLocale &locale) const { + bool ok = false; + int time = value.toInt(&ok); + + if (!ok || time == -1) + return QString::null; + + return QDateTime::fromTime_t(time).toString( + QLocale::system().dateTimeFormat(QLocale::ShortFormat)); +} + +QString FileTypeItemDelegate::displayText(const QVariant &value, const QLocale &locale) const { + bool ok = false; + Song::FileType type = Song::FileType(value.toInt(&ok)); + + if (!ok) + return tr("Unknown"); + + switch (type) { + case Song::Type_Asf: return tr("ASF"); + case Song::Type_Flac: return tr("FLAC"); + case Song::Type_Mp4: return tr("MP4"); + case Song::Type_Mpc: return tr("MPC"); + case Song::Type_Mpeg: return tr("MP3"); // Not technically correct + case Song::Type_OggFlac: return tr("Ogg FLAC"); + case Song::Type_OggSpeex: return tr("Ogg Speex"); + case Song::Type_OggVorbis: return tr("Ogg Vorbis"); + case Song::Type_Aiff: return tr("AIFF"); + case Song::Type_Wav: return tr("WAV"); + case Song::Type_TrueAudio: return tr("TrueAudio"); + + case Song::Type_Stream: return tr("Stream"); + + case Song::Type_Unknown: + default: + return tr("Unknown"); + } +} + PlaylistView::PlaylistView(QWidget *parent) : QTreeView(parent), @@ -129,6 +169,9 @@ PlaylistView::PlaylistView(QWidget *parent) setItemDelegate(new PlaylistDelegateBase(this)); setItemDelegateForColumn(Playlist::Column_Length, new LengthItemDelegate(this)); setItemDelegateForColumn(Playlist::Column_Filesize, new SizeItemDelegate(this)); + setItemDelegateForColumn(Playlist::Column_Filetype, new FileTypeItemDelegate(this)); + setItemDelegateForColumn(Playlist::Column_DateCreated, new DateItemDelegate(this)); + setItemDelegateForColumn(Playlist::Column_DateModified, new DateItemDelegate(this)); setHeader(new PlaylistHeader(Qt::Horizontal, this)); header()->setMovable(true); @@ -158,6 +201,9 @@ void PlaylistView::LoadGeometry() { header()->hideSection(Playlist::Column_Samplerate); header()->hideSection(Playlist::Column_Filename); header()->hideSection(Playlist::Column_Filesize); + header()->hideSection(Playlist::Column_Filetype); + header()->hideSection(Playlist::Column_DateCreated); + header()->hideSection(Playlist::Column_DateModified); } } diff --git a/src/playlistview.h b/src/playlistview.h index 6b185f705..ec3b26449 100644 --- a/src/playlistview.h +++ b/src/playlistview.h @@ -30,6 +30,18 @@ class SizeItemDelegate : public PlaylistDelegateBase { QString displayText(const QVariant& value, const QLocale& locale) const; }; +class DateItemDelegate : public PlaylistDelegateBase { + public: + DateItemDelegate(QTreeView* view) : PlaylistDelegateBase(view) {} + QString displayText(const QVariant& value, const QLocale& locale) const; +}; + +class FileTypeItemDelegate : public PlaylistDelegateBase { + public: + FileTypeItemDelegate(QTreeView* view) : PlaylistDelegateBase(view) {} + QString displayText(const QVariant& value, const QLocale& locale) const; +}; + class PlaylistView : public QTreeView { Q_OBJECT diff --git a/src/radioplaylistitem.cpp b/src/radioplaylistitem.cpp index d1cb6b2a2..0ea1cb3fa 100644 --- a/src/radioplaylistitem.cpp +++ b/src/radioplaylistitem.cpp @@ -45,6 +45,7 @@ void RadioPlaylistItem::InitMetadata() { metadata_.set_title(url_.toString()); metadata_.set_artist(artist_); + metadata_.set_filetype(Song::Type_Stream); } Song RadioPlaylistItem::Metadata() const { @@ -84,6 +85,7 @@ PlaylistItem::Options RadioPlaylistItem::options() const { void RadioPlaylistItem::SetTemporaryMetadata(const Song& metadata) { temp_metadata_ = metadata; + temp_metadata_.set_filetype(Song::Type_Stream); } void RadioPlaylistItem::ClearTemporaryMetadata() { diff --git a/src/song.cpp b/src/song.cpp index d39c09d5f..142118f1c 100644 --- a/src/song.cpp +++ b/src/song.cpp @@ -8,8 +8,16 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include #include @@ -30,13 +38,15 @@ const char* Song::kColumnSpec = "title, album, artist, albumartist, composer, " "track, disc, bpm, year, genre, comment, compilation, " "length, bitrate, samplerate, directory, filename, " - "mtime, ctime, filesize, sampler, art_automatic, art_manual"; + "mtime, ctime, filesize, sampler, art_automatic, art_manual, " + "filetype, playcount, lastplayed, rating"; const char* Song::kBindSpec = ":title, :album, :artist, :albumartist, :composer, " ":track, :disc, :bpm, :year, :genre, :comment, :compilation, " ":length, :bitrate, :samplerate, :directory_id, :filename, " - ":mtime, :ctime, :filesize, :sampler, :art_automatic, :art_manual"; + ":mtime, :ctime, :filesize, :sampler, :art_automatic, :art_manual, " + ":filetype, :playcount, :lastplayed, :rating"; const char* Song::kUpdateSpec = "title = :title, album = :album, artist = :artist, " @@ -46,11 +56,13 @@ const char* Song::kUpdateSpec = "bitrate = :bitrate, samplerate = :samplerate, " "directory = :directory_id, filename = :filename, mtime = :mtime, " "ctime = :ctime, filesize = :filesize, sampler = :sampler, " - "art_automatic = :art_automatic, art_manual = :art_manual"; + "art_automatic = :art_automatic, art_manual = :art_manual, " + "filetype = :filetype, playcount = :playcount, lastplayed = :lastplayed, " + "rating = :rating"; TagLibFileRefFactory Song::kDefaultFactory; -SongData::SongData() +Song::Private::Private() : valid_(false), id_(-1), track_(-1), @@ -65,7 +77,8 @@ SongData::SongData() directory_id_(-1), mtime_(-1), ctime_(-1), - filesize_(-1) + filesize_(-1), + filetype_(Type_Unknown) { } @@ -74,7 +87,7 @@ TagLib::FileRef* TagLibFileRefFactory::GetFileRef(const QString& filename) { } Song::Song() - : d(new SongData), + : d(new Private), factory_(&kDefaultFactory) { } @@ -86,7 +99,7 @@ Song::Song(const Song &other) } Song::Song(FileRefFactory* factory) - : d(new SongData), + : d(new Private), factory_(factory) { } @@ -200,6 +213,34 @@ void Song::InitFromFile(const QString& filename, int directory_id) { d->length_ = fileref->audioProperties()->length(); d->samplerate_ = fileref->audioProperties()->sampleRate(); } + + // Get the filetype if we can + GuessFileType(fileref.get()); +} + +void Song::GuessFileType(TagLib::FileRef* fileref) { + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_Asf; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_Flac; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_Mp4; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_Mpc; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_Mpeg; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_OggFlac; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_OggSpeex; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_OggVorbis; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_Aiff; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_Wav; + if (dynamic_cast(fileref->file())) + d->filetype_ = Type_TrueAudio; } void Song::InitFromQuery(const QSqlQuery& q) { @@ -241,6 +282,11 @@ void Song::InitFromQuery(const QSqlQuery& q) { d->art_automatic_ = q.value(22).toString(); d->art_manual_ = q.value(23).toString(); + d->filetype_ = FileType(q.value(24).toInt()); + // playcount = 25 + // lastplayed = 26 + // rating = 27 + #undef tostr #undef toint #undef tofloat @@ -248,6 +294,7 @@ void Song::InitFromQuery(const QSqlQuery& q) { void Song::InitFromLastFM(const lastfm::Track& track) { d->valid_ = true; + d->filetype_ = Type_Stream; d->title_ = track.title(); d->album_ = track.album(); @@ -303,6 +350,11 @@ void Song::BindToQuery(QSqlQuery *query) const { query->bindValue(":art_automatic", d->art_automatic_); query->bindValue(":art_manual", d->art_manual_); + query->bindValue(":filetype", d->filetype_); + query->bindValue(":playcount", 0); // TODO + query->bindValue(":lastplayed", -1); // TODO + query->bindValue(":rating", -1); + #undef intval } diff --git a/src/song.h b/src/song.h index 940f7d05d..d6282a626 100644 --- a/src/song.h +++ b/src/song.h @@ -19,43 +19,6 @@ namespace TagLib { class FileRef; } -struct SongData : public QSharedData { - SongData(); - - bool valid_; - int id_; - - QString title_; - QString album_; - QString artist_; - QString albumartist_; - QString composer_; - int track_; - int disc_; - float bpm_; - int year_; - QString genre_; - QString comment_; - bool compilation_; - bool sampler_; - - int length_; - int bitrate_; - int samplerate_; - - int directory_id_; - QString filename_; - int mtime_; - int ctime_; - int filesize_; - - // Filenames to album art for this song. - QString art_automatic_; // Guessed by LibraryWatcher - QString art_manual_; // Set by the user - should take priority - - QImage image_; -}; - class FileRefFactory { public: virtual ~FileRefFactory() {} @@ -77,6 +40,24 @@ class Song { static const char* kBindSpec; static const char* kUpdateSpec; + // Don't change these values - they're stored in the database + enum FileType { + Type_Unknown = 0, + Type_Asf = 1, + Type_Flac = 2, + Type_Mp4 = 3, + Type_Mpc = 4, + Type_Mpeg = 5, + Type_OggFlac = 6, + Type_OggSpeex = 7, + Type_OggVorbis = 8, + Type_Aiff = 9, + Type_Wav = 10, + Type_TrueAudio = 11, + + Type_Stream = 99, + }; + // Constructors void Init(const QString& title, const QString& artist, int length); void InitFromFile(const QString& filename, int directory_id); @@ -114,6 +95,7 @@ class Song { uint mtime() const { return d->mtime_; } uint ctime() const { return d->ctime_; } int filesize() const { return d->filesize_; } + FileType filetype() const { return d->filetype_; } const QString& art_automatic() const { return d->art_automatic_; } const QString& art_manual() const { return d->art_manual_; } @@ -156,6 +138,7 @@ class Song { void set_mtime(int v) { d->mtime_ = v; } void set_ctime(int v) { d->ctime_ = v; } void set_filesize(int v) { d->filesize_ = v; } + void set_filetype(FileType v) { d->filetype_ = v; } void set_art_automatic(const QString& v) { d->art_automatic_ = v; } void set_art_manual(const QString& v) { d->art_manual_ = v; } void set_image(const QImage& i) { d->image_ = i; } @@ -168,7 +151,49 @@ class Song { bool IsMetadataEqual(const Song& other) const; private: - QSharedDataPointer d; + void GuessFileType(TagLib::FileRef* fileref); + + private: + struct Private : public QSharedData { + Private(); + + bool valid_; + int id_; + + QString title_; + QString album_; + QString artist_; + QString albumartist_; + QString composer_; + int track_; + int disc_; + float bpm_; + int year_; + QString genre_; + QString comment_; + bool compilation_; + bool sampler_; + + int length_; + int bitrate_; + int samplerate_; + + int directory_id_; + QString filename_; + int mtime_; + int ctime_; + int filesize_; + FileType filetype_; + + // Filenames to album art for this song. + QString art_automatic_; // Guessed by LibraryWatcher + QString art_manual_; // Set by the user - should take priority + + QImage image_; + }; + + private: + QSharedDataPointer d; FileRefFactory* factory_; static TagLibFileRefFactory kDefaultFactory;