Clean up deleted subdirectories from the database properly. Fixes an issue where subdirectories in the library wouldn't be rescanned if they were removed and recreated. Updates issue #45.

This commit is contained in:
David Sansome 2010-04-04 14:59:55 +00:00
parent 9151ba05d4
commit dacdf4cc5a
5 changed files with 80 additions and 45 deletions

View File

@ -119,9 +119,9 @@ void Library::Initialise() {
connect(watcher_->Worker().get(), SIGNAL(SongsDeleted(SongList)),
backend_->Worker().get(), SLOT(DeleteSongs(SongList)));
connect(watcher_->Worker().get(), SIGNAL(SubdirsDiscovered(SubdirectoryList)),
backend_->Worker().get(), SLOT(AddSubdirs(SubdirectoryList)));
backend_->Worker().get(), SLOT(AddOrUpdateSubdirs(SubdirectoryList)));
connect(watcher_->Worker().get(), SIGNAL(SubdirsMTimeUpdated(SubdirectoryList)),
backend_->Worker().get(), SLOT(UpdateSubdirMTimes(SubdirectoryList)));
backend_->Worker().get(), SLOT(AddOrUpdateSubdirs(SubdirectoryList)));
// This will start the watcher checking for updates
backend_->Worker()->LoadDirectoriesAsync();

View File

@ -301,6 +301,11 @@ void LibraryBackend::LoadDirectories() {
}
}
SubdirectoryList LibraryBackend::SubdirsInDirectory(int id) {
QSqlDatabase db = Connect();
return SubdirsInDirectory(id, db);
}
SubdirectoryList LibraryBackend::SubdirsInDirectory(int id, QSqlDatabase &db) {
QSqlQuery q("SELECT path, mtime FROM subdirectories"
" WHERE directory = :dir", db);
@ -390,34 +395,46 @@ SongList LibraryBackend::FindSongsInDirectory(int id) {
return ret;
}
void LibraryBackend::AddSubdirs(const SubdirectoryList& subdirs) {
void LibraryBackend::AddOrUpdateSubdirs(const SubdirectoryList& subdirs) {
QSqlDatabase db(Connect());
QSqlQuery q("INSERT INTO subdirectories (directory, path, mtime)"
" VALUES (:id, :path, :mtime)", db);
QSqlQuery find_query("SELECT ROWID FROM subdirectories"
" WHERE directory = :id AND path = :path", db);
QSqlQuery add_query("INSERT INTO subdirectories (directory, path, mtime)"
" VALUES (:id, :path, :mtime)", db);
QSqlQuery update_query("UPDATE subdirectories SET mtime = :mtime"
" WHERE directory = :id AND path = :path", db);
QSqlQuery delete_query("DELETE FROM subdirectories"
" WHERE directory = :id AND path = :path", db);
db.transaction();
foreach (const Subdirectory& subdir, subdirs) {
q.bindValue(":id", subdir.directory_id);
q.bindValue(":path", subdir.path);
q.bindValue(":mtime", subdir.mtime);
q.exec();
if (CheckErrors(q.lastError())) continue;
}
db.commit();
}
if (subdir.mtime == 0) {
// Delete the subdirectory
delete_query.bindValue(":id", subdir.directory_id);
delete_query.bindValue(":path", subdir.path);
delete_query.exec();
CheckErrors(delete_query.lastError());
} 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();
if (CheckErrors(find_query.lastError())) continue;
void LibraryBackend::UpdateSubdirMTimes(const SubdirectoryList& subdirs) {
QSqlDatabase db(Connect());
QSqlQuery q("UPDATE subdirectories SET mtime = :mtime"
" WHERE directory = :id AND path = :path", db);
db.transaction();
foreach (const Subdirectory& subdir, subdirs) {
q.bindValue(":mtime", subdir.mtime);
q.bindValue(":id", subdir.directory_id);
q.bindValue(":path", subdir.path);
q.exec();
CheckErrors(q.lastError());
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();
CheckErrors(update_query.lastError());
} else {
add_query.bindValue(":id", subdir.directory_id);
add_query.bindValue(":path", subdir.path);
add_query.bindValue(":mtime", subdir.mtime);
add_query.exec();
CheckErrors(add_query.lastError());
}
}
}
db.commit();
}

