20 m_dir(m_dbfs.GetImageFilters())
40 m_mutexQueue.unlock();
42 QMutexLocker locker(&m_mutexState);
54 QMutexLocker locker(&m_mutexState);
66 QMutexLocker locker(&m_mutexQueue);
67 return !m_clearQueue.isEmpty();
81 QMutexLocker locker(&m_mutexState);
101 m_clearQueue << qMakePair(devId,
action);
102 m_mutexQueue.unlock();
115 QMutexLocker locker(&m_mutexProgress);
117 << QString::number(m_progressCount)
118 << QString::number(m_progressTotalCount);
132 setPriority(QThread::LowPriority);
134 bool at_least_once {
true };
135 while (ClearsPending() || at_least_once)
137 at_least_once =
false;
140 while (ClearsPending())
143 if (m_clearQueue.isEmpty())
145 ClearTask task = m_clearQueue.takeFirst();
146 m_mutexQueue.unlock();
148 int devId = task.first;
149 QString
action = task.second;
151 LOG(VB_GENERAL, LOG_INFO,
152 QString(
"Clearing Filesystem: %1 %2").arg(
action).arg(devId));
155 m_dbfs.ClearDb(devId,
action);
158 m_thumb.ClearThumbs(devId,
action);
164 LOG(VB_GENERAL, LOG_INFO,
"Starting scan");
167 if (!m_dbfs.ReadAllImages(m_dbFileMap, m_dbDirMap))
171 bool firstScan = m_dbFileMap.isEmpty();
174 m_thumb.PauseBackground(
true);
179 CountFiles(
paths.values());
183 m_changedImages.clear();
184 StringMap::const_iterator i =
paths.constBegin();
185 while (i !=
paths.constEnd() && IsScanning())
187 LOG(VB_FILE, LOG_INFO, QString(
"Synchronizing %1").arg(i.value()));
190 SyncSubTree(QFileInfo(i.value()),
GALLERY_DB_ID, i.key(), i.value());
192 LOG(VB_FILE, LOG_INFO, QString(
"Synchronize took %2 seconds")
193 .arg(timer.elapsed()/1000));
197 m_thumb.PauseBackground(
false);
202 m_dbfs.RemoveFromDB(QVector<ImagePtr>::fromList(m_dbDirMap.values()));
203 m_dbfs.RemoveFromDB(QVector<ImagePtr>::fromList(m_dbFileMap.values()));
206 QStringList mesg(m_thumb.DeleteThumbs(QVector<ImagePtr>::fromList(m_dbFileMap.values())));
207 mesg << m_changedImages.join(
",");
214 m_mutexProgress.lock();
216 Broadcast(m_progressTotalCount);
218 m_progressCount = m_progressTotalCount = 0;
219 m_mutexProgress.unlock();
221 LOG(VB_GENERAL, LOG_INFO,
"Finished scan");
226 std::this_thread::sleep_for(1s);
229 m_dbfs.Notify(
"IMAGE_DB_CHANGED", mesg);
250 int devId,
const QString &base)
253 if (m_exclusions.match(dirInfo.fileName()).hasMatch())
255 LOG(VB_FILE, LOG_INFO,
256 QString(
"Excluding dir %1").arg(dirInfo.absoluteFilePath()));
262 if (!dir.cd(dirInfo.absoluteFilePath()))
264 LOG(VB_FILE, LOG_INFO,
265 QString(
"Failed to open dir %1").arg(dirInfo.absoluteFilePath()));
270 int id = SyncDirectory(dirInfo, devId, base, parentId);
273 LOG(VB_FILE, LOG_INFO,
274 QString(
"Failed to sync dir %1").arg(dirInfo.absoluteFilePath()));
279 QFileInfoList entries = dir.entryInfoList();
280 for (
const auto & fileInfo : std::as_const(entries))
284 LOG(VB_GENERAL, LOG_INFO,
285 QString(
"Scan interrupted in %2").arg(dirInfo.absoluteFilePath()));
289 if (fileInfo.isDir())
292 SyncSubTree(fileInfo,
id, devId, base);
296 SyncFile(fileInfo, devId, base,
id);
298 QMutexLocker locker(&m_mutexProgress);
302 if (m_bcastTimer.elapsed() > 250)
303 Broadcast(m_progressCount);
326 QString absFilePath = dirInfo.absoluteFilePath();
328 LOG(VB_FILE, LOG_DEBUG, QString(
"Syncing directory %1").arg(absFilePath));
330 ImagePtr dir(m_dbfs.CreateItem(dirInfo, parentId, devId, base));
333 if (m_dbDirMap.contains(dir->m_filePath))
335 ImagePtr dbDir = m_dbDirMap.value(dir->m_filePath);
336 if (dbDir ==
nullptr)
340 dir->m_id = dbDir->m_id;
343 if (dir->m_modTime != dbDir->m_modTime
344 || dir->m_parentId != dbDir->m_parentId)
346 LOG(VB_FILE, LOG_INFO,
347 QString(
"Changed directory %1").arg(absFilePath));
350 dir->m_isHidden = dbDir->m_isHidden;
351 dir->m_userThumbnail = dbDir->m_userThumbnail;
353 m_dbfs.UpdateDbImage(*dir);
355 m_changedImages << QString::number(dir->m_id);
359 m_dbDirMap.remove(dir->m_filePath);
362 else if (m_seenDir.contains(dir->m_filePath))
364 ImagePtr cloneDir = m_seenDir.value(dir->m_filePath);
365 if (cloneDir ==
nullptr)
369 if (cloneDir->m_modTime >= dir->m_modTime )
371 LOG(VB_FILE, LOG_INFO, QString(
"Directory %1 is an older clone of %2")
372 .arg(absFilePath, cloneDir->m_filePath));
379 LOG(VB_FILE, LOG_INFO,
380 QString(
"Directory %1 is a more recent clone of %2")
381 .arg(absFilePath, cloneDir->m_filePath));
384 dir->m_id = cloneDir->m_id;
386 m_changedImages << QString::number(dir->m_id);
390 if (!dir->IsDevice())
393 m_dbfs.UpdateDbImage(*dir);
398 LOG(VB_FILE, LOG_INFO, QString(
"New directory %1").arg(absFilePath));
401 dir->m_id = m_dbfs.InsertDbImage(*dir);
405 m_seenDir.insert(dir->m_filePath, dir);
421 const QString &path,
int type, QString &comment,
422 std::chrono::seconds &time,
431 comment = metadata->
GetComment().simplified();
433 time = (dt.isValid()) ? std::chrono::seconds(dt.toSecsSinceEpoch()) : 0s;
455 const QString &base,
int parentId)
458 if (m_exclusions.match(fileInfo.fileName()).hasMatch())
460 LOG(VB_FILE, LOG_INFO,
461 QString(
"Excluding file %1").arg(fileInfo.absoluteFilePath()));
465 QString absFilePath = fileInfo.absoluteFilePath();
467 ImagePtr im(m_dbfs.CreateItem(fileInfo, parentId, devId, base));
472 if (m_dbFileMap.contains(im->m_filePath))
474 ImagePtrK dbIm = m_dbFileMap.value(im->m_filePath);
477 if (im->m_modTime == dbIm->m_modTime && im->m_parentId == dbIm->m_parentId)
481 m_dbFileMap.remove(im->m_filePath);
483 m_seenFile.insert(im->m_filePath, absFilePath);
487 LOG(VB_FILE, LOG_INFO, QString(
"Modified file %1").arg(absFilePath));
490 im->m_id = dbIm->m_id;
491 im->m_isHidden = dbIm->m_isHidden;
495 PopulateMetadata(absFilePath, im->m_type,
496 im->m_comment, im->m_date, fileOrient);
503 m_dbFileMap.remove(im->m_filePath);
505 m_changedImages << QString::number(im->m_id);
508 m_dbfs.UpdateDbImage(*im);
510 else if (m_seenFile.contains(im->m_filePath))
512 LOG(VB_GENERAL, LOG_WARNING, QString(
"Ignoring %1 (Duplicate of %2)")
513 .arg(absFilePath, m_seenFile.value(im->m_filePath)));
519 LOG(VB_FILE, LOG_INFO, QString(
"New file %1").arg(absFilePath));
523 PopulateMetadata(absFilePath, im->m_type,
524 im->m_comment, im->m_date, fileOrient);
530 im->m_id = m_dbfs.InsertDbImage(*im);
534 m_seenFile.insert(im->m_filePath, absFilePath);
537 im->m_filePath = absFilePath;
540 m_thumb.CreateThumbnail(im);
552 QFileInfoList entries = dir.entryInfoList();
553 for (
const auto & fileInfo : std::as_const(entries))
556 if (m_exclusions.match(fileInfo.fileName()).hasMatch())
559 if (fileInfo.isFile())
561 ++m_progressTotalCount;
564 else if (dir.cd(fileInfo.fileName()))
584 excPattern.replace(
".",
"\\.");
585 excPattern.replace(
"*",
".*");
586 excPattern.replace(
"?",
".");
587 excPattern.replace(
",",
"|");
589 QString pattern = QString(
"^(%1)$").arg(excPattern);
590 m_exclusions = QRegularExpression(pattern);
592 LOG(VB_FILE, LOG_DEBUG, QString(
"Exclude regexp is \"%1\"").arg(pattern));
595 QMutexLocker locker(&m_mutexProgress);
597 m_progressTotalCount = 0;
601 for (
const auto& sgDir : std::as_const(
paths))
606 LOG(VB_FILE, LOG_INFO, QString(
"Counting %1").arg(dir.absolutePath()));
607 int startCount {m_progressTotalCount};
611 LOG(VB_FILE, LOG_INFO, QString(
"Counted %1 files in %2 seconds")
612 .arg(m_progressTotalCount - startCount)
613 .arg(timer.elapsed()/1000));
633 << QString::number(m_progressTotalCount);
635 m_dbfs.Notify(
"IMAGE_SCAN_STATUS", status);
638 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)