Go to the documentation of this file.
3 #include <QImageReader>
13 #define LOC QString("ImageManager: ")
14 #define DBLOC QString("ImageDb(%1): ").arg(m_table)
19 static constexpr
const char*
DB_TABLE {
"gallery_files" };
22 #define RESULT_ERR(ERR, MESG) \
23 { LOG(VB_GENERAL, LOG_ERR, LOC + (MESG)); \
24 return QStringList("ERROR") << (ERR); }
26 #define RESULT_OK(MESG) \
27 { LOG(VB_FILE, LOG_DEBUG, LOC + (MESG)); \
28 return QStringList("OK"); }
31 static constexpr
const char*
IMPORTDIR {
"Import" };
73 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Ejecting '%1' at '%2'")
79 LOG(VB_MEDIA, LOG_DEBUG,
LOC + QString(
"Unlocked '%1'").arg(
m_name));
94 QDir(path).removeRecursively();
102 QString dirFmt = QString(
"%1/") %
TEMP_SUBDIR %
"/%2";
104 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Removing thumbnails in %1").arg(dir));
106 QDir::root().rmpath(dir);
168 QString state(
"Known");
184 LOG(VB_GENERAL, LOG_INFO,
LOC +
185 QString(
"%1 device %2 mounted at '%3' [Id %4]")
186 .arg(state, name, mount).arg(
id));
202 if (
action ==
"DEVICE CLOSE ALL")
205 for (
auto *dev : std::as_const(
m_devices))
209 else if (
action ==
"DEVICE CLEAR ALL")
212 for (
const auto *dev : std::as_const(
m_devices)) {
215 clear << dev->m_mount;
226 if (
action ==
"DEVICE EJECT")
243 DeviceMap::const_iterator it =
m_devices.constBegin();
246 if (it.value()->m_mount == mount)
258 for (
auto it =
m_devices.constKeyValueBegin();
259 it !=
m_devices.constKeyValueEnd(); ++it)
262 paths.insert(it->first, it->second->m_mount);
272 for (
auto it =
m_devices.constKeyValueBegin();
273 it !=
m_devices.constKeyValueEnd(); it++)
287 m_imageFileExt(SupportedImages()),
288 m_videoFileExt(SupportedVideos())
293 for (
const auto& ext : std::as_const(allExt))
298 m_dirFilter.setFilter(QDir::AllDirs | QDir::Files | QDir::Readable |
299 QDir::NoDotAndDotDot | QDir::NoSymLinks);
303 m_dirFilter.setSorting(QDir::DirsLast | QDir::Time | QDir::Reversed);
314 QList<QByteArray> supported = QImageReader::supportedImageFormats();
315 for (
const auto& ext : std::as_const(supported))
331 for (
const auto & fa : faList)
333 if (!fa.use_default && fa.playcommand ==
"Internal")
334 formats << QString(fa.extension);
349 int devId,
const QString & )
const
353 im->m_parentId = parentId;
354 im->m_device = devId;
355 im->m_filePath = fi.absoluteFilePath();
360 auto secs = im->m_filePath.contains(
IMPORTDIR)
361 ? fi.lastModified().toSecsSinceEpoch()
362 : QDateTime::currentSecsSinceEpoch();
363 im->m_date = std::chrono::seconds(secs);
364 im->m_modTime = im->m_date;
369 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
377 im->m_extension = fi.suffix().toLower();
387 im->m_size = fi.size();
399 const QStringList &extra)
415 int ,
const QString &base)
const
420 im->m_parentId = parentId;
427 im->m_date = std::chrono::seconds(QDateTime::currentSecsSinceEpoch());
428 im->m_modTime = im->m_date;
433 im->m_filePath = fi.absoluteFilePath().mid(base.size() + 1);
434 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
442 im->m_extension = fi.suffix().toLower();
452 im->m_size = fi.size();
464 const QStringList &extra)
479 for (
const auto& path : std::as_const(
paths))
480 map.insert(i++, path);
495 return im->m_filePath.startsWith(
"/") ? im->m_filePath
502 "file_id, filename, name, dir_id, type, modtime, size, "
503 "extension, date, hidden, orientation, angle, path, zoom"
519 im->m_filePath = query.
value(1).toString();
520 im->m_baseName = query.
value(2).toString();
521 im->m_parentId = FS::ImageId(query.
value(3).toInt());
522 im->m_type = query.
value(4).toInt();
523 im->m_modTime = std::chrono::seconds(query.
value(5).toInt());
524 im->m_size = query.
value(6).toInt();
525 im->m_extension = query.
value(7).toString();
526 im->m_date = std::chrono::seconds(query.
value(8).toUInt());
527 im->m_isHidden = query.
value(9).toBool();
528 im->m_orientation = query.
value(10).toInt();
529 im->m_userThumbnail = FS::ImageId(query.
value(11).toInt());
530 im->m_comment = query.
value(12).toString();
531 im->m_device = query.
value(13).toInt();
532 im->m_url = FS::MakeFileUrl(im->m_filePath);
537 QString thumbPath(FS::ThumbPath(*im));
538 QString devPath(FS::ThumbDir(im->m_device));
539 QString url(FS::MakeThumbUrl(devPath, thumbPath));
541 im->m_thumbPath = FS::GetAbsThumbPath(devPath, thumbPath);
542 im->m_thumbNails.append(qMakePair(im->m_id, url));
558 const QString &refine)
const
563 QString select = QString(
"file_id IN (%1) %2").arg(FS::DbIds(ids), refine);
564 return ReadImages(dirs, files, select);
578 const QString &refine)
const
580 QString select = QString(
"dir_id IN (%1) %2").arg(FS::DbIds(ids), refine);
581 return ReadImages(dirs, files, select);
597 const QString &refine)
const
600 query.
prepare(QString(
"SELECT %1 FROM %2 "
601 "WHERE (dir_id = :ID1 OR file_id = :ID2) "
605 int dbId = FS::DbId(
id);
620 else if (im->m_id ==
id)
643 if (ReadImages(dirs, files, QString(
"file_id IN (%1)").arg(FS::DbIds(ids))) < 0)
649 ", LENGTH(filename) - LENGTH(REPLACE(filename, '/', ''))"
651 "FROM %2 WHERE filename LIKE :PREFIX "
654 for (
const auto& im1 : std::as_const(dirs))
657 query.
bindValue(
":PREFIX", im1->m_filePath +
"/%");
668 if (im2->IsDirectory())
690 if (GetChildren(QString::number(
id), files, dirs, refine) < 0)
693 for (
const auto& im : std::as_const(dirs))
694 if (!GetImageTree(im->m_id, files, refine))
720 if (im->IsDirectory())
721 dirs.insert(im->m_filePath, im);
723 files.insert(im->m_filePath, im);
739 if (
action ==
"DEVICE CLOSE ALL")
745 if (
action ==
"DEVICE CLEAR ALL")
748 query.
prepare(QString(
"TRUNCATE TABLE %1;").arg(m_table));
756 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE zoom = :FS;").arg(m_table));
777 if (checkForDuplicate)
779 query.
prepare(QString(
"SELECT file_id FROM %1 WHERE filename = :NAME;")
790 if (query.
size() > 0)
792 LOG(VB_FILE, LOG_DEBUG, QString(
"Image: %1 already exists in Db")
794 return query.
value(0).toInt();
798 query.
prepare(QString(
"INSERT INTO %1 (%2) VALUES (0, "
799 ":FILEPATH, :NAME, :PARENT, :TYPE, :MODTIME, "
800 ":SIZE, :EXTENSION, :DATE, :HIDDEN, :ORIENT, "
801 ":COVER, :COMMENT, :FS);").arg(m_table,
kDBColumns));
836 "filename = :FILEPATH, name = :NAME, "
837 "dir_id = :PARENT, type = :TYPE, "
838 "modtime = :MODTIME, size = :SIZE, "
839 "extension = :EXTENSION, date = :DATE, zoom = :FS, "
840 "hidden = :HIDDEN, orientation = :ORIENT, "
841 "angle = :COVER, path = :COMMENT "
842 "WHERE file_id = :ID;").arg(m_table));
877 if (!imList.isEmpty())
879 for (
const auto& im : std::as_const(imList))
880 ids << QString::number(FS::DbId(im->m_id));
882 QString idents = ids.join(
",");
884 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE file_id IN (%2);")
885 .arg(m_table, idents));
911 query.
prepare(QString(
"UPDATE %1 SET "
913 "WHERE file_id IN (%2);").arg(m_table, FS::DbIds(ids)));
914 query.
bindValue(
":HIDDEN", hide ? 1 : 0);
934 query.
prepare(QString(
"UPDATE %1 SET "
936 "WHERE file_id = :DIR").arg(m_table));
958 query.
prepare(QString(
"UPDATE %1 SET ").arg(m_table) +
959 "orientation = :ORIENTATION "
960 "WHERE file_id = :ID");
961 query.
bindValue(
":ORIENTATION", orientation);
981 const QString &selector)
const
984 query.
prepare(QString(
"SELECT %1 FROM %2 WHERE %3")
1001 return query.
size();
1016 int &pics,
int &videos,
int &sizeKb)
const
1018 QString whereClause;
1021 whereClause =
"WHERE filename LIKE "
1022 "( SELECT CONCAT(filename, '/%') "
1023 " FROM %2 WHERE file_id = :ID);";
1027 query.
prepare(QString(
"SELECT SUM(type <= :FLDR) AS Fldr, "
1028 " SUM(type = :PIC) AS Pics, "
1029 " SUM(type = :VID) AS Vids, "
1030 " SUM(size / 1024) "
1031 "FROM %2 %1;").arg(whereClause, m_table));
1043 else if (query.
next())
1045 dirs += query.
value(0).toInt();
1046 pics += query.
value(1).toInt();
1047 videos += query.
value(2).toInt();
1048 sizeKb += query.
value(3).toInt();
1103 query.
prepare(QString(
"ALTER TABLE %1 ENGINE = MEMORY;").arg(
m_table));
1107 LOG(VB_FILE, LOG_DEBUG, QString(
"Created Db table %1").arg(
m_table));
1127 :
m_im(std::move(im)),
m_path(std::move(path)) {}
1132 QString orientation;
1148 tags.prepend(QString::number(
m_im->m_id));
1179 template <
class DBFS>
1185 if (DBFS::GetImages(
id, files, dirs) != 1)
1186 RESULT_ERR(
"Image not found", QString(
"Unknown image %1").arg(
id))
1188 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1190 QString absPath = DBFS::GetAbsFilePath(im);
1191 if (absPath.isEmpty())
1193 QString(
"File %1 not found").arg(im->m_filePath))
1199 RESULT_OK(QString(
"Fetching metadata for %1").arg(
id))
1210 template <
class DBFS>
1212 const QString &newBase)
const
1215 if (newBase.isEmpty() || newBase.contains(
"/") || newBase.contains(
"\\"))
1216 RESULT_ERR(
"Invalid name", QString(
"Invalid name %1").arg(newBase))
1221 if (DBFS::GetImages(
id, files, dirs) != 1)
1222 RESULT_ERR(
"Image not found", QString(
"Image %1 not in Db").arg(
id))
1224 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1227 QString oldPath = DBFS::GetAbsFilePath(im);
1228 if (oldPath.isEmpty())
1230 QString(
"File %1 not found").arg(im->m_filePath))
1233 QFileInfo oldFi = QFileInfo(oldPath);
1234 QString newName = im->IsDirectory()
1235 ? newBase : QString(
"%1.%2").arg(newBase, oldFi.suffix());
1237 im->m_filePath = DBFS::ConstructPath(DBFS::PathOf(im->m_filePath), newName);
1242 QString existPath = DBFS::GetAbsFilePath(im);
1243 if (!existPath.isEmpty())
1245 QString(
"Renaming %1 to %2 will create a duplicate of %3")
1246 .arg(oldPath, im->m_filePath, existPath))
1250 QString newPath = oldFi.dir().absoluteFilePath(newName);
1251 if (!QFile::rename(oldPath, newPath))
1253 QString(
"Rename of %1 -> %2 failed").arg(oldPath, newPath))
1255 if (im->IsDirectory())
1258 HandleScanRequest(
"START");
1263 DBFS::UpdateDbImage(*im);
1266 QStringList mesg(
"");
1267 mesg << QString::number(im->m_id);
1270 m_thumbGen->MoveThumbnail(im);
1273 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1275 RESULT_OK(QString(
"Renamed %1 -> %2").arg(oldPath, newPath))
1286 template <
class DBFS>
1293 DBFS::GetDescendants(ids, files, dirs);
1301 if (files.isEmpty() && dirs.isEmpty())
1302 RESULT_ERR(
"Delete failed", QString(
"Delete of %1 failed").arg(ids))
1308 QStringList mesg(m_thumbGen->DeleteThumbs(files));
1311 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1313 return QStringList(
"OK");
1329 template <
class DBFS>
1336 const QString separator = defs.takeFirst();
1341 QHash<QString, int> idMap;
1346 for (
const auto& def : std::as_const(defs))
1348 QStringList aDef = def.split(separator);
1351 if (aDef.size() != 6)
1354 LOG(VB_GENERAL, LOG_ERR,
1355 LOC + QString(
"Bad definition: (%1)").arg(def));
1359 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Creating %1").arg(aDef.join(
",")));
1361 im.
m_type = aDef[1].toInt();
1368 int newId = DBFS::InsertDbImage(im,
true);
1371 idMap.insert(aDef[0], newId);
1373 HandleScanRequest(
"START");
1388 template <
class DBFS>
1390 const QString &srcPath,
1391 QString destPath)
const
1394 if (destPath.contains(
".."))
1395 RESULT_ERR(
"Invalid path", QString(
"Invalid path %1").arg(destPath))
1401 bool ok = DBFS::GetDescendants(ids, files, dirs);
1402 images << dirs << files;
1404 if (!ok || images.isEmpty())
1405 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1407 if (!destPath.isEmpty() && !destPath.endsWith(QChar(
'/')))
1408 destPath.append(
"/");
1411 for (
const auto& im : std::as_const(images))
1413 QString old = im->m_filePath;
1415 if (srcPath.isEmpty())
1418 im->m_filePath.prepend(destPath);
1420 else if (im->m_filePath.startsWith(srcPath))
1423 im->m_filePath.replace(srcPath, destPath);
1428 LOG(VB_GENERAL, LOG_ERR,
1429 LOC + QString(
"Bad image: (%1 -> %2)").arg(srcPath, destPath));
1433 LOG(VB_FILE, LOG_DEBUG,
1434 LOC + QString(
"Db Renaming %1 -> %2").arg(old, im->m_filePath));
1436 DBFS::UpdateDbImage(*im);
1440 m_thumbGen->MoveThumbnail(im);
1442 HandleScanRequest(
"START");
1444 RESULT_OK(QString(
"Moved %1 from %2 -> %3").arg(ids, srcPath, destPath))
1455 template <
class DBFS>
1458 if (!DBFS::SetHidden(hide, ids))
1459 RESULT_ERR(
"Hide failed", QString(
"Db hide failed for %1").arg(ids))
1462 QStringList mesg = QStringList(
"") << ids;
1463 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1465 RESULT_OK(QString(
"Images %1 now %2hidden").arg(ids, hide ?
"" :
"un"))
1477 template <
class DBFS>
1479 const QString &ids)
const
1482 RESULT_ERR(
"Transform failed", QString(
"Bad transform %1").arg(transform))
1486 if (DBFS::GetImages(ids, files, dirs) < 1 || files.isEmpty())
1487 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1490 for (
const auto& im : std::as_const(files))
1492 int old = im->m_orientation;
1496 if (DBFS::SetOrientation(im->m_id, im->m_orientation))
1498 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Transformed %1 from %2 to %3")
1499 .arg(im->m_filePath).arg(old).arg(im->m_orientation));
1504 QStringList mesg(
"");
1507 mesg << m_thumbGen->DeleteThumbs(files);
1510 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1512 return QStringList(
"OK");
1524 template <
class DBFS>
1527 const QStringList &relPaths)
const
1532 if (DBFS::GetImages(destId, files, dirs) != 1 || dirs.isEmpty())
1534 QString(
"Image %1 not in Db").arg(destId))
1537 QString destPath = DBFS::GetAbsFilePath(dirs[0]);
1538 if (destPath.isEmpty())
1540 QString(
"Dest dir %1 not found").arg(dirs[0]->m_filePath))
1542 QDir destDir(destPath);
1543 bool succeeded =
false;
1544 for (
const auto& relPath : std::as_const(relPaths))
1547 if (relPath.isEmpty() || relPath.contains(
"..") || relPath.startsWith(QChar(
'/')))
1550 QString newPath = DBFS::ConstructPath(destDir.absolutePath(), relPath);
1551 if (!destDir.mkpath(relPath))
1553 LOG(VB_GENERAL, LOG_ERR,
1554 LOC + QString(
"Failed to create dir %1").arg(newPath));
1557 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Dir %1 created").arg(newPath));
1563 RESULT_ERR(
"Invalid Name", QString(
"Invalid name %1")
1564 .arg(relPaths.join(
",")))
1568 HandleScanRequest(
"START");
1570 return QStringList(
"OK");
1580 template <
class DBFS>
1583 if (!DBFS::SetCover(dir, cover))
1585 QString(
"Failed to set %1 to cover %2").arg(dir).arg(cover))
1588 QStringList mesg = QStringList(
"") << QString::number(dir);
1589 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1591 RESULT_OK(QString(
"Cover of %1 is now %2").arg(dir).arg(cover));
1603 template <
class DBFS>
1610 HandleScanRequest(
"START");
1612 RESULT_OK(QString(
"Using exclusions '%1'").arg(exclusions))
1623 template <
class DBFS>
1628 RESULT_ERR(
"Missing Scanner",
"Missing Scanner");
1630 if (command ==
"START")
1633 if (m_scanner->IsScanning())
1636 m_scanner->ChangeState(
true);
1639 else if (command ==
"STOP")
1642 if (!m_scanner->IsScanning())
1643 RESULT_ERR(
"Scanner not running",
"Scanner not running");
1645 m_scanner->ChangeState(
false);
1648 else if (command ==
"QUERY")
1650 return QStringList(
"OK") << m_scanner->GetProgress();
1652 else if (command.startsWith(QString(
"DEVICE")))
1654 m_scanner->EnqueueClear(devId, command);
1655 RESULT_OK(QString(
"Clearing device %1 %2").arg(command).arg(devId))
1657 RESULT_ERR(
"Unknown command", QString(
"Unknown command %1").arg(command));
1668 template <
class DBFS>
1670 (
const QStringList &message)
const
1672 if (message.size() != 2)
1674 QString(
"Bad request: %1").arg(message.join(
"|")))
1676 int priority = message.at(0).toInt()
1682 DBFS::GetImages(message.at(1), files, dirs);
1684 for (
const auto& im : std::as_const(files))
1686 m_thumbGen->CreateThumbnail(im, priority,
true);
1688 return QStringList(
"OK");
1701 template <
class DBFS>
1704 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1705 QMutableVectorIterator<ImagePtr> it(images);
1707 QMutableListIterator<ImagePtr> it(images);
1710 while (it.hasPrevious())
1715 QString absFilename = DBFS::GetAbsFilePath(im);
1717 bool success = !absFilename.isEmpty()
1718 && (im->IsFile() ? QFile::remove(absFilename)
1719 : QDir::root().rmdir(absFilename));
1721 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Deleted %1").arg(absFilename));
1724 LOG(VB_GENERAL, LOG_ERR,
LOC +
1725 QString(
"Can't delete %1").arg(absFilename));
1759 "CASE WHEN type <= %1 THEN %4, "
1760 "CASE WHEN type > %1 THEN %5 ")
1812 count += ImageHandler::GetDirectory(
id, parent, files, dirs,
m_refineClause);
1840 if (!lists.second.isEmpty())
1843 return ImageHandler::GetImages(lists.first, files, dirs,
m_refineClause);
1862 count += ImageHandler::GetChildren(QString::number(
id), files, dirs,
1880 if (!lists.second.isEmpty())
1883 ImageHandler::GetDescendants(lists.first, files, dirs);
1911 int &videos,
int &sizeKb)
const
1918 ImageHandler::GetDescendantCount(
id,
true, dirs, pics, videos, sizeKb);
1924 dirs, pics, videos, sizeKb);
1929 ImageHandler::GetDescendantCount(
id,
false, dirs, pics, videos, sizeKb);
1985 if (!lists.second.isEmpty())
1987 LOG(VB_FILE, LOG_DEBUG,
LOC +
1988 QString(
"Sending CREATE_THUMBNAILS %1 (forFolder %2)")
1989 .arg(lists.second).arg(forFolder));
1991 QStringList message;
1992 message << QString::number(static_cast<int>(forFolder)) << lists.second;
1996 if (!lists.first.isEmpty())
1998 LOG(VB_FILE, LOG_DEBUG,
LOC +
1999 QString(
"Creating local thumbnails %1 (forFolder %2)")
2000 .arg(lists.first).arg(forFolder));
2002 QStringList message;
2003 message << QString::number(static_cast<int>(forFolder)) << lists.first;
2017 QStringList command;
2018 command << (start ?
"START" :
"STOP");
2022 command.push_front(
"IMAGE_SCAN");
2024 return ok ?
"" : command[1];
2029 return "Couldn't create database";
2032 return err[0] ==
"OK" ?
"" : err[1];
2043 QStringList strList;
2044 strList <<
"IMAGE_SCAN" <<
"QUERY";
2048 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Scan query failed : %1")
2049 .arg(strList.join(
",")));
2065 QString result =
"";
2067 if (!lists.second.isEmpty())
2069 QStringList message;
2070 message <<
"IMAGE_HIDE" << QString::number(
static_cast<int>(hidden)) << lists.second;
2073 result = message[1];
2076 if (!lists.first.isEmpty())
2078 QStringList err =
HandleHide(hidden, lists.first);
2097 QString result =
"";
2099 if (!lists.second.isEmpty())
2101 QStringList message;
2102 message <<
"IMAGE_TRANSFORM" << QString::number(transform) << lists.second;
2105 result = message[1];
2108 if (!lists.first.isEmpty())
2128 QStringList message;
2129 message <<
"IMAGE_COVER" << QString::number(parent) << QString::number(cover);
2132 return ok ?
"" : message[1];
2136 return err[0] ==
"OK" ?
"" : err[1];
2156 QStringList message(
"IMAGE_SCAN");
2157 message <<
"DEVICE CLEAR ALL";
2170 QStringList message(
"IMAGE_IGNORE");
2171 message << excludes;
2173 return ok ?
"" : message[1];
2186 QString destId = QString::number(parent);
2190 QStringList message(
"IMAGE_CREATE_DIRS");
2191 message << destId << QString::number(static_cast<int>(rescan)) << names;
2193 return ok ?
"" : message[1];
2195 QStringList err =
HandleDirs(destId, rescan, names);
2196 return (err[0] ==
"OK") ?
"" : err[1];
2210 QStringList message(
"IMAGE_RENAME");
2211 message << QString::number(im->m_id) << name;
2213 return ok ?
"" : message[1];
2215 QStringList err =
HandleRename(QString::number(im->m_id), name);
2216 return (err[0] ==
"OK") ?
"" : err[1];
2228 if (images.isEmpty())
2232 const QString seperator(
"...");
2233 QStringList imageDefs(seperator);
2235 for (
const auto& im : std::as_const(images))
2241 aDef << QString::number(im->m_id)
2242 << QString::number(im->m_type)
2244 << QString::number(
static_cast<int>(im->m_isHidden))
2245 << QString::number(im->m_orientation)
2246 << QString::number(im->m_userThumbnail);
2248 imageDefs << aDef.join(seperator);
2255 return (err[0] ==
"OK") ?
"" : err[1];
2257 imageDefs.prepend(
"IMAGE_COPY");
2259 return ok ?
"" : imageDefs[1];
2271 const QString &srcPath)
2274 for (
const auto& im : std::as_const(images))
2275 idents << QString::number(im->m_id);
2278 if (destDir->IsLocal())
2280 QStringList err =
HandleDbMove(idents.join(
","), srcPath,
2281 destDir->m_filePath);
2282 return (err[0] ==
"OK") ?
"" : err[1];
2285 QStringList message(
"IMAGE_MOVE");
2286 message << idents.join(
",") << srcPath << destDir->m_filePath;
2288 return ok ?
"" : message[1];
2301 QString result =
"";
2302 if (!lists.second.isEmpty())
2304 QStringList message(
"IMAGE_DELETE");
2305 message << lists.second;
2309 result = message[1];
2311 if (!lists.first.isEmpty())
2332 std::chrono::seconds secs = 0s;
2335 if (im->m_date > 0s)
2342 secs = im->m_modTime;
2360 std::chrono::seconds secs(im->m_date > 0s ? im->m_date : im->m_modTime);
2373 return tr(
"Gallery");
2375 return tr(
"Photographs");
2405 return dev + path.replace(
"/",
" > ");
2411 QString reason {
"DEVICE REMOVE" };
2413 reason =
"DEVICE CLOSE ALL";
2415 reason =
"DEVICE EJECT";
2431 QList<MythMediaDevice*> devices
2434 for (
auto *dev : std::as_const(devices))
2437 OpenDevice(dev->getDeviceModel(), dev->getMountPath(), dev);
2446 for (
int devId : std::as_const(absentees))
2452 LOG(VB_GENERAL, LOG_ERR,
LOC + err);
2466 if (!event || !monitor)
2476 LOG(VB_FILE, LOG_DEBUG,
LOC +
2477 QString(
"Media event for %1 (%2) at %3, type %4, status %5 (was %6)")
2483 LOG(VB_FILE, LOG_DEBUG,
LOC +
2484 QString(
"Ignoring event - wrong type %1").arg(
type));
2512 auto *
tmp =
new QTemporaryDir(QDir::tempPath() %
"/" %
IMPORTDIR %
"-XXXXXX");
2513 if (!
tmp->isValid())
2519 QString time(QDateTime::currentDateTime().
toString(
"mm:ss"));
ImageAdapterBase()
Constructor.
static ImageManagerBe * s_instance
BE Gallery instance.
QString m_dateFormat
UI format for thumbnail date captions.
ImageManagerFe(int order, int dirOrder, bool showAll, int showType, QString dateFormat)
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
QSharedPointer< ImageItemK > ImagePtrK
QSqlQuery wrapper that fetches a DB connection from the connection pool.
int m_showType
Type of images to display - pic only/video only/both.
QStringList HandleRename(const QString &id, const QString &newBase) const
Change name of an image/dir.
bool SetHidden(bool hide, const QString &ids) const
Sets hidden status of an image/dir in database.
QString m_thumbs
Dir sub-path of device thumbnails.
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
int GetImages(const ImageIdList &ids, ImageList &files, ImageList &dirs) const
Returns images (local or remote but not a combination)
StringMap GetDeviceDirs() const
Get all known devices.
@ kSortBySizeAsc
File size Smallest -> Largest.
static QString BaseNameOf(const QString &path)
Extracts file name (incl extension) from a filepath.
void CreateThumbnails(const ImageIdList &ids, bool forFolder)
Create thumbnails or verify that they already exist.
@ kSortByModTimeAsc
File modified time Earliest -> Latest.
void GetImageTree(int id, ImageList &files) const
Return all files (local or remote) in the sub-trees of a dir.
int m_type
Type of node: dir, video etc.
@ kUnknown
Unprocessable file type.
int m_id
Uniquely identifies an image (file/dir).
bool UpdateDbImage(ImageItemK &im) const
Updates or creates database image or dir.
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
QString m_comment
User comment, from Exif metadata.
int GetDirectory(int id, ImagePtr &parent, ImageList &files, ImageList &dirs) const
Return images (local and/or remote) for a dir and its direct children.
QString CreateImages(int destId, const ImageListK &images)
Copies database images (but not the files themselves).
@ kSortByDateAsc
Exif date Earliest -> Latest.
QString ScanImagesAction(bool start, bool local=false)
Handle scanner start/stop commands.
QStringList HandleGetMetadata(const QString &id) const
Read meta data for an image.
void DropTable()
Remove local image table.
static ImageManagerFe * s_instance
FE Gallery instance.
@ kPicRequestPriority
Client request to display an image thumbnail.
QString DeviceName(int devId) const
Get model name of the device.
static Device kNullDevice
QString MakeDir(int parent, const QStringList &names, bool rescan=true)
Create directories.
bool m_showHidden
Whether hidden images are displayed.
bool m_isHidden
If true, image won't be shown.
static void RemoveDirContents(const QString &path)
Clears all files and sub-dirs within a directory.
QString FindFile(const QString &filename)
QVariant lastInsertId()
Return the id of the last inserted row.
QString m_mount
Mountpoint.
This class is used as a container for messages.
void DeviceEvent(MythMediaEvent *event)
Manage events for local devices.
static QString IgnoreDirs(const QString &excludes)
Set directories to ignore during scans of the storage group.
QStringList m_videoFileExt
List of file extensions recognised as videos.
MythMediaDevice * m_media
Set for MediaMonitor devices only.
#define RESULT_ERR(ERR, MESG)
Manages a collection of images.
void Close(bool eject=false)
Releases device.
QVariant value(int i) const
bool RemoveFromDB(Bookmark *site)
void RemoveThumbs(void) const
Delete thumbnails associated with device.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
static void Notify(const QString &mesg, const QStringList &extra)
Send message to all clients about remote ids.
QString HideFiles(bool hidden, const ImageIdList &ids)
Hide/unhide images.
StorageGroup m_sg
Images storage group.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
const association_list & getList() const
static constexpr const char * DB_TABLE
int GetImages(const QString &ids, ImageList &files, ImageList &dirs, const QString &refine="") const
Read database images/dirs by id.
int InsertDbImage(ImageItemK &im, bool checkForDuplicate=false) const
Adds new image to database, optionally checking for existing filepath.
A device containing images (ie. USB stick, CD, storage group etc)
ImageItem * CreateImage(const MSqlQuery &query) const
Create image from Db query data.
QStringList HandleCover(int dir, int cover) const
Updates/resets cover thumbnail for an image dir.
QString ThumbDir(int fs) const
static QString TypeSelector(int type)
Generate SQL type filter clause.
@ kSortByExtAsc
Extension A-Z.
static constexpr const char * IMPORTDIR
@ kDirectory
A device sub directory.
int m_device
Id of media device. Always 0 (SG) for remotes, 1+ for local devices.
void CloseDevices(int devId=DEVICE_INVALID, bool eject=false)
static StringPair PartitionIds(const ImageIdList &ids)
Separates list of ids into a list of local ids and a list of remote ids.
ImageDbSg()
SG database constructor.
QString ChangeOrientation(ImageFileTransform transform, const ImageIdList &ids)
Apply an orientation transform to images.
@ kDevice
Storage Group and local mounted media.
QStringList HandleTransform(int transform, const QString &ids) const
Change orientation of pictures by applying a transformation.
static ImageManagerFe & getInstance()
Get Frontend Gallery.
@ kPicAndVideo
Show Pictures & Videos.
ImageItem * CreateItem(const QFileInfo &fi, int parentId, int devId, const QString &base) const
Construct a local image from a file.
QString DeviceCaption(ImageItemK &im) const
Return translated device name.
int Transform(int transform)
Adjust orientation to apply a transform to an image.
QStringList HandleDbCreate(QStringList defs) const
Creates images for files created by a copy operation.
QString m_refineClause
SQL clause for image filtering/ordering.
bool GetImageTree(int id, ImageList &files, const QString &refine) const
Returns all files in the sub-tree of a dir.
QHash< QString, ImagePtr > ImageHash
MBASE_PUBLIC QDateTime fromSecsSinceEpoch(int64_t seconds)
This function takes the number of seconds since the start of the epoch and returns a QDateTime with t...
@ kSortByNameAsc
Name A-Z.
QStringList HandleDbMove(const QString &ids, const QString &srcPath, QString destPath) const
Updates images that have been renamed.
static void ClearStorageGroup()
Clear database & thumbnails of Storage Group images.
void SendEvent(const MythEvent &event)
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
static constexpr const char * IMAGE_STORAGE_GROUP
std::vector< file_association > association_list
QMap< int, QString > StringMap
static void DBError(const QString &where, const MSqlQuery &query)
static QString FormatSize(int sizeKib)
bool SetOrientation(int id, int orientation) const
Sets image orientation in Db.
const std::array< const std::string, 8 > formats
void GetDescendants(const ImageIdList &ids, ImageList &files, ImageList &dirs) const
Return all (local or remote) images that are direct children of a dir.
static QStringList SupportedImages()
Return recognised pictures.
static QString OrderSelector(int order)
Generate SQL ordering clause.
std::chrono::seconds m_modTime
Filesystem modified datestamp.
static QString PathOf(const QString &path)
Extracts path from a filepath.
QList< ImagePtrK > ImageListK
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
QString Description() const
Generate text description of orientation.
static QString LongDateOf(const ImagePtrK &im)
Return a timestamp/datestamp for an image or dir.
static constexpr const char * kDBColumns
QStringList GetDirList(void) const
QString SetCover(int parent, int cover)
Set image to use as a cover thumbnail(s)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool IsLocalId(int id)
Determine image type (local/remote) from its id. Root/Gallery is remote.
int m_orientation
Image orientation.
QString m_baseName
File/Dir name with extension (no path)
@ kSortByNameDesc
Name Z-A.
QVector< ImagePtr > ImageList
int GetNumSetting(const QString &key, int defaultval=0)
QString DeviceMount(int devId) const
Get path at which the device is mounted.
static bool IsLocalParent(int id)
Parents of locals are locals or root.
@ kDirRequestPriority
Client request to display a directory thumbnail.
void RemoveFiles(ImageList &images) const
Deletes images and dirs from the filesystem.
static QString GetAbsThumbPath(const QString &devPath, const QString &path)
Get absolute filepath for thumbnail of an image.
void GetDescendantCount(int id, int &dirs, int &pics, int &videos, int &sizeKb) const
Return counts of dirs, pics and videos in the subtree of a dir. Also dir size.
bool GetBoolSetting(const QString &key, bool defaultval=false)
bool m_present
True when gallery UI is running & device is useable. Always true for imports.
static FileAssociations & getFileAssociation()
QTemporaryDir * m_dir
Dir path of images: import devices only.
QStringList HandleScanRequest(const QString &command, int devId=DEVICE_INVALID) const
Process scan requests.
QStringList HandleDirs(const QString &destId, bool rescan, const QStringList &relPaths) const
Creates new image directories.
QString MoveDbImages(const ImagePtrK &destDir, ImageListK &images, const QString &srcPath)
Moves database images (but not the files themselves).
QDir m_dirFilter
A pre-configured dir for reading image/video files.
QSharedPointer< ImageItem > ImagePtr
@ kAddYear
Add year to string if not included.
The image manager for use by Frontends.
QString FindNextDirMostFree(void)
int m_fileOrder
Display ordering of pics/videos.
ImageDbSg * m_remote
Remote database access.
Represents a picture, video or directory.
ImageDbLocal()
Local database constructor.
~Device()
Delete device, its thumbnails and any imported images.
static constexpr const char * TEMP_SUBDIR
int LocateMount(const QString &mount) const
Find the id of a device.
@ kSortByDateDesc
Exif date Latest -> Earliest.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Device(QString name, QString mount, MythMediaDevice *media=nullptr, QTemporaryDir *import=nullptr)
QStringList HandleIgnore(const QString &exclusions) const
Updates exclusion list for images.
int OpenDevice(const QString &name, const QString &mount, MythMediaDevice *media=nullptr, QTemporaryDir *dir=nullptr)
Define a new device and assign it a unique id. If the device is already known, its existing id is ret...
bool SetCover(int dir, int id) const
Set the thumbnail(s) to be used for a dir.
static QString ThumbPath(const ImageItem &im)
Thumbnails of videos are a JPEG snapshot with jpg suffix appended.
int ReadImages(ImageList &dirs, ImageList &files, const QString &selector) const
Read selected database images/dirs.
QString CrumbName(ImageItemK &im, bool getPath=false) const
Return a displayable name (with optional path) for an image.
int m_parentId
Id of parent dir.
void ClearDb(int devId, const QString &action)
Clear Db for device & remove device.
static constexpr const char * THUMBNAIL_SUBDIR
static constexpr int PHOTO_DB_ID
QPair< QString, QString > StringPair
int GetChildren(int id, ImageList &files, ImageList &dirs) const
Return (local or remote) images that are direct children of a dir.
QString GetAbsFilePath(const ImagePtrK &im) const
Get absolute filepath for a remote image.
int m_dirOrder
Display ordering of dirs.
static ImageManagerBe * getInstance()
Get Backend Gallery.
int GetDirectory(int id, ImagePtr &parent, ImageList &files, ImageList &dirs, const QString &refine) const
Read a dir and its immediate children from Db.
QStringList CloseDevices(int devId, const QString &action)
Remove a device (or all devices)
static void Notify(const QString &mesg, const QStringList &extra)
Send local message to UI about local ids.
QStringList RemoveFromDB(const ImageList &imList) const
Remove images/dirs from database.
bool ReadAllImages(ImageHash &files, ImageHash &dirs) const
Read all database images and dirs as map. No filters or ordering applied.
bool CreateTable()
Create local database table, if it doesn't exist.
@ kDateFull
Default local time.
QList< int > GetAbsentees()
Get list of mountpoints for non-import devices.
@ kSortByExtDesc
Extension Z-A.
void SetRefinementClause()
Sets filter/ordering SQL clause used when reading database according to current filter/sort settings.
@ kSortBySizeDesc
File size Largest -> Smallest.
QString GetHostName(void)
int m_userThumbnail
Id of thumbnail to use as cover (dirs only)
QStringList HandleDelete(const QString &ids) const
Deletes images/dirs.
StringMap GetScanDirs() const
Returns SG dirs.
std::chrono::seconds m_date
Image creation date, from Exif metadata.
QString DeleteFiles(const ImageIdList &ids)
Delete images.
QString m_table
Db table name.
void setPresent(MythMediaDevice *media)
int GetChildren(const QString &ids, ImageList &files, ImageList &dirs, const QString &refine="") const
Read immediate children of a dir.
QString m_extension
Image file extension.
@ kTime
Default local time.
static constexpr int GALLERY_DB_ID
ImageItem * CreateItem(const QFileInfo &fi, int parentId, int devId, const QString &base) const
Construct a remote image from a file.
The image manager to be used by the Backend.
bool GetDescendants(const QString &ids, ImageList &files, ImageList &dirs) const
Return images and all of their descendants.
void RequestMetaData(int id)
Requests all exif/ffmpeg tags for an image, which returns by event.
static MThreadPool * globalInstance(void)
DeviceMap m_devices
Device store.
@ kVideoOnly
Hide pictures.
void GetDescendantCount(int id, bool all, int &dirs, int &pics, int &videos, int &sizeKb) const
Return counts of dirs, pics, videos and size in the subtree of a dir.
ImageNodeType GetImageType(const QString &ext) const
Determine file type from its extension.
static QStringList ScanQuery()
Returns storage group scanner status.
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
QString RenameFile(const ImagePtrK &im, const QString &name)
Rename an image.
QString m_name
Device model/volume/id.
QString ShortDateOf(const ImagePtrK &im) const
Return a short datestamp for thumbnail captions.
QStringList HandleCreateThumbnails(const QStringList &message) const
Creates thumbnails on-demand.
QStringList m_imageFileExt
List of file extensions recognised as pictures.
QStringList HandleHide(bool hide, const QString &ids) const
Hides/unhides images/dirs.
static constexpr const char * STORAGE_GROUP_MOUNT
Encapsulates Exif orientation processing.
void start(QRunnable *runnable, const QString &debugName, int priority=0)
int m_size
Filesize (files only)
static constexpr int DEVICE_INVALID
QString GetSetting(const QString &key, const QString &defaultval="")
static QStringList SupportedVideos()
Return recognised video extensions.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
@ kSortByModTimeDesc
File modified time Latest -> Earliest.
QString m_filePath
Absolute for local images. Usually SG-relative for remotes.
bool DetectLocalDevices()
Detect and scan local devices.