2010-03-24 00:11:46 +01:00
|
|
|
/* This file is part of Clementine.
|
2010-11-20 14:27:10 +01:00
|
|
|
Copyright 2010, David Sansome <me@davidsansome.com>
|
2010-03-24 00:11:46 +01:00
|
|
|
|
|
|
|
Clementine is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Clementine is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
#include "librarybackend.h"
|
|
|
|
#include "libraryquery.h"
|
2010-08-03 20:57:17 +02:00
|
|
|
#include "sqlrow.h"
|
2013-10-03 17:08:42 +02:00
|
|
|
#include "core/application.h"
|
2010-05-10 23:50:31 +02:00
|
|
|
#include "core/database.h"
|
|
|
|
#include "core/scopedtransaction.h"
|
2013-02-24 17:45:25 +01:00
|
|
|
#include "core/tagreaderclient.h"
|
2013-10-03 17:08:42 +02:00
|
|
|
#include "core/utilities.h"
|
2010-11-18 21:19:33 +01:00
|
|
|
#include "smartplaylists/search.h"
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2011-03-13 15:43:03 +01:00
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QDateTime>
|
2009-12-24 20:16:07 +01:00
|
|
|
#include <QDir>
|
2011-03-13 15:43:03 +01:00
|
|
|
#include <QFileInfo>
|
2009-12-24 20:16:07 +01:00
|
|
|
#include <QSettings>
|
2011-03-13 15:43:03 +01:00
|
|
|
#include <QVariant>
|
2009-12-24 20:16:07 +01:00
|
|
|
#include <QtDebug>
|
|
|
|
|
2013-02-24 18:36:37 +01:00
|
|
|
const char* LibraryBackend::kSettingsGroup = "LibraryBackend";
|
|
|
|
|
2010-11-01 22:15:52 +01:00
|
|
|
const char* LibraryBackend::kNewScoreSql =
|
|
|
|
"case when playcount <= 0 then (%1 * 100 + score) / 2"
|
2014-02-07 16:34:20 +01:00
|
|
|
" else (score * (playcount + skipcount) + %1 * 100) / (playcount + "
|
|
|
|
"skipcount + 1)"
|
2010-11-01 22:15:52 +01:00
|
|
|
" end";
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
LibraryBackend::LibraryBackend(QObject* parent)
|
|
|
|
: LibraryBackendInterface(parent),
|
|
|
|
save_statistics_in_file_(false),
|
|
|
|
save_ratings_in_file_(false) {}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2012-02-12 14:41:50 +01:00
|
|
|
void LibraryBackend::Init(Database* db, const QString& songs_table,
|
2014-02-07 16:34:20 +01:00
|
|
|
const QString& dirs_table,
|
|
|
|
const QString& subdirs_table,
|
2010-06-20 18:30:10 +02:00
|
|
|
const QString& fts_table) {
|
2010-06-02 17:58:07 +02:00
|
|
|
db_ = db;
|
|
|
|
songs_table_ = songs_table;
|
|
|
|
dirs_table_ = dirs_table;
|
|
|
|
subdirs_table_ = subdirs_table;
|
2010-06-20 18:30:10 +02:00
|
|
|
fts_table_ = fts_table;
|
2010-06-02 17:58:07 +02:00
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
void LibraryBackend::LoadDirectoriesAsync() {
|
|
|
|
metaObject()->invokeMethod(this, "LoadDirectories", Qt::QueuedConnection);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LibraryBackend::UpdateTotalSongCountAsync() {
|
2014-02-07 16:34:20 +01:00
|
|
|
metaObject()->invokeMethod(this, "UpdateTotalSongCount",
|
|
|
|
Qt::QueuedConnection);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2010-10-17 21:27:31 +02:00
|
|
|
void LibraryBackend::IncrementPlayCountAsync(int id) {
|
|
|
|
metaObject()->invokeMethod(this, "IncrementPlayCount", Qt::QueuedConnection,
|
|
|
|
Q_ARG(int, id));
|
|
|
|
}
|
|
|
|
|
2010-11-01 22:15:52 +01:00
|
|
|
void LibraryBackend::IncrementSkipCountAsync(int id, float progress) {
|
2010-10-17 21:27:31 +02:00
|
|
|
metaObject()->invokeMethod(this, "IncrementSkipCount", Qt::QueuedConnection,
|
2010-11-01 22:15:52 +01:00
|
|
|
Q_ARG(int, id), Q_ARG(float, progress));
|
2010-10-17 21:27:31 +02:00
|
|
|
}
|
|
|
|
|
2010-12-25 12:54:21 +01:00
|
|
|
void LibraryBackend::ResetStatisticsAsync(int id) {
|
|
|
|
metaObject()->invokeMethod(this, "ResetStatistics", Qt::QueuedConnection,
|
|
|
|
Q_ARG(int, id));
|
|
|
|
}
|
|
|
|
|
2010-10-17 22:53:48 +02:00
|
|
|
void LibraryBackend::UpdateSongRatingAsync(int id, float rating) {
|
|
|
|
metaObject()->invokeMethod(this, "UpdateSongRating", Qt::QueuedConnection,
|
|
|
|
Q_ARG(int, id), Q_ARG(float, rating));
|
|
|
|
}
|
|
|
|
|
2014-04-26 01:41:04 +02:00
|
|
|
void LibraryBackend::UpdateSongsRatingAsync(const QList<int>& ids,
|
|
|
|
float rating) {
|
2014-04-20 03:07:44 +02:00
|
|
|
metaObject()->invokeMethod(this, "UpdateSongsRating", Qt::QueuedConnection,
|
2014-04-26 01:41:04 +02:00
|
|
|
Q_ARG(const QList<int>&, ids),
|
|
|
|
Q_ARG(float, rating));
|
2014-04-20 03:07:44 +02:00
|
|
|
}
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
void LibraryBackend::LoadDirectories() {
|
2010-07-04 14:10:44 +02:00
|
|
|
DirectoryList dirs = GetAllDirectories();
|
|
|
|
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const Directory& dir : dirs) {
|
2010-07-04 14:10:44 +02:00
|
|
|
emit DirectoryDiscovered(dir, SubdirsInDirectory(dir.id, db));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-28 14:27:53 +02:00
|
|
|
void LibraryBackend::ChangeDirPath(int id, const QString& old_path,
|
|
|
|
const QString& new_path) {
|
2010-07-04 14:10:44 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
ScopedTransaction t(&db);
|
|
|
|
|
|
|
|
// Do the dirs table
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(
|
|
|
|
QString("UPDATE %1 SET path=:path WHERE ROWID=:id").arg(dirs_table_), db);
|
2010-07-04 14:10:44 +02:00
|
|
|
q.bindValue(":path", new_path);
|
|
|
|
q.bindValue(":id", id);
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-07-04 14:10:44 +02:00
|
|
|
|
2011-04-28 14:27:53 +02:00
|
|
|
const QByteArray old_url = QUrl::fromLocalFile(old_path).toEncoded();
|
|
|
|
const QByteArray new_url = QUrl::fromLocalFile(new_path).toEncoded();
|
|
|
|
|
|
|
|
const int path_len = old_url.length();
|
2010-07-04 14:10:44 +02:00
|
|
|
|
|
|
|
// Do the subdirs table
|
2014-02-07 16:34:20 +01:00
|
|
|
q = QSqlQuery(QString(
|
|
|
|
"UPDATE %1 SET path=:path || substr(path, %2)"
|
|
|
|
" WHERE directory=:id")
|
|
|
|
.arg(subdirs_table_)
|
|
|
|
.arg(path_len),
|
|
|
|
db);
|
2011-04-28 14:27:53 +02:00
|
|
|
q.bindValue(":path", new_url);
|
2010-07-04 14:10:44 +02:00
|
|
|
q.bindValue(":id", id);
|
2009-12-24 20:16:07 +01:00
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-07-04 14:10:44 +02:00
|
|
|
// Do the songs table
|
2014-02-07 16:34:20 +01:00
|
|
|
q = QSqlQuery(QString(
|
|
|
|
"UPDATE %1 SET filename=:path || substr(filename, %2)"
|
|
|
|
" WHERE directory=:id")
|
|
|
|
.arg(songs_table_)
|
|
|
|
.arg(path_len),
|
|
|
|
db);
|
2011-04-28 14:27:53 +02:00
|
|
|
q.bindValue(":path", new_url);
|
2010-07-04 14:10:44 +02:00
|
|
|
q.bindValue(":id", id);
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-07-04 14:10:44 +02:00
|
|
|
|
|
|
|
t.Commit();
|
|
|
|
}
|
|
|
|
|
|
|
|
DirectoryList LibraryBackend::GetAllDirectories() {
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
DirectoryList ret;
|
|
|
|
|
|
|
|
QSqlQuery q(QString("SELECT ROWID, path FROM %1").arg(dirs_table_), db);
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return ret;
|
2010-07-04 14:10:44 +02:00
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
while (q.next()) {
|
|
|
|
Directory dir;
|
|
|
|
dir.id = q.value(0).toInt();
|
|
|
|
dir.path = q.value(1).toString();
|
2010-04-01 18:59:32 +02:00
|
|
|
|
2010-07-04 14:10:44 +02:00
|
|
|
ret << dir;
|
2010-04-01 18:59:32 +02:00
|
|
|
}
|
2010-07-04 14:10:44 +02:00
|
|
|
return ret;
|
2010-04-01 18:59:32 +02:00
|
|
|
}
|
|
|
|
|
2010-04-04 16:59:55 +02:00
|
|
|
SubdirectoryList LibraryBackend::SubdirsInDirectory(int id) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db = db_->Connect();
|
2010-04-04 16:59:55 +02:00
|
|
|
return SubdirsInDirectory(id, db);
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
SubdirectoryList LibraryBackend::SubdirsInDirectory(int id, QSqlDatabase& db) {
|
|
|
|
QSqlQuery q(QString(
|
|
|
|
"SELECT path, mtime FROM %1"
|
|
|
|
" WHERE directory = :dir").arg(subdirs_table_),
|
|
|
|
db);
|
2010-04-01 18:59:32 +02:00
|
|
|
q.bindValue(":dir", id);
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return SubdirectoryList();
|
2010-04-01 18:59:32 +02:00
|
|
|
|
|
|
|
SubdirectoryList subdirs;
|
|
|
|
while (q.next()) {
|
|
|
|
Subdirectory subdir;
|
|
|
|
subdir.directory_id = id;
|
|
|
|
subdir.path = q.value(0).toString();
|
|
|
|
subdir.mtime = q.value(1).toUInt();
|
|
|
|
subdirs << subdir;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-04-01 18:59:32 +02:00
|
|
|
|
|
|
|
return subdirs;
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void LibraryBackend::UpdateTotalSongCount() {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(QString("SELECT COUNT(*) FROM %1 WHERE unavailable = 0")
|
|
|
|
.arg(songs_table_),
|
|
|
|
db);
|
2009-12-24 20:16:07 +01:00
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2009-12-24 20:16:07 +01:00
|
|
|
if (!q.next()) return;
|
|
|
|
|
|
|
|
emit TotalSongCountUpdated(q.value(0).toInt());
|
|
|
|
}
|
|
|
|
|
2011-03-13 15:43:03 +01:00
|
|
|
void LibraryBackend::AddDirectory(const QString& path) {
|
2013-10-03 17:08:42 +02:00
|
|
|
QString canonical_path = QFileInfo(path).canonicalFilePath();
|
|
|
|
QString db_path = canonical_path;
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (Application::kIsPortable && Utilities::UrlOnSameDriveAsClementine(
|
|
|
|
QUrl::fromLocalFile(canonical_path))) {
|
|
|
|
db_path = Utilities::GetRelativePathToClementineBin(
|
|
|
|
QUrl::fromLocalFile(db_path)).toLocalFile();
|
2013-10-03 17:08:42 +02:00
|
|
|
qLog(Debug) << "db_path" << db_path;
|
|
|
|
}
|
2011-03-13 15:43:03 +01:00
|
|
|
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(QString(
|
|
|
|
"INSERT INTO %1 (path, subdirs)"
|
|
|
|
" VALUES (:path, 1)").arg(dirs_table_),
|
|
|
|
db);
|
2013-10-03 17:08:42 +02:00
|
|
|
q.bindValue(":path", db_path);
|
2009-12-24 20:16:07 +01:00
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
Directory dir;
|
2011-03-13 15:43:03 +01:00
|
|
|
dir.path = canonical_path;
|
2009-12-24 20:16:07 +01:00
|
|
|
dir.id = q.lastInsertId().toInt();
|
|
|
|
|
2010-04-01 18:59:32 +02:00
|
|
|
emit DirectoryDiscovered(dir, SubdirectoryList());
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void LibraryBackend::RemoveDirectory(const Directory& dir) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
// Remove songs first
|
|
|
|
DeleteSongs(FindSongsInDirectory(dir.id));
|
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
ScopedTransaction transaction(&db);
|
2010-04-01 18:59:32 +02:00
|
|
|
|
|
|
|
// Delete the subdirs that were in this directory
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(
|
|
|
|
QString("DELETE FROM %1 WHERE directory = :id").arg(subdirs_table_), db);
|
2009-12-24 20:16:07 +01:00
|
|
|
q.bindValue(":id", dir.id);
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-01 18:59:32 +02:00
|
|
|
// Now remove the directory itself
|
2014-02-07 16:34:20 +01:00
|
|
|
q = QSqlQuery(QString("DELETE FROM %1 WHERE ROWID = :id").arg(dirs_table_),
|
|
|
|
db);
|
2010-04-01 18:59:32 +02:00
|
|
|
q.bindValue(":id", dir.id);
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-04-01 18:59:32 +02:00
|
|
|
|
|
|
|
emit DirectoryDeleted(dir);
|
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
transaction.Commit();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
SongList LibraryBackend::FindSongsInDirectory(int id) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(
|
|
|
|
QString("SELECT ROWID, " + Song::kColumnSpec +
|
|
|
|
" FROM %1 WHERE directory = :directory").arg(songs_table_),
|
|
|
|
db);
|
2009-12-24 20:16:07 +01:00
|
|
|
q.bindValue(":directory", id);
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return SongList();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
SongList ret;
|
|
|
|
while (q.next()) {
|
|
|
|
Song song;
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(q, true);
|
2009-12-24 20:16:07 +01:00
|
|
|
ret << song;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-04 16:59:55 +02:00
|
|
|
void LibraryBackend::AddOrUpdateSubdirs(const SubdirectoryList& subdirs) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery find_query(
|
|
|
|
QString(
|
|
|
|
"SELECT ROWID FROM %1"
|
|
|
|
" WHERE directory = :id AND path = :path").arg(subdirs_table_),
|
|
|
|
db);
|
|
|
|
QSqlQuery add_query(QString(
|
|
|
|
"INSERT INTO %1 (directory, path, mtime)"
|
|
|
|
" VALUES (:id, :path, :mtime)").arg(subdirs_table_),
|
|
|
|
db);
|
|
|
|
QSqlQuery update_query(
|
|
|
|
QString(
|
|
|
|
"UPDATE %1 SET mtime = :mtime"
|
|
|
|
" WHERE directory = :id AND path = :path").arg(subdirs_table_),
|
|
|
|
db);
|
|
|
|
QSqlQuery delete_query(
|
|
|
|
QString(
|
|
|
|
"DELETE FROM %1"
|
|
|
|
" WHERE directory = :id AND path = :path").arg(subdirs_table_),
|
|
|
|
db);
|
2010-04-01 18:59:32 +02:00
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
ScopedTransaction transaction(&db);
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const Subdirectory& subdir : subdirs) {
|
2010-04-04 16:59:55 +02:00
|
|
|
if (subdir.mtime == 0) {
|
|
|
|
// Delete the subdirectory
|
|
|
|
delete_query.bindValue(":id", subdir.directory_id);
|
|
|
|
delete_query.bindValue(":path", subdir.path);
|
|
|
|
delete_query.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
db_->CheckErrors(delete_query);
|
2010-04-04 16:59:55 +02:00
|
|
|
} else {
|
|
|
|
// See if this subdirectory already exists in the database
|
|
|
|
find_query.bindValue(":id", subdir.directory_id);
|
|
|
|
find_query.bindValue(":path", subdir.path);
|
|
|
|
find_query.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(find_query)) continue;
|
2010-04-04 16:59:55 +02:00
|
|
|
|
|
|
|
if (find_query.next()) {
|
|
|
|
update_query.bindValue(":mtime", subdir.mtime);
|
|
|
|
update_query.bindValue(":id", subdir.directory_id);
|
|
|
|
update_query.bindValue(":path", subdir.path);
|
|
|
|
update_query.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
db_->CheckErrors(update_query);
|
2010-04-04 16:59:55 +02:00
|
|
|
} else {
|
|
|
|
add_query.bindValue(":id", subdir.directory_id);
|
|
|
|
add_query.bindValue(":path", subdir.path);
|
|
|
|
add_query.bindValue(":mtime", subdir.mtime);
|
|
|
|
add_query.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
db_->CheckErrors(add_query);
|
2010-04-04 16:59:55 +02:00
|
|
|
}
|
|
|
|
}
|
2010-04-01 18:59:32 +02:00
|
|
|
}
|
2010-04-14 23:03:00 +02:00
|
|
|
transaction.Commit();
|
2010-04-01 18:59:32 +02:00
|
|
|
}
|
|
|
|
|
2010-11-27 17:14:09 +01:00
|
|
|
void LibraryBackend::AddOrUpdateSongs(const SongList& songs) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery check_dir(
|
|
|
|
QString("SELECT ROWID FROM %1 WHERE ROWID = :id").arg(dirs_table_), db);
|
|
|
|
QSqlQuery add_song(QString("INSERT INTO %1 (" + Song::kColumnSpec +
|
|
|
|
")"
|
|
|
|
" VALUES (" +
|
|
|
|
Song::kBindSpec + ")").arg(songs_table_),
|
|
|
|
db);
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlQuery update_song(QString("UPDATE %1 SET " + Song::kUpdateSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" WHERE ROWID = :id").arg(songs_table_),
|
|
|
|
db);
|
|
|
|
QSqlQuery add_song_fts(
|
|
|
|
QString("INSERT INTO %1 (ROWID, " + Song::kFtsColumnSpec +
|
|
|
|
")"
|
|
|
|
" VALUES (:id, " +
|
|
|
|
Song::kFtsBindSpec + ")").arg(fts_table_),
|
|
|
|
db);
|
2010-06-20 18:30:10 +02:00
|
|
|
QSqlQuery update_song_fts(QString("UPDATE %1 SET " + Song::kFtsUpdateSpec +
|
2014-02-07 16:34:20 +01:00
|
|
|
" WHERE ROWID = :id").arg(fts_table_),
|
|
|
|
db);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
ScopedTransaction transaction(&db);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
SongList added_songs;
|
|
|
|
SongList deleted_songs;
|
|
|
|
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const Song& song : songs) {
|
2009-12-24 20:16:07 +01:00
|
|
|
// Do a sanity check first - make sure the song's directory still exists
|
|
|
|
// This is to fix a possible race condition when a directory is removed
|
|
|
|
// while LibraryWatcher is scanning it.
|
2010-05-09 18:53:35 +02:00
|
|
|
if (!dirs_table_.isEmpty()) {
|
|
|
|
check_dir.bindValue(":id", song.directory_id());
|
|
|
|
check_dir.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(check_dir)) continue;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!check_dir.next()) continue; // Directory didn't exist
|
2010-05-09 18:53:35 +02:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-11-27 17:14:09 +01:00
|
|
|
if (song.id() == -1) {
|
2009-12-24 20:16:07 +01:00
|
|
|
// Create
|
|
|
|
|
2010-11-27 17:14:09 +01:00
|
|
|
// Insert the row and create a new ID
|
|
|
|
song.BindToQuery(&add_song);
|
|
|
|
add_song.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(add_song)) continue;
|
2010-11-26 00:05:37 +01:00
|
|
|
|
2010-11-27 17:14:09 +01:00
|
|
|
// Get the new ID
|
|
|
|
const int id = add_song.lastInsertId().toInt();
|
2010-06-20 18:30:10 +02:00
|
|
|
|
2010-11-26 00:05:37 +01:00
|
|
|
// Add to the FTS index
|
2010-06-20 18:30:10 +02:00
|
|
|
add_song_fts.bindValue(":id", id);
|
|
|
|
song.BindToFtsQuery(&add_song_fts);
|
|
|
|
add_song_fts.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(add_song_fts)) continue;
|
2010-06-20 18:30:10 +02:00
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
Song copy(song);
|
2010-06-20 18:30:10 +02:00
|
|
|
copy.set_id(id);
|
2009-12-24 20:16:07 +01:00
|
|
|
added_songs << copy;
|
|
|
|
} else {
|
|
|
|
// Get the previous song data first
|
|
|
|
Song old_song(GetSongById(song.id()));
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!old_song.is_valid()) continue;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
// Update
|
|
|
|
song.BindToQuery(&update_song);
|
|
|
|
update_song.bindValue(":id", song.id());
|
|
|
|
update_song.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(update_song)) continue;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-06-20 18:30:10 +02:00
|
|
|
song.BindToFtsQuery(&update_song_fts);
|
|
|
|
update_song_fts.bindValue(":id", song.id());
|
|
|
|
update_song_fts.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(update_song_fts)) continue;
|
2010-06-20 18:30:10 +02:00
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
deleted_songs << old_song;
|
|
|
|
added_songs << song;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
transaction.Commit();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!deleted_songs.isEmpty()) emit SongsDeleted(deleted_songs);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!added_songs.isEmpty()) emit SongsDiscovered(added_songs);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
UpdateTotalSongCountAsync();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LibraryBackend::UpdateMTimesOnly(const SongList& songs) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlQuery q(QString("UPDATE %1 SET mtime = :mtime WHERE ROWID = :id")
|
2014-02-07 16:34:20 +01:00
|
|
|
.arg(songs_table_),
|
|
|
|
db);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
ScopedTransaction transaction(&db);
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const Song& song : songs) {
|
2009-12-24 20:16:07 +01:00
|
|
|
q.bindValue(":mtime", song.mtime());
|
|
|
|
q.bindValue(":id", song.id());
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
db_->CheckErrors(q);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-04-14 23:03:00 +02:00
|
|
|
transaction.Commit();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void LibraryBackend::DeleteSongs(const SongList& songs) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery remove(
|
|
|
|
QString("DELETE FROM %1 WHERE ROWID = :id").arg(songs_table_), db);
|
|
|
|
QSqlQuery remove_fts(
|
|
|
|
QString("DELETE FROM %1 WHERE ROWID = :id").arg(fts_table_), db);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
ScopedTransaction transaction(&db);
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const Song& song : songs) {
|
2010-06-20 18:30:10 +02:00
|
|
|
remove.bindValue(":id", song.id());
|
|
|
|
remove.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
db_->CheckErrors(remove);
|
2010-06-20 18:30:10 +02:00
|
|
|
|
|
|
|
remove_fts.bindValue(":id", song.id());
|
|
|
|
remove_fts.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
db_->CheckErrors(remove_fts);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
2010-04-14 23:03:00 +02:00
|
|
|
transaction.Commit();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
emit SongsDeleted(songs);
|
|
|
|
|
|
|
|
UpdateTotalSongCountAsync();
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void LibraryBackend::MarkSongsUnavailable(const SongList& songs,
|
|
|
|
bool unavailable) {
|
2011-07-06 22:07:56 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
2014-02-01 20:21:28 +01:00
|
|
|
QSqlQuery remove(QString("UPDATE %1 SET unavailable = %2 WHERE ROWID = :id")
|
2014-02-07 16:34:20 +01:00
|
|
|
.arg(songs_table_)
|
|
|
|
.arg(int(unavailable)),
|
|
|
|
db);
|
2011-07-06 22:07:56 +02:00
|
|
|
|
|
|
|
ScopedTransaction transaction(&db);
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const Song& song : songs) {
|
2011-07-06 22:07:56 +02:00
|
|
|
remove.bindValue(":id", song.id());
|
|
|
|
remove.exec();
|
|
|
|
db_->CheckErrors(remove);
|
|
|
|
}
|
|
|
|
transaction.Commit();
|
|
|
|
|
|
|
|
emit SongsDeleted(songs);
|
|
|
|
UpdateTotalSongCountAsync();
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QStringList LibraryBackend::GetAll(const QString& column,
|
|
|
|
const QueryOptions& opt) {
|
2009-12-24 20:16:07 +01:00
|
|
|
LibraryQuery query(opt);
|
2010-12-25 14:11:09 +01:00
|
|
|
query.SetColumnSpec("DISTINCT " + column);
|
2010-02-27 21:12:22 +01:00
|
|
|
query.AddCompilationRequirement(false);
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-03-31 02:30:57 +02:00
|
|
|
if (!ExecQuery(&query)) return QStringList();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-06-12 00:35:41 +02:00
|
|
|
QStringList ret;
|
|
|
|
while (query.Next()) {
|
|
|
|
ret << query.Value(0).toString();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-12-25 14:11:09 +01:00
|
|
|
QStringList LibraryBackend::GetAllArtists(const QueryOptions& opt) {
|
|
|
|
return GetAll("artist", opt);
|
|
|
|
}
|
|
|
|
|
2010-06-12 00:35:41 +02:00
|
|
|
QStringList LibraryBackend::GetAllArtistsWithAlbums(const QueryOptions& opt) {
|
|
|
|
LibraryQuery query(opt);
|
|
|
|
query.SetColumnSpec("DISTINCT artist");
|
|
|
|
query.AddCompilationRequirement(false);
|
|
|
|
query.AddWhere("album", "", "!=");
|
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
if (!ExecQuery(&query)) return QStringList();
|
|
|
|
|
2009-12-24 20:16:07 +01:00
|
|
|
QStringList ret;
|
2010-03-31 02:30:57 +02:00
|
|
|
while (query.Next()) {
|
|
|
|
ret << query.Value(0).toString();
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
LibraryBackend::AlbumList LibraryBackend::GetAllAlbums(
|
|
|
|
const QueryOptions& opt) {
|
2010-02-28 23:07:59 +01:00
|
|
|
return GetAlbums(QString(), false, opt);
|
2010-02-28 19:04:50 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
LibraryBackend::AlbumList LibraryBackend::GetAlbumsByArtist(
|
|
|
|
const QString& artist, const QueryOptions& opt) {
|
2010-02-28 23:07:59 +01:00
|
|
|
return GetAlbums(artist, false, opt);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
SongList LibraryBackend::GetSongsByAlbum(const QString& album,
|
|
|
|
const QueryOptions& opt) {
|
2012-02-19 14:38:24 +01:00
|
|
|
LibraryQuery query(opt);
|
|
|
|
query.AddCompilationRequirement(false);
|
|
|
|
query.AddWhere("album", album);
|
|
|
|
return ExecLibraryQuery(&query);
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
SongList LibraryBackend::GetSongs(const QString& artist, const QString& album,
|
|
|
|
const QueryOptions& opt) {
|
2009-12-24 20:16:07 +01:00
|
|
|
LibraryQuery query(opt);
|
2010-02-27 21:12:22 +01:00
|
|
|
query.AddCompilationRequirement(false);
|
2009-12-24 20:16:07 +01:00
|
|
|
query.AddWhere("artist", artist);
|
|
|
|
query.AddWhere("album", album);
|
2012-02-19 14:38:24 +01:00
|
|
|
return ExecLibraryQuery(&query);
|
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2012-02-19 14:38:24 +01:00
|
|
|
SongList LibraryBackend::ExecLibraryQuery(LibraryQuery* query) {
|
|
|
|
query->SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2012-02-19 14:38:24 +01:00
|
|
|
if (!ExecQuery(query)) return SongList();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
SongList ret;
|
2012-02-19 14:38:24 +01:00
|
|
|
while (query->Next()) {
|
2009-12-24 20:16:07 +01:00
|
|
|
Song song;
|
2012-02-19 14:38:24 +01:00
|
|
|
song.InitFromQuery(*query, true);
|
2009-12-24 20:16:07 +01:00
|
|
|
ret << song;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
Song LibraryBackend::GetSongById(int id) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2010-10-17 21:27:31 +02:00
|
|
|
return GetSongById(id, db);
|
|
|
|
}
|
|
|
|
|
2010-11-27 17:14:09 +01:00
|
|
|
SongList LibraryBackend::GetSongsById(const QList<int>& ids) {
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
QStringList str_ids;
|
2014-02-10 14:29:07 +01:00
|
|
|
for (int id : ids) {
|
|
|
|
str_ids << QString::number(id);
|
|
|
|
}
|
2010-11-27 17:14:09 +01:00
|
|
|
|
|
|
|
return GetSongsById(str_ids, db);
|
|
|
|
}
|
|
|
|
|
|
|
|
SongList LibraryBackend::GetSongsById(const QStringList& ids) {
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
return GetSongsById(ids, db);
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
SongList LibraryBackend::GetSongsByForeignId(const QStringList& ids,
|
|
|
|
const QString& table,
|
|
|
|
const QString& column) {
|
2010-11-27 17:14:09 +01:00
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
QString in = ids.join(",");
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(
|
|
|
|
QString(
|
|
|
|
"SELECT %2.ROWID, " + Song::kColumnSpec +
|
|
|
|
", %2.%3"
|
|
|
|
" FROM %2, %1"
|
|
|
|
" WHERE %2.%3 IN (%4) AND %1.ROWID = %2.ROWID AND unavailable = 0")
|
|
|
|
.arg(songs_table_, table, column, in),
|
|
|
|
db);
|
2010-11-27 17:14:09 +01:00
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return SongList();
|
2010-11-27 17:14:09 +01:00
|
|
|
|
2010-11-27 18:52:08 +01:00
|
|
|
QVector<Song> ret(ids.count());
|
2010-11-27 17:14:09 +01:00
|
|
|
while (q.next()) {
|
2010-11-27 18:52:08 +01:00
|
|
|
const QString foreign_id = q.value(Song::kColumns.count() + 1).toString();
|
|
|
|
const int index = ids.indexOf(foreign_id);
|
2014-02-07 16:34:20 +01:00
|
|
|
if (index == -1) continue;
|
2010-11-27 18:52:08 +01:00
|
|
|
|
2011-06-17 22:00:10 +02:00
|
|
|
ret[index].InitFromQuery(q, true);
|
2010-11-27 17:14:09 +01:00
|
|
|
}
|
2010-11-27 18:52:08 +01:00
|
|
|
return ret.toList();
|
2010-11-27 17:14:09 +01:00
|
|
|
}
|
|
|
|
|
2010-10-17 21:27:31 +02:00
|
|
|
Song LibraryBackend::GetSongById(int id, QSqlDatabase& db) {
|
2010-11-27 17:14:09 +01:00
|
|
|
SongList list = GetSongsById(QStringList() << QString::number(id), db);
|
2014-02-07 16:34:20 +01:00
|
|
|
if (list.isEmpty()) return Song();
|
2010-11-27 17:14:09 +01:00
|
|
|
return list.first();
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
SongList LibraryBackend::GetSongsById(const QStringList& ids,
|
|
|
|
QSqlDatabase& db) {
|
2010-11-27 17:14:09 +01:00
|
|
|
QString in = ids.join(",");
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(QString("SELECT ROWID, " + Song::kColumnSpec +
|
|
|
|
" FROM %1"
|
|
|
|
" WHERE ROWID IN (%2)").arg(songs_table_, in),
|
|
|
|
db);
|
2009-12-24 20:16:07 +01:00
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return SongList();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-11-27 17:14:09 +01:00
|
|
|
SongList ret;
|
|
|
|
while (q.next()) {
|
|
|
|
Song song;
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(q, true);
|
2010-11-27 17:14:09 +01:00
|
|
|
ret << song;
|
2010-08-03 21:29:49 +02:00
|
|
|
}
|
2009-12-24 20:16:07 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-04-28 14:27:53 +02:00
|
|
|
Song LibraryBackend::GetSongByUrl(const QUrl& url, qint64 beginning) {
|
2011-01-12 00:09:59 +01:00
|
|
|
LibraryQuery query;
|
|
|
|
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
2011-04-28 14:27:53 +02:00
|
|
|
query.AddWhere("filename", url.toEncoded());
|
2011-01-12 00:09:59 +01:00
|
|
|
query.AddWhere("beginning", beginning);
|
|
|
|
|
|
|
|
Song song;
|
|
|
|
if (ExecQuery(&query) && query.Next()) {
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(query, true);
|
2011-01-12 00:09:59 +01:00
|
|
|
}
|
|
|
|
return song;
|
|
|
|
}
|
|
|
|
|
2011-04-28 14:27:53 +02:00
|
|
|
SongList LibraryBackend::GetSongsByUrl(const QUrl& url) {
|
2011-01-15 19:46:23 +01:00
|
|
|
LibraryQuery query;
|
|
|
|
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
2011-04-28 14:27:53 +02:00
|
|
|
query.AddWhere("filename", url.toEncoded());
|
2011-01-15 19:46:23 +01:00
|
|
|
|
|
|
|
SongList songlist;
|
|
|
|
if (ExecQuery(&query)) {
|
2014-02-07 16:34:20 +01:00
|
|
|
while (query.Next()) {
|
2011-01-15 19:46:23 +01:00
|
|
|
Song song;
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(query, true);
|
2011-01-15 19:46:23 +01:00
|
|
|
|
|
|
|
songlist << song;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return songlist;
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
LibraryBackend::AlbumList LibraryBackend::GetCompilationAlbums(
|
|
|
|
const QueryOptions& opt) {
|
2010-02-28 23:07:59 +01:00
|
|
|
return GetAlbums(QString(), true, opt);
|
2009-12-24 20:16:07 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
SongList LibraryBackend::GetCompilationSongs(const QString& album,
|
|
|
|
const QueryOptions& opt) {
|
2009-12-24 20:16:07 +01:00
|
|
|
LibraryQuery query(opt);
|
2010-06-20 18:30:10 +02:00
|
|
|
query.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
|
2010-02-27 21:12:22 +01:00
|
|
|
query.AddCompilationRequirement(true);
|
2009-12-24 20:16:07 +01:00
|
|
|
query.AddWhere("album", album);
|
|
|
|
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-03-31 02:30:57 +02:00
|
|
|
if (!ExecQuery(&query)) return SongList();
|
2009-12-24 20:16:07 +01:00
|
|
|
|
|
|
|
SongList ret;
|
2010-03-31 02:30:57 +02:00
|
|
|
while (query.Next()) {
|
2009-12-24 20:16:07 +01:00
|
|
|
Song song;
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(query, true);
|
2009-12-24 20:16:07 +01:00
|
|
|
ret << song;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-02-27 21:12:22 +01:00
|
|
|
void LibraryBackend::UpdateCompilations() {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2010-02-27 21:12:22 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
// Look for albums that have songs by more than one 'effective album artist'
|
|
|
|
// in the same
|
2010-02-27 21:12:22 +01:00
|
|
|
// directory
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(
|
|
|
|
QString(
|
|
|
|
"SELECT effective_albumartist, album, filename, sampler "
|
|
|
|
"FROM %1 WHERE unavailable = 0 ORDER BY album").arg(songs_table_),
|
|
|
|
db);
|
2010-02-27 21:12:22 +01:00
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-02-27 21:12:22 +01:00
|
|
|
|
|
|
|
QMap<QString, CompilationInfo> compilation_info;
|
|
|
|
while (q.next()) {
|
|
|
|
QString artist = q.value(0).toString();
|
|
|
|
QString album = q.value(1).toString();
|
|
|
|
QString filename = q.value(2).toString();
|
|
|
|
bool sampler = q.value(3).toBool();
|
|
|
|
|
|
|
|
// Ignore songs that don't have an album field set
|
2014-02-07 16:34:20 +01:00
|
|
|
if (album.isEmpty()) continue;
|
2010-02-27 21:12:22 +01:00
|
|
|
|
|
|
|
// Find the directory the song is in
|
2010-06-20 18:30:10 +02:00
|
|
|
int last_separator = filename.lastIndexOf('/');
|
2014-02-07 16:34:20 +01:00
|
|
|
if (last_separator == -1) continue;
|
2010-02-27 21:12:22 +01:00
|
|
|
|
|
|
|
CompilationInfo& info = compilation_info[album];
|
|
|
|
info.artists.insert(artist);
|
2010-06-20 18:30:10 +02:00
|
|
|
info.directories.insert(filename.left(last_separator));
|
2014-02-07 16:34:20 +01:00
|
|
|
if (sampler)
|
|
|
|
info.has_samplers = true;
|
|
|
|
else
|
|
|
|
info.has_not_samplers = true;
|
2010-02-27 21:12:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now mark the songs that we think are in compilations
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery update(
|
|
|
|
QString(
|
|
|
|
"UPDATE %1"
|
|
|
|
" SET sampler = :sampler,"
|
|
|
|
" effective_compilation = ((compilation OR :sampler OR "
|
|
|
|
"forced_compilation_on) AND NOT forced_compilation_off) + 0"
|
|
|
|
" WHERE album = :album AND unavailable = 0").arg(songs_table_),
|
|
|
|
db);
|
|
|
|
QSqlQuery find_songs(
|
|
|
|
QString(
|
|
|
|
"SELECT ROWID, " + Song::kColumnSpec +
|
|
|
|
" FROM %1"
|
|
|
|
" WHERE album = :album AND sampler = :sampler AND unavailable = 0")
|
|
|
|
.arg(songs_table_),
|
|
|
|
db);
|
2010-02-27 21:12:22 +01:00
|
|
|
|
2010-03-21 23:14:07 +01:00
|
|
|
SongList deleted_songs;
|
|
|
|
SongList added_songs;
|
2010-02-27 21:12:22 +01:00
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
ScopedTransaction transaction(&db);
|
2010-02-27 21:12:22 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QMap<QString, CompilationInfo>::const_iterator it =
|
|
|
|
compilation_info.constBegin();
|
|
|
|
for (; it != compilation_info.constEnd(); ++it) {
|
2010-02-27 21:12:22 +01:00
|
|
|
const CompilationInfo& info = it.value();
|
|
|
|
QString album(it.key());
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
// If there were more 'effective album artists' than there were directories
|
|
|
|
// for this album,
|
2010-02-27 21:12:22 +01:00
|
|
|
// then it's a compilation
|
|
|
|
|
|
|
|
if (info.artists.count() > info.directories.count()) {
|
|
|
|
if (info.has_not_samplers)
|
2014-02-07 16:34:20 +01:00
|
|
|
UpdateCompilations(find_songs, update, deleted_songs, added_songs,
|
|
|
|
album, 1);
|
2010-02-27 21:12:22 +01:00
|
|
|
} else {
|
|
|
|
if (info.has_samplers)
|
2014-02-07 16:34:20 +01:00
|
|
|
UpdateCompilations(find_songs, update, deleted_songs, added_songs,
|
|
|
|
album, 0);
|
2010-02-27 21:12:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-14 23:03:00 +02:00
|
|
|
transaction.Commit();
|
2010-02-27 21:12:22 +01:00
|
|
|
|
2010-03-21 23:14:07 +01:00
|
|
|
if (!deleted_songs.isEmpty()) {
|
|
|
|
emit SongsDeleted(deleted_songs);
|
|
|
|
emit SongsDiscovered(added_songs);
|
2010-02-27 21:12:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void LibraryBackend::UpdateCompilations(QSqlQuery& find_songs,
|
|
|
|
QSqlQuery& update,
|
|
|
|
SongList& deleted_songs,
|
|
|
|
SongList& added_songs,
|
2010-02-27 21:12:22 +01:00
|
|
|
const QString& album, int sampler) {
|
|
|
|
// Get songs that were already in that album, so we can tell the model
|
|
|
|
// they've been updated
|
|
|
|
find_songs.bindValue(":album", album);
|
|
|
|
find_songs.bindValue(":sampler", int(!sampler));
|
|
|
|
find_songs.exec();
|
|
|
|
while (find_songs.next()) {
|
|
|
|
Song song;
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(find_songs, true);
|
2010-03-21 23:14:07 +01:00
|
|
|
deleted_songs << song;
|
2010-02-27 21:12:22 +01:00
|
|
|
song.set_sampler(true);
|
2010-03-21 23:14:07 +01:00
|
|
|
added_songs << song;
|
2010-02-27 21:12:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mark this album
|
|
|
|
update.bindValue(":sampler", sampler);
|
|
|
|
update.bindValue(":album", album);
|
|
|
|
update.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
db_->CheckErrors(update);
|
2010-02-27 21:12:22 +01:00
|
|
|
}
|
|
|
|
|
2010-02-28 23:07:59 +01:00
|
|
|
LibraryBackend::AlbumList LibraryBackend::GetAlbums(const QString& artist,
|
|
|
|
bool compilation,
|
|
|
|
const QueryOptions& opt) {
|
|
|
|
AlbumList ret;
|
|
|
|
|
2010-02-28 19:04:50 +01:00
|
|
|
LibraryQuery query(opt);
|
2014-02-07 16:34:20 +01:00
|
|
|
query.SetColumnSpec(
|
|
|
|
"album, artist, compilation, sampler, art_automatic, "
|
|
|
|
"art_manual, filename");
|
2010-02-28 19:04:50 +01:00
|
|
|
query.SetOrderBy("album");
|
|
|
|
|
2010-02-28 23:07:59 +01:00
|
|
|
if (compilation) {
|
|
|
|
query.AddCompilationRequirement(true);
|
|
|
|
} else if (!artist.isNull()) {
|
|
|
|
query.AddCompilationRequirement(false);
|
2010-02-28 19:04:50 +01:00
|
|
|
query.AddWhere("artist", artist);
|
2010-02-28 23:07:59 +01:00
|
|
|
}
|
2010-02-28 19:04:50 +01:00
|
|
|
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-03-31 02:30:57 +02:00
|
|
|
if (!ExecQuery(&query)) return ret;
|
2010-02-28 19:04:50 +01:00
|
|
|
|
|
|
|
QString last_album;
|
2010-03-05 11:51:16 +01:00
|
|
|
QString last_artist;
|
2010-03-31 02:30:57 +02:00
|
|
|
while (query.Next()) {
|
|
|
|
bool compilation = query.Value(2).toBool() | query.Value(3).toBool();
|
2010-02-28 20:25:52 +01:00
|
|
|
|
2010-02-28 23:07:59 +01:00
|
|
|
Album info;
|
2010-03-31 02:30:57 +02:00
|
|
|
info.artist = compilation ? QString() : query.Value(1).toString();
|
|
|
|
info.album_name = query.Value(0).toString();
|
|
|
|
info.art_automatic = query.Value(4).toString();
|
|
|
|
info.art_manual = query.Value(5).toString();
|
2011-04-28 14:27:53 +02:00
|
|
|
info.first_url = QUrl::fromEncoded(query.Value(6).toByteArray());
|
2010-03-05 11:51:16 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
if (info.artist == last_artist && info.album_name == last_album) continue;
|
2010-03-05 11:51:16 +01:00
|
|
|
|
2010-02-28 19:04:50 +01:00
|
|
|
ret << info;
|
2009-12-24 20:16:07 +01:00
|
|
|
|
2010-02-28 19:04:50 +01:00
|
|
|
last_album = info.album_name;
|
2010-03-05 11:51:16 +01:00
|
|
|
last_artist = info.artist;
|
2010-02-28 19:04:50 +01:00
|
|
|
}
|
2010-03-21 22:17:01 +01:00
|
|
|
|
2010-02-28 19:04:50 +01:00
|
|
|
return ret;
|
2010-02-28 23:07:59 +01:00
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
LibraryBackend::Album LibraryBackend::GetAlbumArt(const QString& artist,
|
|
|
|
const QString& album) {
|
2010-02-28 23:07:59 +01:00
|
|
|
Album ret;
|
|
|
|
ret.album_name = album;
|
|
|
|
ret.artist = artist;
|
|
|
|
|
|
|
|
LibraryQuery query = LibraryQuery(QueryOptions());
|
2010-07-19 20:08:25 +02:00
|
|
|
query.SetColumnSpec("art_automatic, art_manual, filename");
|
2010-02-28 23:07:59 +01:00
|
|
|
query.AddWhere("artist", artist);
|
|
|
|
query.AddWhere("album", album);
|
|
|
|
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-07-19 20:08:25 +02:00
|
|
|
if (!ExecQuery(&query)) return ret;
|
2010-02-28 23:07:59 +01:00
|
|
|
|
2010-03-31 02:30:57 +02:00
|
|
|
if (query.Next()) {
|
|
|
|
ret.art_automatic = query.Value(0).toString();
|
|
|
|
ret.art_manual = query.Value(1).toString();
|
2011-04-28 14:27:53 +02:00
|
|
|
ret.first_url = QUrl::fromEncoded(query.Value(2).toByteArray());
|
2010-02-28 23:07:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2010-02-28 19:04:50 +01:00
|
|
|
}
|
2010-02-28 20:25:52 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void LibraryBackend::UpdateManualAlbumArtAsync(const QString& artist,
|
|
|
|
const QString& album,
|
|
|
|
const QString& art) {
|
2010-02-28 20:25:52 +01:00
|
|
|
metaObject()->invokeMethod(this, "UpdateManualAlbumArt", Qt::QueuedConnection,
|
2014-02-07 16:34:20 +01:00
|
|
|
Q_ARG(QString, artist), Q_ARG(QString, album),
|
2010-02-28 20:25:52 +01:00
|
|
|
Q_ARG(QString, art));
|
|
|
|
}
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void LibraryBackend::UpdateManualAlbumArt(const QString& artist,
|
|
|
|
const QString& album,
|
|
|
|
const QString& art) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2010-02-28 20:25:52 +01:00
|
|
|
|
2010-06-18 15:47:11 +02:00
|
|
|
// Get the songs before they're updated
|
|
|
|
LibraryQuery query;
|
|
|
|
query.SetColumnSpec("ROWID, " + Song::kColumnSpec);
|
|
|
|
query.AddWhere("album", album);
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!artist.isNull()) query.AddWhere("artist", artist);
|
2010-06-18 15:47:11 +02:00
|
|
|
|
|
|
|
if (!ExecQuery(&query)) return;
|
|
|
|
|
|
|
|
SongList deleted_songs;
|
|
|
|
while (query.Next()) {
|
|
|
|
Song song;
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(query, true);
|
2010-06-18 15:47:11 +02:00
|
|
|
deleted_songs << song;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the songs
|
2014-02-07 16:34:20 +01:00
|
|
|
QString sql(
|
|
|
|
QString(
|
|
|
|
"UPDATE %1 SET art_manual = :art"
|
|
|
|
" WHERE album = :album AND unavailable = 0").arg(songs_table_));
|
|
|
|
if (!artist.isNull()) sql += " AND artist = :artist";
|
2010-02-28 20:25:52 +01:00
|
|
|
|
|
|
|
QSqlQuery q(sql, db);
|
|
|
|
q.bindValue(":art", art);
|
|
|
|
q.bindValue(":album", album);
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!artist.isNull()) q.bindValue(":artist", artist);
|
2010-02-28 20:25:52 +01:00
|
|
|
|
|
|
|
q.exec();
|
2011-02-05 14:43:04 +01:00
|
|
|
db_->CheckErrors(q);
|
2010-06-18 15:47:11 +02:00
|
|
|
|
|
|
|
// Now get the updated songs
|
|
|
|
if (!ExecQuery(&query)) return;
|
|
|
|
|
|
|
|
SongList added_songs;
|
|
|
|
while (query.Next()) {
|
|
|
|
Song song;
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(query, true);
|
2010-06-18 15:47:11 +02:00
|
|
|
added_songs << song;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!added_songs.isEmpty() || !deleted_songs.isEmpty()) {
|
|
|
|
emit SongsDeleted(deleted_songs);
|
|
|
|
emit SongsDiscovered(added_songs);
|
|
|
|
}
|
2010-02-28 20:25:52 +01:00
|
|
|
}
|
2010-03-21 00:59:39 +01:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
void LibraryBackend::ForceCompilation(const QString& album,
|
|
|
|
const QList<QString>& artists, bool on) {
|
2010-06-02 18:22:20 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
2010-05-09 02:10:26 +02:00
|
|
|
QSqlDatabase db(db_->Connect());
|
2011-11-28 07:11:46 +01:00
|
|
|
SongList deleted_songs, added_songs;
|
2010-03-21 00:59:39 +01:00
|
|
|
|
2014-02-10 14:29:07 +01:00
|
|
|
for (const QString& artist : artists) {
|
2011-11-28 07:11:46 +01:00
|
|
|
// Get the songs before they're updated
|
|
|
|
LibraryQuery query;
|
|
|
|
query.SetColumnSpec("ROWID, " + Song::kColumnSpec);
|
|
|
|
query.AddWhere("album", album);
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!artist.isNull()) query.AddWhere("artist", artist);
|
2010-03-21 00:59:39 +01:00
|
|
|
|
2011-11-28 07:11:46 +01:00
|
|
|
if (!ExecQuery(&query)) return;
|
2010-03-21 00:59:39 +01:00
|
|
|
|
2011-11-28 07:11:46 +01:00
|
|
|
while (query.Next()) {
|
|
|
|
Song song;
|
|
|
|
song.InitFromQuery(query, true);
|
|
|
|
deleted_songs << song;
|
|
|
|
}
|
2010-03-21 00:59:39 +01:00
|
|
|
|
2011-11-28 07:11:46 +01:00
|
|
|
// Update the songs
|
2014-02-07 16:34:20 +01:00
|
|
|
QString sql(
|
|
|
|
QString(
|
|
|
|
"UPDATE %1 SET forced_compilation_on = :forced_compilation_on,"
|
|
|
|
" forced_compilation_off = :forced_compilation_off,"
|
|
|
|
" effective_compilation = ((compilation OR sampler OR "
|
|
|
|
":forced_compilation_on) AND NOT :forced_compilation_off) + 0"
|
|
|
|
" WHERE album = :album AND unavailable = 0").arg(songs_table_));
|
|
|
|
if (!artist.isEmpty()) sql += " AND artist = :artist";
|
2011-11-28 07:11:46 +01:00
|
|
|
|
|
|
|
QSqlQuery q(sql, db);
|
|
|
|
q.bindValue(":forced_compilation_on", on ? 1 : 0);
|
|
|
|
q.bindValue(":forced_compilation_off", on ? 0 : 1);
|
|
|
|
q.bindValue(":album", album);
|
2014-02-07 16:34:20 +01:00
|
|
|
if (!artist.isEmpty()) q.bindValue(":artist", artist);
|
2010-03-21 00:59:39 +01:00
|
|
|
|
2011-11-28 07:11:46 +01:00
|
|
|
q.exec();
|
|
|
|
db_->CheckErrors(q);
|
2010-03-21 00:59:39 +01:00
|
|
|
|
2011-11-28 07:11:46 +01:00
|
|
|
// Now get the updated songs
|
|
|
|
if (!ExecQuery(&query)) return;
|
2010-03-21 00:59:39 +01:00
|
|
|
|
2011-11-28 07:11:46 +01:00
|
|
|
while (query.Next()) {
|
|
|
|
Song song;
|
|
|
|
song.InitFromQuery(query, true);
|
|
|
|
added_songs << song;
|
|
|
|
}
|
2010-03-21 00:59:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!added_songs.isEmpty() || !deleted_songs.isEmpty()) {
|
|
|
|
emit SongsDeleted(deleted_songs);
|
|
|
|
emit SongsDiscovered(added_songs);
|
|
|
|
}
|
|
|
|
}
|
2010-03-31 02:30:57 +02:00
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
bool LibraryBackend::ExecQuery(LibraryQuery* q) {
|
2010-06-20 18:30:10 +02:00
|
|
|
return !db_->CheckErrors(q->Exec(db_->Connect(), songs_table_, fts_table_));
|
2010-04-14 23:03:00 +02:00
|
|
|
}
|
2010-10-17 21:27:31 +02:00
|
|
|
|
2010-11-18 21:19:33 +01:00
|
|
|
SongList LibraryBackend::FindSongs(const smart_playlists::Search& search) {
|
2010-10-24 17:38:12 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
// Build the query
|
2010-10-29 20:41:49 +02:00
|
|
|
QString sql = search.ToSql(songs_table());
|
2010-10-24 17:38:12 +02:00
|
|
|
|
|
|
|
// Run the query
|
|
|
|
SongList ret;
|
|
|
|
QSqlQuery query(sql, db);
|
|
|
|
query.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(query)) return ret;
|
2010-10-24 17:38:12 +02:00
|
|
|
|
|
|
|
// Read the results
|
|
|
|
while (query.next()) {
|
|
|
|
Song song;
|
2011-06-17 22:00:10 +02:00
|
|
|
song.InitFromQuery(query, true);
|
2010-10-24 17:38:12 +02:00
|
|
|
ret << song;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-03-26 23:56:46 +01:00
|
|
|
SongList LibraryBackend::GetAllSongs() {
|
|
|
|
// Get all the songs!
|
|
|
|
return FindSongs(smart_playlists::Search(
|
|
|
|
smart_playlists::Search::Type_All, smart_playlists::Search::TermList(),
|
2014-02-07 16:34:20 +01:00
|
|
|
smart_playlists::Search::Sort_FieldAsc,
|
|
|
|
smart_playlists::SearchTerm::Field_Artist, -1));
|
2013-03-26 23:56:46 +01:00
|
|
|
}
|
|
|
|
|
2010-10-17 21:27:31 +02:00
|
|
|
void LibraryBackend::IncrementPlayCount(int id) {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (id == -1) return;
|
2010-10-17 21:27:31 +02:00
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(QString(
|
|
|
|
"UPDATE %1 SET playcount = playcount + 1,"
|
|
|
|
" lastplayed = :now,"
|
|
|
|
" score = " +
|
|
|
|
QString(kNewScoreSql).arg("1.0") +
|
|
|
|
" WHERE ROWID = :id").arg(songs_table_),
|
|
|
|
db);
|
2010-10-17 21:27:31 +02:00
|
|
|
q.bindValue(":now", QDateTime::currentDateTime().toTime_t());
|
|
|
|
q.bindValue(":id", id);
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-10-17 21:27:31 +02:00
|
|
|
|
|
|
|
Song new_song = GetSongById(id, db);
|
2010-10-17 21:34:45 +02:00
|
|
|
emit SongsStatisticsChanged(SongList() << new_song);
|
2010-10-17 21:27:31 +02:00
|
|
|
}
|
|
|
|
|
2010-11-01 22:15:52 +01:00
|
|
|
void LibraryBackend::IncrementSkipCount(int id, float progress) {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (id == -1) return;
|
2010-11-01 22:15:52 +01:00
|
|
|
progress = qBound(0.0f, progress, 1.0f);
|
2010-10-17 21:27:31 +02:00
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(QString(
|
|
|
|
"UPDATE %1 SET skipcount = skipcount + 1,"
|
|
|
|
" score = " +
|
|
|
|
QString(kNewScoreSql).arg(progress) +
|
|
|
|
" WHERE ROWID = :id").arg(songs_table_),
|
|
|
|
db);
|
2010-10-17 21:27:31 +02:00
|
|
|
q.bindValue(":id", id);
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-10-17 21:27:31 +02:00
|
|
|
|
|
|
|
Song new_song = GetSongById(id, db);
|
2010-12-25 12:54:21 +01:00
|
|
|
emit SongsStatisticsChanged(SongList() << new_song);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LibraryBackend::ResetStatistics(int id) {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (id == -1) return;
|
2010-12-25 12:54:21 +01:00
|
|
|
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
|
|
|
QSqlQuery q(QString(
|
2014-02-07 16:34:20 +01:00
|
|
|
"UPDATE %1 SET playcount = 0, skipcount = 0,"
|
|
|
|
" lastplayed = -1, score = 0"
|
|
|
|
" WHERE ROWID = :id").arg(songs_table_),
|
|
|
|
db);
|
2010-12-25 12:54:21 +01:00
|
|
|
q.bindValue(":id", id);
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-12-25 12:54:21 +01:00
|
|
|
|
|
|
|
Song new_song = GetSongById(id, db);
|
2010-10-17 21:34:45 +02:00
|
|
|
emit SongsStatisticsChanged(SongList() << new_song);
|
2010-10-17 21:27:31 +02:00
|
|
|
}
|
2010-10-17 22:53:48 +02:00
|
|
|
|
|
|
|
void LibraryBackend::UpdateSongRating(int id, float rating) {
|
2014-02-07 16:34:20 +01:00
|
|
|
if (id == -1) return;
|
2010-10-17 22:53:48 +02:00
|
|
|
|
2014-04-20 03:07:44 +02:00
|
|
|
QList<int> id_list;
|
|
|
|
id_list << id;
|
|
|
|
UpdateSongsRating(id_list, rating);
|
|
|
|
}
|
|
|
|
|
2014-04-26 01:41:04 +02:00
|
|
|
void LibraryBackend::UpdateSongsRating(const QList<int>& id_list,
|
|
|
|
float rating) {
|
2014-04-20 03:07:44 +02:00
|
|
|
if (id_list.isEmpty()) return;
|
|
|
|
|
2010-10-17 22:53:48 +02:00
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
|
2014-04-20 03:07:44 +02:00
|
|
|
QStringList id_str_list;
|
|
|
|
for (int i : id_list) {
|
|
|
|
id_str_list << QString::number(i);
|
|
|
|
}
|
|
|
|
QString ids = id_str_list.join(",");
|
2014-02-07 16:34:20 +01:00
|
|
|
QSqlQuery q(QString(
|
2014-04-26 01:41:04 +02:00
|
|
|
"UPDATE %1 SET rating = :rating"
|
|
|
|
" WHERE ROWID IN (%2)").arg(songs_table_, ids),
|
|
|
|
db);
|
2010-10-17 22:53:48 +02:00
|
|
|
q.bindValue(":rating", rating);
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2014-04-20 03:07:44 +02:00
|
|
|
SongList new_song_list = GetSongsById(id_str_list, db);
|
|
|
|
emit SongsRatingChanged(new_song_list);
|
2010-10-17 22:53:48 +02:00
|
|
|
}
|
2010-11-26 00:05:37 +01:00
|
|
|
|
|
|
|
void LibraryBackend::DeleteAll() {
|
|
|
|
{
|
|
|
|
QMutexLocker l(db_->Mutex());
|
|
|
|
QSqlDatabase db(db_->Connect());
|
|
|
|
ScopedTransaction t(&db);
|
|
|
|
|
|
|
|
QSqlQuery q("DELETE FROM " + songs_table_, db);
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-11-26 00:05:37 +01:00
|
|
|
|
|
|
|
q = QSqlQuery("DELETE FROM " + fts_table_, db);
|
|
|
|
q.exec();
|
2014-02-07 16:34:20 +01:00
|
|
|
if (db_->CheckErrors(q)) return;
|
2010-11-26 00:05:37 +01:00
|
|
|
|
|
|
|
t.Commit();
|
|
|
|
}
|
|
|
|
|
|
|
|
emit DatabaseReset();
|
|
|
|
}
|