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
This commit is contained in:
David Sansome 2010-03-07 22:46:41 +00:00
parent 030e454d1e
commit 3d34aa240c
10 changed files with 208 additions and 46 deletions

View File

@ -65,5 +65,6 @@
<file>view-choose.png</file>
<file>download.png</file>
<file>zoom-in.png</file>
<file>schema-3.sql</file>
</qresource>
</RCC>

9
data/schema-3.sql Normal file
View File

@ -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;

View File

@ -12,7 +12,7 @@
#include <QThread>
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),

View File

@ -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();
}
}

View File

@ -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
};

View File

@ -11,6 +11,7 @@
#include <QKeyEvent>
#include <QMenu>
#include <QScrollBar>
#include <QDateTime>
#include <math.h>
@ -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);
}
}

View File

@ -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

View File

@ -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() {

View File

@ -8,8 +8,16 @@
#include <taglib/mpegfile.h>
#include <taglib/id3v2tag.h>
#include <taglib/oggfile.h>
#include <taglib/oggflacfile.h>
#include <taglib/vorbisfile.h>
#include <taglib/flacfile.h>
#include <taglib/asffile.h>
#include <taglib/mp4file.h>
#include <taglib/mpcfile.h>
#include <taglib/aifffile.h>
#include <taglib/wavfile.h>
#include <taglib/speexfile.h>
#include <taglib/trueaudiofile.h>
#include <lastfm/Track>
@ -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<TagLib::ASF::File*>(fileref->file()))
d->filetype_ = Type_Asf;
if (dynamic_cast<TagLib::FLAC::File*>(fileref->file()))
d->filetype_ = Type_Flac;
if (dynamic_cast<TagLib::MP4::File*>(fileref->file()))
d->filetype_ = Type_Mp4;
if (dynamic_cast<TagLib::MPC::File*>(fileref->file()))
d->filetype_ = Type_Mpc;
if (dynamic_cast<TagLib::MPEG::File*>(fileref->file()))
d->filetype_ = Type_Mpeg;
if (dynamic_cast<TagLib::Ogg::FLAC::File*>(fileref->file()))
d->filetype_ = Type_OggFlac;
if (dynamic_cast<TagLib::Ogg::Speex::File*>(fileref->file()))
d->filetype_ = Type_OggSpeex;
if (dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file()))
d->filetype_ = Type_OggVorbis;
if (dynamic_cast<TagLib::RIFF::AIFF::File*>(fileref->file()))
d->filetype_ = Type_Aiff;
if (dynamic_cast<TagLib::RIFF::WAV::File*>(fileref->file()))
d->filetype_ = Type_Wav;
if (dynamic_cast<TagLib::TrueAudio::File*>(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
}

View File

@ -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<SongData> 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<Private> d;
FileRefFactory* factory_;
static TagLibFileRefFactory kDefaultFactory;