View File

@ -61,6 +61,7 @@ class LibraryBackendInterface : public QObject {
virtual void UpdateTotalSongCountAsync() = 0;
virtual SongList FindSongsInDirectory(int id) = 0;
virtual SubdirectoryList SubdirsInDirectory(int id) = 0;
virtual QStringList GetAllArtists(const QueryOptions& opt = QueryOptions()) = 0;
virtual SongList GetSongs(const QString& artist, const QString& album, const QueryOptions& opt = QueryOptions()) = 0;
@ -90,8 +91,7 @@ class LibraryBackendInterface : public QObject {
virtual void AddOrUpdateSongs(const SongList& songs) = 0;
virtual void UpdateMTimesOnly(const SongList& songs) = 0;
virtual void DeleteSongs(const SongList& songs) = 0;
virtual void AddSubdirs(const SubdirectoryList& subdirs) = 0;
virtual void UpdateSubdirMTimes(const SubdirectoryList& subdirs) = 0;
virtual void AddOrUpdateSubdirs(const SubdirectoryList& subdirs) = 0;
virtual void UpdateCompilations() = 0;
virtual void UpdateManualAlbumArt(const QString& artist, const QString& album, const QString& art) = 0;
virtual void ForceCompilation(const QString& artist, const QString& album, bool on) = 0;
@ -127,6 +127,7 @@ class LibraryBackend : public LibraryBackendInterface {
void UpdateTotalSongCountAsync();
SongList FindSongsInDirectory(int id);
SubdirectoryList SubdirsInDirectory(int id);
QStringList GetAllArtists(const QueryOptions& opt = QueryOptions());
SongList GetSongs(const QString& artist, const QString& album, const QueryOptions& opt = QueryOptions());
@ -156,8 +157,7 @@ class LibraryBackend : public LibraryBackendInterface {
void AddOrUpdateSongs(const SongList& songs);
void UpdateMTimesOnly(const SongList& songs);
void DeleteSongs(const SongList& songs);
void AddSubdirs(const SubdirectoryList& subdirs);
void UpdateSubdirMTimes(const SubdirectoryList& subdirs);
void AddOrUpdateSubdirs(const SubdirectoryList& subdirs);
void UpdateCompilations();
void UpdateManualAlbumArt(const QString& artist, const QString& album, const QString& art);
void ForceCompilation(const QString& artist, const QString& album, bool on);

View File

@ -54,7 +54,8 @@ LibraryWatcher::ScanTransaction::ScanTransaction(LibraryWatcher* watcher,
: dir_(dir),
incremental_(incremental),
watcher_(watcher),
cached_songs_dirty_(true)
cached_songs_dirty_(true),
known_subdirs_dirty_(true)
{
emit watcher_->ScanStarted();
}
@ -98,10 +99,25 @@ SongList LibraryWatcher::ScanTransaction::FindSongsInSubdirectory(const QString
return ret;
}
void LibraryWatcher::ScanTransaction::SetKnownSubdirs(const SubdirectoryList &subdirs) {
known_subdirs_ = subdirs;
known_subdirs_dirty_ = false;
}
bool LibraryWatcher::ScanTransaction::HasSeenSubdir(const QString &path) {
if (known_subdirs_dirty_)
SetKnownSubdirs(watcher_->backend_->SubdirsInDirectory(dir_));
foreach (const Subdirectory& subdir, known_subdirs_) {
if (subdir.path == path && subdir.mtime != 0)
return true;
}
return false;
}
void LibraryWatcher::AddDirectory(const Directory& dir, const SubdirectoryList& subdirs) {
DirData data;
data.dir = dir;
data.known_subdirs = subdirs;
data.watcher = new QFileSystemWatcher(this);
connect(data.watcher, SIGNAL(directoryChanged(QString)), SLOT(DirectoryChanged(QString)));
watched_dirs_[dir.id] = data;
@ -110,11 +126,13 @@ void LibraryWatcher::AddDirectory(const Directory& dir, const SubdirectoryList&
// This is a new directory that we've never seen before.
// Scan it fully.
ScanTransaction transaction(this, dir.id, false);
transaction.SetKnownSubdirs(subdirs);
ScanSubdirectory(dir.path, Subdirectory(), &transaction);
} else {
// We can do an incremental scan - looking at the mtimes of each
// subdirectory and only rescan if the directory has changed.
ScanTransaction transaction(this, dir.id, true);
transaction.SetKnownSubdirs(subdirs);
foreach (const Subdirectory& subdir, subdirs) {
ScanSubdirectory(subdir.path, subdir, &transaction);
AddWatch(data.watcher, subdir.path);
@ -124,14 +142,6 @@ void LibraryWatcher::AddDirectory(const Directory& dir, const SubdirectoryList&
backend_->UpdateCompilationsAsync();
}
bool LibraryWatcher::HasSeenSubdir(int id, const QString& path) const {
foreach (const Subdirectory& subdir, watched_dirs_[id].known_subdirs) {
if (subdir.path == path)
return true;
}
return false;
}
void LibraryWatcher::ScanSubdirectory(
const QString& path, const Subdirectory& subdir, ScanTransaction* t,
bool force_noincremental) {
@ -155,11 +165,11 @@ void LibraryWatcher::ScanSubdirectory(
QFileInfo child_info(child);
if (child_info.isDir()) {
if (!HasSeenSubdir(t->dir(), child)) {
if (!t->HasSeenSubdir(child)) {
// We haven't seen this subdirectory before - add it to a list and
// later we'll tell the backend about it and scan it.
Subdirectory new_subdir;
new_subdir.directory_id = t->dir();
new_subdir.directory_id = -1;
new_subdir.path = child;
new_subdir.mtime = child_info.lastModified().toTime_t();
my_new_subdirs << new_subdir;
@ -253,7 +263,8 @@ void LibraryWatcher::ScanSubdirectory(
// Add this subdir to the new or touched list
Subdirectory updated_subdir;
updated_subdir.directory_id = t->dir();
updated_subdir.mtime = path_info.lastModified().toTime_t();
updated_subdir.mtime = path_info.exists() ?
path_info.lastModified().toTime_t() : 0;
updated_subdir.path = path;
if (subdir.directory_id == -1)
@ -265,7 +276,6 @@ void LibraryWatcher::ScanSubdirectory(
foreach (const Subdirectory& my_new_subdir, my_new_subdirs) {
ScanSubdirectory(my_new_subdir.path, my_new_subdir, t, true);
}
t->new_subdirs << my_new_subdirs;
}
void LibraryWatcher::AddWatch(QFileSystemWatcher* w, const QString& path) {
@ -275,6 +285,10 @@ void LibraryWatcher::AddWatch(QFileSystemWatcher* w, const QString& path) {
return;
}
#endif
if (!QFile::exists(path))
return;
w->addPath(path);
}

View File

@ -73,6 +73,8 @@ class LibraryWatcher : public QObject {
~ScanTransaction();
SongList FindSongsInSubdirectory(const QString& path);
bool HasSeenSubdir(const QString& path);
void SetKnownSubdirs(const SubdirectoryList& subdirs);
int dir() const { return dir_; }
bool is_incremental() const { return incremental_; }
@ -90,8 +92,12 @@ class LibraryWatcher : public QObject {
int dir_;
bool incremental_;
LibraryWatcher* watcher_;
SongList cached_songs_;
bool cached_songs_dirty_;
SubdirectoryList known_subdirs_;
bool known_subdirs_dirty_;
};
private slots:
@ -107,13 +113,11 @@ class LibraryWatcher : public QObject {
static QString PickBestImage(const QStringList& images);
static QString ImageForSong(const QString& path, QMap<QString, QStringList>& album_art);
void AddWatch(QFileSystemWatcher* w, const QString& path);
bool HasSeenSubdir(int id, const QString& path) const;
private:
// One of these gets stored for each Directory we're watching
struct DirData {
Directory dir;
SubdirectoryList known_subdirs;
QFileSystemWatcher* watcher;
};