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 : std::as_const(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 : std::as_const(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));
225 LOG(VB_GENERAL, LOG_ERR,
226 QString(
"Unknown task %1").arg(task->m_action));
239 template <
class DBFS>
242 if (QDir::root().
exists(im->m_thumbPath))
244 LOG(VB_FILE, LOG_DEBUG, QString(
"[%3] %2 already exists")
245 .arg(im->m_thumbPath).arg(thumbPriority));
252 QString imagePath = m_dbfs.GetAbsFilePath(im);
253 if (imagePath.isEmpty())
254 return QString(
"Empty image path: %1").arg(im->m_filePath);
257 QDir::root().mkpath(QFileInfo(im->m_thumbPath).path());
262 if (!image.load(imagePath))
263 return QString(
"Failed to open image %1").arg(imagePath);
266 image = image.scaled(QSize(240,180), Qt::KeepAspectRatio, Qt::SmoothTransformation);
273 args <<
"--size 320x240";
274 args << QString(
"--infile '%1'").arg(imagePath);
275 args << QString(
"--outfile '%1'").arg(im->m_thumbPath);
289 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to run %2 %3")
290 .arg(cmd,
args.join(
" ")));
291 return QString(
"Preview Generator failed for %1").arg(imagePath);
294 if (!image.load(im->m_thumbPath))
295 return QString(
"Failed to open preview %1").arg(im->m_thumbPath);
299 return QString(
"Can't create thumbnail for type %1 (image %2)")
300 .arg(im->m_type).arg(imagePath);
312 if (!image.save(im->m_thumbPath))
313 return QString(
"Failed to create thumbnail %1").arg(im->m_thumbPath);
315 LOG(VB_FILE, LOG_INFO, QString(
"[%2] Created %1")
316 .arg(im->m_thumbPath).arg(thumbPriority));
324 template <
class DBFS>
327 QMutexLocker locker(&m_mutex);
328 m_doBackground = !pause;
331 if (m_doBackground && !this->
isRunning())
339 template <
class DBFS>
342 m_imageThread(new
ThumbThread<DBFS>(
"ImageThumbs", dbfs)),
343 m_videoThread(new
ThumbThread<DBFS>(
"VideoThumbs", dbfs))
350 template <
class DBFS>
353 delete m_imageThread;
354 delete m_videoThread;
355 m_imageThread = m_videoThread =
nullptr;
365 template <
class DBFS>
371 m_imageThread->AbortDevice(devId,
action);
373 m_videoThread->AbortDevice(devId,
action);
376 QStringList mountPaths = m_dbfs.CloseDevices(devId,
action);
382 for (
const auto& mount : std::as_const(mountPaths))
383 mesg << m_dbfs.MakeFileUrl(mount)
384 << m_dbfs.MakeThumbUrl(mount);
387 m_dbfs.Notify(
"IMAGE_DEVICE_CHANGED", mesg);
396 template <
class DBFS>
405 for (
const auto& im : std::as_const(images))
412 ids << QString::number(im->m_id);
415 if (!pics.isEmpty() && m_imageThread)
417 if (!videos.isEmpty() && m_videoThread)
419 return ids.join(
",");
429 template <
class DBFS>
442 if (im->m_type ==
kImageFile && m_imageThread)
444 m_imageThread->Enqueue(task);
446 else if (im->m_type ==
kVideoFile && m_videoThread)
448 m_videoThread->Enqueue(task);
452 LOG(VB_FILE, LOG_INFO, QString(
"Ignoring create thumbnail %1, type %2")
453 .arg(im->m_id).arg(im->m_type));
462 template <
class DBFS>
470 if (im->m_type ==
kImageFile && m_imageThread)
472 m_imageThread->Enqueue(task);
474 else if (im->m_type ==
kVideoFile && m_videoThread)
476 m_videoThread->Enqueue(task);
480 LOG(VB_FILE, LOG_INFO, QString(
"Ignoring move thumbnail %1, type %2")
481 .arg(im->m_id).arg(im->m_type));
489 template <
class DBFS>
492 LOG(VB_FILE, LOG_INFO, QString(
"Paused %1").arg(pause));
495 m_imageThread->PauseBackground(pause);
497 m_videoThread->PauseBackground(pause);