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:
parent
9151ba05d4
commit
dacdf4cc5a
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user