18 m_dir(m_dbfs.GetImageFilters())
38 m_mutexQueue.unlock();
40 QMutexLocker locker(&m_mutexState);
52 QMutexLocker locker(&m_mutexState);
64 QMutexLocker locker(&m_mutexQueue);
65 return !m_clearQueue.isEmpty();
79 QMutexLocker locker(&m_mutexState);
99 m_clearQueue << qMakePair(devId,
action);
100 m_mutexQueue.unlock();
113 QMutexLocker locker(&m_mutexProgress);
115 << QString::number(m_progressCount)
116 << QString::number(m_progressTotalCount);
130 setPriority(QThread::LowPriority);
132 bool at_least_once {
true };
133 while (ClearsPending() || at_least_once)
135 at_least_once =
false;
138 while (ClearsPending())
141 if (m_clearQueue.isEmpty())
143 ClearTask task = m_clearQueue.takeFirst();
144 m_mutexQueue.unlock();
146 int devId = task.first;
147 QString
action = task.second;
149 LOG(VB_GENERAL, LOG_INFO,
150 QString(
"Clearing Filesystem: %1 %2").arg(
action).arg(devId));
153 m_dbfs.ClearDb(devId,
action);
156 m_thumb.ClearThumbs(devId,
action);
162 LOG(VB_GENERAL, LOG_INFO,
"Starting scan");
165 if (!m_dbfs.ReadAllImages(m_dbFileMap, m_dbDirMap))
169 bool firstScan = m_dbFileMap.isEmpty();
172 m_thumb.PauseBackground(
true);
177 CountFiles(
paths.values());
181 m_changedImages.clear();
182 StringMap::const_iterator i =
paths.constBegin();
183 while (i !=
paths.constEnd() && IsScanning())
185 SyncSubTree(QFileInfo(i.value()),
GALLERY_DB_ID, i.key(), i.value());
190 m_thumb.PauseBackground(
false);
195 m_dbfs.RemoveFromDB(QVector<ImagePtr>::fromList(m_dbDirMap.values()));
196 m_dbfs.RemoveFromDB(QVector<ImagePtr>::fromList(m_dbFileMap.values()));
199 QStringList mesg(m_thumb.DeleteThumbs(QVector<ImagePtr>::fromList(m_dbFileMap.values())));
200 mesg << m_changedImages.join(
",");
207 m_mutexProgress.lock();
209 Broadcast(m_progressTotalCount);
211 m_progressCount = m_progressTotalCount = 0;
212 m_mutexProgress.unlock();
214 LOG(VB_GENERAL, LOG_INFO,
"Finished scan");
222 m_dbfs.Notify(
"IMAGE_DB_CHANGED", mesg);
243 int devId,
const QString &base)
246 if (m_exclusions.match(dirInfo.fileName()).hasMatch())
248 LOG(VB_FILE, LOG_INFO,
249 QString(
"Excluding dir %1").arg(dirInfo.absoluteFilePath()));
255 if (!dir.cd(dirInfo.absoluteFilePath()))
257 LOG(VB_FILE, LOG_INFO,
258 QString(
"Failed to open dir %1").arg(dirInfo.absoluteFilePath()));
263 int id = SyncDirectory(dirInfo, devId, base, parentId);
266 LOG(VB_FILE, LOG_INFO,
267 QString(
"Failed to sync dir %1").arg(dirInfo.absoluteFilePath()));
272 QFileInfoList entries = dir.entryInfoList();
273 for (
const auto & fileInfo : std::as_const(entries))
277 LOG(VB_GENERAL, LOG_INFO,
278 QString(
"Scan interrupted in %2").arg(dirInfo.absoluteFilePath()));
282 if (fileInfo.isDir())
285 SyncSubTree(fileInfo,
id, devId, base);
289 SyncFile(fileInfo, devId, base,
id);
291 QMutexLocker locker(&m_mutexProgress);
295 if (m_bcastTimer.elapsed() > 250)
296 Broadcast(m_progressCount);
319 QString absFilePath = dirInfo.absoluteFilePath();
321 LOG(VB_FILE, LOG_DEBUG, QString(
"Syncing directory %1").arg(absFilePath));
323 ImagePtr dir(m_dbfs.CreateItem(dirInfo, parentId, devId, base));
326 if (m_dbDirMap.contains(dir->m_filePath))
328 ImagePtr dbDir = m_dbDirMap.value(dir->m_filePath);
329 if (dbDir ==
nullptr)
333 dir->m_id = dbDir->m_id;
336 if (dir->m_modTime != dbDir->m_modTime
337 || dir->m_parentId != dbDir->m_parentId)
339 LOG(VB_FILE, LOG_INFO,
340 QString(
"Changed directory %1").arg(absFilePath));
343 dir->m_isHidden = dbDir->m_isHidden;
344 dir->m_userThumbnail = dbDir->m_userThumbnail;
346 m_dbfs.UpdateDbImage(*dir);
348 m_changedImages << QString::number(dir->m_id);
352 m_dbDirMap.remove(dir->m_filePath);
355 else if (m_seenDir.contains(dir->m_filePath))
357 ImagePtr cloneDir = m_seenDir.value(dir->m_filePath);
358 if (cloneDir ==
nullptr)
362 if (cloneDir->m_modTime >= dir->m_modTime )
364 LOG(VB_FILE, LOG_INFO, QString(
"Directory %1 is an older clone of %2")
365 .arg(absFilePath, cloneDir->m_filePath));
372 LOG(VB_FILE, LOG_INFO,
373 QString(
"Directory %1 is a more recent clone of %2")
374 .arg(absFilePath, cloneDir->m_filePath));
377 dir->m_id = cloneDir->m_id;
379 m_changedImages << QString::number(dir->m_id);
383 if (!dir->IsDevice())
386 m_dbfs.UpdateDbImage(*dir);
391 LOG(VB_FILE, LOG_INFO, QString(
"New directory %1").arg(absFilePath));
394 dir->m_id = m_dbfs.InsertDbImage(*dir);
398 m_seenDir.insert(dir->m_filePath, dir);
414 const QString &path,
int type, QString &comment,
415 std::chrono::seconds &time,
424 comment = metadata->
GetComment().simplified();
426 time = (dt.isValid()) ? std::chrono::seconds(dt.toSecsSinceEpoch()) : 0s;
448 const QString &base,
int parentId)
451 if (m_exclusions.match(fileInfo.fileName()).hasMatch())
453 LOG(VB_FILE, LOG_INFO,
454 QString(
"Excluding file %1").arg(fileInfo.absoluteFilePath()));
458 QString absFilePath = fileInfo.absoluteFilePath();
460 ImagePtr im(m_dbfs.CreateItem(fileInfo, parentId, devId, base));
465 if (m_dbFileMap.contains(im->m_filePath))
467 ImagePtrK dbIm = m_dbFileMap.value(im->m_filePath);
470 if (im->m_modTime == dbIm->m_modTime && im->m_parentId == dbIm->m_parentId)
474 m_dbFileMap.remove(im->m_filePath);
476 m_seenFile.insert(im->m_filePath, absFilePath);
480 LOG(VB_FILE, LOG_INFO, QString(
"Modified file %1").arg(absFilePath));
483 im->m_id = dbIm->m_id;
484 im->m_isHidden = dbIm->m_isHidden;
488 PopulateMetadata(absFilePath, im->m_type,
489 im->m_comment, im->m_date, fileOrient);
496 m_dbFileMap.remove(im->m_filePath);
498 m_changedImages << QString::number(im->m_id);
501 m_dbfs.UpdateDbImage(*im);
503 else if (m_seenFile.contains(im->m_filePath))
505 LOG(VB_GENERAL, LOG_WARNING, QString(
"Ignoring %1 (Duplicate of %2)")
506 .arg(absFilePath, m_seenFile.value(im->m_filePath)));
512 LOG(VB_FILE, LOG_INFO, QString(
"New file %1").arg(absFilePath));
516 PopulateMetadata(absFilePath, im->m_type,
517 im->m_comment, im->m_date, fileOrient);
523 im->m_id = m_dbfs.InsertDbImage(*im);
527 m_seenFile.insert(im->m_filePath, absFilePath);
530 im->m_filePath = absFilePath;
533 m_thumb.CreateThumbnail(im);
545 QFileInfoList entries = dir.entryInfoList();
546 for (
const auto & fileInfo : std::as_const(entries))
549 if (m_exclusions.match(fileInfo.fileName()).hasMatch())
552 if (fileInfo.isFile())
554 ++m_progressTotalCount;
557 else if (dir.cd(fileInfo.fileName()))
577 excPattern.replace(
".",
"\\.");
578 excPattern.replace(
"*",
".*");
579 excPattern.replace(
"?",
".");
580 excPattern.replace(
",",
"|");
582 QString pattern = QString(
"^(%1)$").arg(excPattern);
583 m_exclusions = QRegularExpression(pattern);
585 LOG(VB_FILE, LOG_DEBUG, QString(
"Exclude regexp is \"%1\"").arg(pattern));
588 QMutexLocker locker(&m_mutexProgress);
590 m_progressTotalCount = 0;
594 for (
const auto& sgDir : std::as_const(
paths))
618 << QString::number(m_progressTotalCount);
620 m_dbfs.Notify(
"IMAGE_SCAN_STATUS", status);
623 m_bcastTimer.start();
~ImageScanThread() override
bool ClearsPending()
Get status of 'clear device' queue.
void CountTree(QDir &dir)
Counts images in a dir subtree.
void EnqueueClear(int devId, const QString &action)
Queues a 'Clear Device' request, which will be actioned immediately.
void PopulateMetadata(const QString &path, int type, QString &comment, std::chrono::seconds &time, int &orientation)
Read image date, orientation, comment from metadata.
bool IsScanning()
Return current scanner status.
ImageScanThread(DBFS *dbfs, ImageThumb< DBFS > *thumbGen)
Constructor.
int SyncDirectory(const QFileInfo &dirInfo, int devId, const QString &base, int parentId)
Updates/populates db for a dir.
void ChangeState(bool scan)
Run or interrupt scanner.
void SyncFile(const QFileInfo &fileInfo, int devId, const QString &base, int parentId)
Updates/populates db for an image/video file.
QStringList GetProgress()
Returns number of images scanned & total number to scan.
void CountFiles(const QStringList &paths)
Counts images in a list of subtrees.
void SyncSubTree(const QFileInfo &dirInfo, int parentId, int devId, const QString &base)
Scans a dir subtree.
void cancel()
Clears queued items so that the thread can exit.
QPair< int, QString > ClearTask
void run() override
Synchronises database to the storage group.
void Broadcast(int progress)
Notify listeners of scan progress.
This is a wrapper around QThread that does several additional things.
QString GetSetting(const QString &key, const QString &defaultval="")
bool IsBackend(void) const
is this process a backend process
Encapsulates Exif orientation processing.
int Composite() const
Encode original & current orientation to a single Db field.
int GetCurrent() const
Determines orientation required for an image.
Manages a collection of images.
Synchronises image database to filesystem.
QSharedPointer< ImageItemK > ImagePtrK
QMap< int, QString > StringMap
static constexpr int GALLERY_DB_ID
@ kCloneDir
A device sub dir comprised from multiple SG dirs.
QSharedPointer< ImageItem > ImagePtr
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
static bool isRunning(const char *program)
Returns true if a program containing the specified string is running on this machine.
def scan(profile, smoonURL, gate)