/* This file is part of Clementine. Copyright 2010, David Sansome 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 . */ #ifndef LIBRARYWATCHER_H #define LIBRARYWATCHER_H #include "directory.h" #include "core/song.h" #include #include #include class QFileSystemWatcher; class QTimer; class LibraryBackend; class TaskManager; class LibraryWatcher : public QObject { Q_OBJECT public: LibraryWatcher(QObject* parent = 0); static const char* kSettingsGroup; void set_backend(LibraryBackend* backend) { backend_ = backend; } void set_task_manager(TaskManager* task_manager) { task_manager_ = task_manager; } void set_device_name(const QString& device_name) { device_name_ = device_name; } void IncrementalScanAsync(); void SetRescanPausedAsync(bool pause); void ReloadSettingsAsync(); void Stop() { stop_requested_ = true; } signals: void NewOrUpdatedSongs(const SongList& songs); void SongsMTimeUpdated(const SongList& songs); void SongsDeleted(const SongList& songs); void SubdirsDiscovered(const SubdirectoryList& subdirs); void SubdirsMTimeUpdated(const SubdirectoryList& subdirs); void CompilationsNeedUpdating(); void ScanStarted(int task_id); public slots: void ReloadSettings(); void AddDirectory(const Directory& dir, const SubdirectoryList& subdirs); void RemoveDirectory(const Directory& dir); void SetRescanPaused(bool pause); private: // This class encapsulates a full or partial scan of a directory. // Each directory has one or more subdirectories, and any number of // subdirectories can be scanned during one transaction. ScanSubdirectory() // adds its results to the members of this transaction class, and they are // "committed" through calls to the LibraryBackend in the transaction's dtor. // The transaction also caches the list of songs in this directory according // to the library. Multiple calls to FindSongsInSubdirectory during one // transaction will only result in one call to // LibraryBackend::FindSongsInDirectory. class ScanTransaction { public: ScanTransaction(LibraryWatcher* watcher, int dir, bool incremental); ~ScanTransaction(); SongList FindSongsInSubdirectory(const QString& path); bool HasSeenSubdir(const QString& path); void SetKnownSubdirs(const SubdirectoryList& subdirs); SubdirectoryList GetImmediateSubdirs(const QString& path); SubdirectoryList GetAllSubdirs(); void AddToProgress(int n = 1); void AddToProgressMax(int n); int dir() const { return dir_; } bool is_incremental() const { return incremental_; } SongList deleted_songs; SongList new_songs; SongList touched_songs; SubdirectoryList new_subdirs; SubdirectoryList touched_subdirs; private: ScanTransaction(const ScanTransaction&) {} ScanTransaction& operator =(const ScanTransaction&) { return *this; } int task_id_; int progress_; int progress_max_; int dir_; bool incremental_; LibraryWatcher* watcher_; SongList cached_songs_; bool cached_songs_dirty_; SubdirectoryList known_subdirs_; bool known_subdirs_dirty_; }; private slots: void DirectoryChanged(const QString& path); void IncrementalScanNow(); void RescanPathsNow(); void ScanSubdirectory(const QString& path, const Subdirectory& subdir, ScanTransaction* t, bool force_noincremental = false); private: static bool FindSongByPath(const SongList& list, const QString& path, Song* out); inline static QString ExtensionPart( const QString &fileName ); inline static QString DirectoryPart( const QString &fileName ); static QString PickBestImage(const QStringList& images); static QString ImageForSong(const QString& path, QMap& album_art); void AddWatch(QFileSystemWatcher* w, const QString& path); private: // One of these gets stored for each Directory we're watching struct DirData { Directory dir; QFileSystemWatcher* watcher; }; LibraryBackend* backend_; TaskManager* task_manager_; QString device_name_; bool stop_requested_; bool scan_on_startup_; bool monitor_; QMap watched_dirs_; QTimer* rescan_timer_; QMap rescan_queue_; // dir id -> list of subdirs to be scanned bool rescan_paused_; int total_watches_; static QStringList sValidImages; static QStringList sValidPlaylists; #ifdef Q_OS_DARWIN static const int kMaxWatches = 100; #endif }; // Thanks Amarok inline QString LibraryWatcher::ExtensionPart( const QString &fileName ) { return fileName.contains( '.' ) ? fileName.mid( fileName.lastIndexOf('.') + 1 ).toLower() : ""; } inline QString LibraryWatcher::DirectoryPart( const QString &fileName ) { return fileName.section( '/', 0, -2 ); } #endif // LIBRARYWATCHER_H