32 QMutexLocker locker(&m_mutex);
34 m_backgroundQ.clear();
49 QMutexLocker locker(&m_mutex);
51 m_backgroundQ.insert(task->m_priority, task);
53 m_requestQ.insert(task->m_priority, task);
56 if ((m_doBackground || !background) && !this->
isRunning())
69 if (
action ==
"DEVICE CLOSE ALL" ||
action ==
"DEVICE CLEAR ALL")
72 LOG(VB_FILE, LOG_INFO,
73 QString(
"Aborting all thumbnails %1").arg(
action));
80 LOG(VB_FILE, LOG_INFO, QString(
"Aborting thumbnails (%1) for '%2'")
81 .arg(
action).arg(m_dbfs.DeviceName(devId)));
84 QMutexLocker locker(&m_mutex);
85 RemoveTasks(m_requestQ, devId);
86 RemoveTasks(m_backgroundQ, devId);
89 m_taskDone.wait(&m_mutex, 3000);
93 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
94 #define QMutableMultiMapIterator QMutableMapIterator
100 template <
class DBFS>
103 QMutableMultiMapIterator<int, TaskPtr> it(queue);
109 if (task && !task->m_images.isEmpty()
110 && task->m_images.at(0)->m_device == devId)
122 template <
class DBFS>
127 setPriority(QThread::LowestPriority);
132 m_taskDone.wakeAll();
135 QThread::yieldCurrentThread();
140 QMutexLocker locker(&m_mutex);
141 if (!m_requestQ.isEmpty())
142 task = m_requestQ.take(m_requestQ.constBegin().key());
143 else if (m_doBackground && !m_backgroundQ.isEmpty())
144 task = m_backgroundQ.take(m_backgroundQ.constBegin().key());
151 if (task->m_images.isEmpty())
154 if (task->m_action ==
"CREATE")
158 QString err = CreateThumbnail(im, task->m_priority);
162 LOG(VB_GENERAL, LOG_ERR, QString(
"%1").arg(err));
164 else if (task->m_notify)
167 m_dbfs.Notify(
"THUMB_AVAILABLE",
168 QStringList(QString::number(im->m_id)));
171 else if (task->m_action ==
"DELETE")
173 for (
const auto& im : qAsConst(task->m_images))
175 QString thumbnail = im->m_thumbPath;
176 if (!QDir::root().remove(thumbnail))
178 LOG(VB_FILE, LOG_WARNING,
179 QString(
"Failed to delete thumbnail %1").arg(thumbnail));
182 LOG(VB_FILE, LOG_DEBUG,
183 QString(
"Deleted thumbnail %1").arg(thumbnail));
186 QString path = QFileInfo(thumbnail).path();
187 if (QDir::root().rmpath(path))
188 LOG(VB_FILE, LOG_DEBUG,
189 QString(
"Cleaned up path %1").arg(path));
192 else if (task->m_action ==
"MOVE")
194 for (
const auto& im : qAsConst(task->m_images))
197 QString newThumbPath =
198 m_dbfs.GetAbsThumbPath(m_dbfs.ThumbDir(im->m_device),
199 m_dbfs.ThumbPath(*im.data()));
202 if (QDir::root().mkpath(QFileInfo(newThumbPath).path())
203 && QFile::rename(im->m_thumbPath, newThumbPath))
205 LOG(VB_FILE, LOG_DEBUG, QString(
"Moved thumbnail %1 -> %2")
206 .arg(im->m_thumbPath, newThumbPath));
210 LOG(VB_FILE, LOG_WARNING,
211 QString(
"Failed to rename thumbnail %1 -> %2")
212 .arg(im->m_thumbPath, newThumbPath));
217 QString path = QFileInfo(im->m_thumbPath).path();
218 if (QDir::root().rmpath(path))
219 LOG(VB_FILE, LOG_DEBUG,
220 QString(
"Cleaned up path %1").arg(path));
224 LOG(VB_GENERAL, LOG_ERR,
225 QString(
"Unknown task %1").arg(task->m_action));
237 template <
class DBFS>
240 if (QDir::root().exists(im->m_thumbPath))
242 LOG(VB_FILE, LOG_DEBUG, QString(
"[%3] %2 already exists")
243 .arg(im->m_thumbPath).arg(thumbPriority));
250 QString imagePath = m_dbfs.GetAbsFilePath(im);
251 if (imagePath.isEmpty())
252 return QString(
"Empty image path: %1").arg(im->m_filePath);
255 QDir::root().mkpath(QFileInfo(im->m_thumbPath).path());
260 if (!image.load(imagePath))
261 return QString(
"Failed to open image %1").arg(imagePath);
264 image = image.scaled(QSize(240,180), Qt::KeepAspectRatio, Qt::SmoothTransformation);
271 args <<
"--size 320x240";
272 args << QString(
"--infile '%1'").arg(imagePath);
273 args << QString(
"--outfile '%1'").arg(im->m_thumbPath);
287 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to run %2 %3")
288 .arg(cmd,
args.join(
" ")));
289 return QString(
"Preview Generator failed for %1").arg(imagePath);
292 if (!image.load(im->m_thumbPath))
293 return QString(
"Failed to open preview %1").arg(im->m_thumbPath);
296 return QString(
"Can't create thumbnail for type %1 (image %2)")
297 .arg(im->m_type).arg(imagePath);
308 if (!image.save(im->m_thumbPath))
309 return QString(
"Failed to create thumbnail %1").arg(im->m_thumbPath);
311 LOG(VB_FILE, LOG_INFO, QString(
"[%2] Created %1")
312 .arg(im->m_thumbPath).arg(thumbPriority));
320 template <
class DBFS>
323 QMutexLocker locker(&m_mutex);
324 m_doBackground = !pause;
327 if (m_doBackground && !this->
isRunning())
335 template <
class DBFS>
338 m_imageThread(new
ThumbThread<DBFS>(
"ImageThumbs", dbfs)),
339 m_videoThread(new
ThumbThread<DBFS>(
"VideoThumbs", dbfs))
346 template <
class DBFS>
349 delete m_imageThread;
350 delete m_videoThread;
351 m_imageThread = m_videoThread =
nullptr;
361 template <
class DBFS>
367 m_imageThread->AbortDevice(devId,
action);
369 m_videoThread->AbortDevice(devId,
action);
372 QStringList mountPaths = m_dbfs.CloseDevices(devId,
action);
378 for (
const auto& mount : qAsConst(mountPaths))
379 mesg << m_dbfs.MakeFileUrl(mount)
380 << m_dbfs.MakeThumbUrl(mount);
383 m_dbfs.Notify(
"IMAGE_DEVICE_CHANGED", mesg);
392 template <
class DBFS>
401 for (
const auto& im : qAsConst(images))
408 ids << QString::number(im->m_id);
411 if (!pics.isEmpty() && m_imageThread)
413 if (!videos.isEmpty() && m_videoThread)
415 return ids.join(
",");
425 template <
class DBFS>
438 if (im->m_type ==
kImageFile && m_imageThread)
440 m_imageThread->Enqueue(task);
442 else if (im->m_type ==
kVideoFile && m_videoThread)
444 m_videoThread->Enqueue(task);
448 LOG(VB_FILE, LOG_INFO, QString(
"Ignoring create thumbnail %1, type %2")
449 .arg(im->m_id).arg(im->m_type));
458 template <
class DBFS>
466 if (im->m_type ==
kImageFile && m_imageThread)
468 m_imageThread->Enqueue(task);
470 else if (im->m_type ==
kVideoFile && m_videoThread)
472 m_videoThread->Enqueue(task);
476 LOG(VB_FILE, LOG_INFO, QString(
"Ignoring move thumbnail %1, type %2")
477 .arg(im->m_id).arg(im->m_type));
485 template <
class DBFS>
488 LOG(VB_FILE, LOG_INFO, QString(
"Paused %1").arg(pause));
491 m_imageThread->PauseBackground(pause);
493 m_videoThread->PauseBackground(pause);