Handle statistics tags apart, and write them to file when they change.

Next step: add a preference to activate this (should be deactivated by default IMO).
This commit is contained in:
Arnaud Bienner 2013-02-24 17:45:25 +01:00
parent 39e1275980
commit 08d40f5fca
7 changed files with 81 additions and 2 deletions

View File

@ -48,6 +48,11 @@ void TagReaderWorker::MessageArrived(const pb::tagreader::Message& message) {
reply.mutable_save_file_response()->set_success(
tag_reader_.SaveFile(QStringFromStdString(message.save_file_request().filename()),
message.save_file_request().metadata()));
} else if (message.has_save_song_statistics_to_file_request()) {
reply.mutable_save_song_statistics_to_file_response()->set_success(
tag_reader_.SaveSongStatisticsToFile(
QStringFromStdString(message.save_song_statistics_to_file_request().filename()),
message.save_song_statistics_to_file_request().metadata()));
} else if (message.has_is_media_file_request()) {
reply.mutable_is_media_file_response()->set_success(
tag_reader_.IsMediaFile(QStringFromStdString(message.is_media_file_request().filename())));

View File

@ -428,8 +428,6 @@ bool TagReader::SaveFile(const QString& filename,
SetTextFrame("TCOM", song.composer(), tag);
SetTextFrame("TPE2", song.albumartist(), tag);
SetTextFrame("TCMP", std::string(song.compilation() ? "1" : "0"), tag);
SetUserTextFrame("FMPS_Rating", QString::number(song.rating()), tag);
SetUserTextFrame("FMPS_PlayCount", QString::number(song.playcount()), tag);
}
else if (TagLib::Ogg::Vorbis::File* file = dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file())) {
TagLib::Ogg::XiphComment* tag = file->tag();
@ -474,10 +472,43 @@ bool TagReader::SaveFile(const QString& filename,
return ret;
}
bool TagReader::SaveSongStatisticsToFile(const QString& filename,
const pb::tagreader::SongMetadata& song) const {
if (filename.isNull())
return false;
qLog(Debug) << "Saving song statistics tags to" << filename;
scoped_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
if (!fileref || fileref->isNull()) // The file probably doesn't exist
return false;
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
SetUserTextFrame("FMPS_Rating", QString::number(song.rating()), tag);
SetUserTextFrame("FMPS_PlayCount", QString::number(song.playcount()), tag);
} else {
// Nothing to save: stop now
return true;
}
bool ret = fileref->save();
#ifdef Q_OS_LINUX
if (ret) {
// Linux: inotify doesn't seem to notice the change to the file unless we
// change the timestamps as well. (this is what touch does)
utimensat(0, QFile::encodeName(filename).constData(), NULL, 0);
}
#endif // Q_OS_LINUX
return ret;
}
void TagReader::SetUserTextFrame(const QString& description, const QString& value,
TagLib::ID3v2::Tag* tag) const {
const QByteArray descr_utf8(description.toUtf8());
const QByteArray value_utf8(value.toUtf8());
qLog(Debug) << "Setting FMPSFrame:" << description << ", " << value;
SetUserTextFrame(std::string(descr_utf8.constData(), descr_utf8.length()),
std::string(value_utf8.constData(), value_utf8.length()),
tag);

View File

@ -53,6 +53,10 @@ class TagReader {
void ReadFile(const QString& filename, pb::tagreader::SongMetadata* song) const;
bool SaveFile(const QString& filename, const pb::tagreader::SongMetadata& song) const;
// Returns false if something went wrong; returns true otherwise (might
// returns true if the file exists but nothing has been written inside because
// statistics tag format is not supported for this kind of file)
bool SaveSongStatisticsToFile(const QString& filename, const pb::tagreader::SongMetadata& song) const;
bool IsMediaFile(const QString& filename) const;
QByteArray LoadEmbeddedArt(const QString& filename) const;

View File

@ -96,6 +96,16 @@ message ReadCloudFileResponse {
optional SongMetadata metadata = 1;
}
message SaveSongStatisticsToFileRequest {
optional string filename = 1;
optional SongMetadata metadata = 2;
}
message SaveSongStatisticsToFileResponse {
optional bool success = 1;
}
message Message {
optional int32 id = 1;
@ -113,4 +123,8 @@ message Message {
optional ReadCloudFileRequest read_cloud_file_request = 10;
optional ReadCloudFileResponse read_cloud_file_response = 11;
optional SaveSongStatisticsToFileRequest save_song_statistics_to_file_request = 12;
optional SaveSongStatisticsToFileResponse save_song_statistics_to_file_response = 13;
}

View File

@ -68,6 +68,24 @@ TagReaderReply* TagReaderClient::SaveFile(const QString& filename, const Song& m
return worker_pool_->SendMessageWithReply(&message);
}
TagReaderReply* TagReaderClient::UpdateSongStatistics(const Song& metadata) {
pb::tagreader::Message message;
pb::tagreader::SaveSongStatisticsToFileRequest* req =
message.mutable_save_song_statistics_to_file_request();
req->set_filename(DataCommaSizeFromQString(metadata.url().toLocalFile()));
metadata.ToProtobuf(req->mutable_metadata());
return worker_pool_->SendMessageWithReply(&message);
}
void TagReaderClient::UpdateSongsStatistics(const SongList& songs) {
for (const Song& song : songs) {
TagReaderReply* reply = UpdateSongStatistics(song);
connect(reply, SIGNAL(Finished(bool)), reply, SLOT(deleteLater()));
}
}
TagReaderReply* TagReaderClient::IsMediaFile(const QString& filename) {
pb::tagreader::Message message;
pb::tagreader::IsMediaFileRequest* req = message.mutable_is_media_file_request();

View File

@ -43,6 +43,7 @@ public:
ReplyType* ReadFile(const QString& filename);
ReplyType* SaveFile(const QString& filename, const Song& metadata);
ReplyType* UpdateSongStatistics(const Song& metadata);
ReplyType* IsMediaFile(const QString& filename);
ReplyType* LoadEmbeddedArt(const QString& filename);
ReplyType* ReadCloudFile(const QUrl& download_url,
@ -62,6 +63,9 @@ public:
// TODO: Make this not a singleton
static TagReaderClient* Instance() { return sInstance; }
public slots:
void UpdateSongsStatistics(const SongList& songs);
private slots:
void WorkerFailedToStart();

View File

@ -20,6 +20,7 @@
#include "sqlrow.h"
#include "core/database.h"
#include "core/scopedtransaction.h"
#include "core/tagreaderclient.h"
#include "smartplaylists/search.h"
#include <QCoreApplication>
@ -48,6 +49,8 @@ void LibraryBackend::Init(Database* db, const QString& songs_table,
dirs_table_ = dirs_table;
subdirs_table_ = subdirs_table;
fts_table_ = fts_table;
connect(this, SIGNAL(SongsStatisticsChanged(SongList)),
TagReaderClient::Instance(), SLOT(UpdateSongsStatistics(SongList)));
}
void LibraryBackend::LoadDirectoriesAsync() {