Go to the documentation of this file.
3 #include <QImageReader>
15 #define LOC QString("ImageManager: ")
16 #define DBLOC QString("ImageDb(%1): ").arg(m_table)
21 static constexpr
const char*
DB_TABLE {
"gallery_files" };
24 #define RESULT_ERR(ERR, MESG) \
25 { LOG(VB_GENERAL, LOG_ERR, LOC + (MESG)); \
26 return QStringList("ERROR") << (ERR); }
28 #define RESULT_OK(MESG) \
29 { LOG(VB_FILE, LOG_DEBUG, LOC + (MESG)); \
30 return QStringList("OK"); }
33 static constexpr
const char*
IMPORTDIR {
"Import" };
75 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Ejecting '%1' at '%2'")
81 LOG(VB_MEDIA, LOG_DEBUG,
LOC + QString(
"Unlocked '%1'").arg(
m_name));
96 QDir(path).removeRecursively();
104 QString dirFmt = QString(
"%1/") %
TEMP_SUBDIR %
"/%2";
106 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Removing thumbnails in %1").arg(dir));
108 QDir::root().rmpath(dir);
170 QString state(
"Known");
186 LOG(VB_GENERAL, LOG_INFO,
LOC +
187 QString(
"%1 device %2 mounted at '%3' [Id %4]")
188 .arg(state, name, mount).arg(
id));
204 if (
action ==
"DEVICE CLOSE ALL")
207 for (
auto *dev : std::as_const(
m_devices))
211 else if (
action ==
"DEVICE CLEAR ALL")
214 for (
const auto *dev : std::as_const(
m_devices)) {
217 clear << dev->m_mount;
228 if (
action ==
"DEVICE EJECT")
245 DeviceMap::const_iterator it =
m_devices.constBegin();
248 if (it.value()->m_mount == mount)
260 for (
auto it =
m_devices.constKeyValueBegin();
261 it !=
m_devices.constKeyValueEnd(); ++it)
264 paths.insert(it->first, it->second->m_mount);
274 for (
auto it =
m_devices.constKeyValueBegin();
275 it !=
m_devices.constKeyValueEnd(); it++)
289 m_imageFileExt(SupportedImages()),
290 m_videoFileExt(SupportedVideos())
295 for (
const auto& ext : std::as_const(allExt))
300 m_dirFilter.setFilter(QDir::AllDirs | QDir::Files | QDir::Readable |
301 QDir::NoDotAndDotDot | QDir::NoSymLinks);
305 m_dirFilter.setSorting(QDir::DirsLast | QDir::Time | QDir::Reversed);
316 QList<QByteArray> supported = QImageReader::supportedImageFormats();
317 for (
const auto& ext : std::as_const(supported))
333 for (
const auto & fa : faList)
335 if (!fa.use_default && fa.playcommand ==
"Internal")
336 formats << QString(fa.extension);
351 int devId,
const QString & )
const
355 im->m_parentId = parentId;
356 im->m_device = devId;
357 im->m_filePath = fi.absoluteFilePath();
362 auto secs = im->m_filePath.contains(
IMPORTDIR)
363 ? fi.lastModified().toSecsSinceEpoch()
364 : QDateTime::currentSecsSinceEpoch();
365 im->m_date = std::chrono::seconds(secs);
366 im->m_modTime = im->m_date;
371 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
379 im->m_extension = fi.suffix().toLower();
389 im->m_size = fi.size();
401 const QStringList &extra)
434 int ,
const QString &base)
const
439 im->m_parentId = parentId;
446 im->m_date = std::chrono::seconds(QDateTime::currentSecsSinceEpoch());
447 im->m_modTime = im->m_date;
452 im->m_filePath = fi.absoluteFilePath().mid(base.size() + 1);
453 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
461 im->m_extension = fi.suffix().toLower();
471 im->m_size = fi.size();
483 const QStringList &extra)
498 for (
const auto& path : std::as_const(
paths))
499 map.insert(i++, path);
514 return im->m_filePath.startsWith(
"/") ? im->m_filePath
521 "file_id, filename, name, dir_id, type, modtime, size, "
522 "extension, date, hidden, orientation, angle, path, zoom"
538 im->m_filePath = query.
value(1).toString();
539 im->m_baseName = query.
value(2).toString();
540 im->m_parentId = FS::ImageId(query.
value(3).toInt());
541 im->m_type = query.
value(4).toInt();
542 im->m_modTime = std::chrono::seconds(query.
value(5).toInt());
543 im->m_size = query.
value(6).toInt();
544 im->m_extension = query.
value(7).toString();
545 im->m_date = std::chrono::seconds(query.
value(8).toUInt());
546 im->m_isHidden = query.
value(9).toBool();
547 im->m_orientation = query.
value(10).toInt();
548 im->m_userThumbnail = FS::ImageId(query.
value(11).toInt());
549 im->m_comment = query.
value(12).toString();
550 im->m_device = query.
value(13).toInt();
551 im->m_url = FS::MakeFileUrl(im->m_filePath);
556 QString thumbPath(FS::ThumbPath(*im));
557 QString devPath(FS::ThumbDir(im->m_device));
558 QString url(FS::MakeThumbUrl(devPath, thumbPath));
560 im->m_thumbPath = FS::GetAbsThumbPath(devPath, thumbPath);
561 im->m_thumbNails.append(qMakePair(im->m_id, url));
577 const QString &refine)
const
582 QString select = QString(
"file_id IN (%1) %2").arg(FS::DbIds(ids), refine);
583 return ReadImages(dirs, files, select);
597 const QString &refine)
const
599 QString select = QString(
"dir_id IN (%1) %2").arg(FS::DbIds(ids), refine);
600 return ReadImages(dirs, files, select);
616 const QString &refine)
const
619 query.
prepare(QString(
"SELECT %1 FROM %2 "
620 "WHERE (dir_id = :ID1 OR file_id = :ID2) "
624 int dbId = FS::DbId(
id);
639 else if (im->m_id ==
id)
662 if (ReadImages(dirs, files, QString(
"file_id IN (%1)").arg(FS::DbIds(ids))) < 0)
668 ", LENGTH(filename) - LENGTH(REPLACE(filename, '/', ''))"
670 "FROM %2 WHERE filename LIKE :PREFIX "
673 for (
const auto& im1 : std::as_const(dirs))
676 query.
bindValue(
":PREFIX", im1->m_filePath +
"/%");
687 if (im2->IsDirectory())
709 if (GetChildren(QString::number(
id), files, dirs, refine) < 0)
712 for (
const auto& im : std::as_const(dirs))
713 if (!GetImageTree(im->m_id, files, refine))
739 if (im->IsDirectory())
740 dirs.insert(im->m_filePath, im);
742 files.insert(im->m_filePath, im);
758 if (
action ==
"DEVICE CLOSE ALL")
764 if (
action ==
"DEVICE CLEAR ALL")
767 query.
prepare(QString(
"TRUNCATE TABLE %1;").arg(m_table));
775 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE zoom = :FS;").arg(m_table));
796 if (checkForDuplicate)
798 query.
prepare(QString(
"SELECT file_id FROM %1 WHERE filename = :NAME;")
809 if (query.
size() > 0)
811 LOG(VB_FILE, LOG_DEBUG, QString(
"Image: %1 already exists in Db")
813 return query.
value(0).toInt();
817 query.
prepare(QString(
"INSERT INTO %1 (%2) VALUES (0, "
818 ":FILEPATH, :NAME, :PARENT, :TYPE, :MODTIME, "
819 ":SIZE, :EXTENSION, :DATE, :HIDDEN, :ORIENT, "
820 ":COVER, :COMMENT, :FS);").arg(m_table,
kDBColumns));
855 "filename = :FILEPATH, name = :NAME, "
856 "dir_id = :PARENT, type = :TYPE, "
857 "modtime = :MODTIME, size = :SIZE, "
858 "extension = :EXTENSION, date = :DATE, zoom = :FS, "
859 "hidden = :HIDDEN, orientation = :ORIENT, "
860 "angle = :COVER, path = :COMMENT "
861 "WHERE file_id = :ID;").arg(m_table));
896 if (!imList.isEmpty())
898 for (
const auto& im : std::as_const(imList))
899 ids << QString::number(FS::DbId(im->m_id));
901 QString idents = ids.join(
",");
903 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE file_id IN (%2);")
904 .arg(m_table, idents));
930 query.
prepare(QString(
"UPDATE %1 SET "
932 "WHERE file_id IN (%2);").arg(m_table, FS::DbIds(ids)));
933 query.
bindValue(
":HIDDEN", hide ? 1 : 0);
953 query.
prepare(QString(
"UPDATE %1 SET "
955 "WHERE file_id = :DIR").arg(m_table));
977 query.
prepare(QString(
"UPDATE %1 SET ").arg(m_table) +
978 "orientation = :ORIENTATION "
979 "WHERE file_id = :ID");
980 query.
bindValue(
":ORIENTATION", orientation);
1000 const QString &selector)
const
1003 query.
prepare(QString(
"SELECT %1 FROM %2 WHERE %3")
1011 while (query.
next())
1020 return query.
size();
1035 int &pics,
int &videos,
int &sizeKb)
const
1037 QString whereClause;
1040 whereClause =
"WHERE filename LIKE "
1041 "( SELECT CONCAT(filename, '/%') "
1042 " FROM %2 WHERE file_id = :ID);";
1046 query.
prepare(QString(
"SELECT SUM(type <= :FLDR) AS Fldr, "
1047 " SUM(type = :PIC) AS Pics, "
1048 " SUM(type = :VID) AS Vids, "
1049 " SUM(size / 1024) "
1050 "FROM %2 %1;").arg(whereClause, m_table));
1062 else if (query.
next())
1064 dirs += query.
value(0).toInt();
1065 pics += query.
value(1).toInt();
1066 videos += query.
value(2).toInt();
1067 sizeKb += query.
value(3).toInt();
1122 query.
prepare(QString(
"ALTER TABLE %1 ENGINE = MEMORY;").arg(
m_table));
1126 LOG(VB_FILE, LOG_DEBUG, QString(
"Created Db table %1").arg(
m_table));
1146 :
m_im(std::move(im)),
m_path(std::move(path)) {}
1151 QString orientation;
1167 tags.prepend(QString::number(
m_im->m_id));
1198 template <
class DBFS>
1204 if (DBFS::GetImages(
id, files, dirs) != 1)
1205 RESULT_ERR(
"Image not found", QString(
"Unknown image %1").arg(
id))
1207 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1209 QString absPath = DBFS::GetAbsFilePath(im);
1210 if (absPath.isEmpty())
1212 QString(
"File %1 not found").arg(im->m_filePath))
1218 RESULT_OK(QString(
"Fetching metadata for %1").arg(
id))
1229 template <
class DBFS>
1231 const QString &newBase)
const
1234 if (newBase.isEmpty() || newBase.contains(
"/") || newBase.contains(
"\\"))
1235 RESULT_ERR(
"Invalid name", QString(
"Invalid name %1").arg(newBase))
1240 if (DBFS::GetImages(
id, files, dirs) != 1)
1241 RESULT_ERR(
"Image not found", QString(
"Image %1 not in Db").arg(
id))
1243 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1246 QString oldPath = DBFS::GetAbsFilePath(im);
1247 if (oldPath.isEmpty())
1249 QString(
"File %1 not found").arg(im->m_filePath))
1252 QFileInfo oldFi = QFileInfo(oldPath);
1253 QString newName = im->IsDirectory()
1254 ? newBase : QString(
"%1.%2").arg(newBase, oldFi.suffix());
1256 im->m_filePath = DBFS::ConstructPath(DBFS::PathOf(im->m_filePath), newName);
1261 QString existPath = DBFS::GetAbsFilePath(im);
1262 if (!existPath.isEmpty())
1264 QString(
"Renaming %1 to %2 will create a duplicate of %3")
1265 .arg(oldPath, im->m_filePath, existPath))
1269 QString newPath = oldFi.dir().absoluteFilePath(newName);
1270 if (!QFile::rename(oldPath, newPath))
1272 QString(
"Rename of %1 -> %2 failed").arg(oldPath, newPath))
1274 if (im->IsDirectory())
1277 HandleScanRequest(
"START");
1282 DBFS::UpdateDbImage(*im);
1285 QStringList mesg(
"");
1286 mesg << QString::number(im->m_id);
1289 m_thumbGen->MoveThumbnail(im);
1292 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1294 RESULT_OK(QString(
"Renamed %1 -> %2").arg(oldPath, newPath))
1305 template <
class DBFS>
1312 DBFS::GetDescendants(ids, files, dirs);
1320 if (files.isEmpty() && dirs.isEmpty())
1321 RESULT_ERR(
"Delete failed", QString(
"Delete of %1 failed").arg(ids))
1327 QStringList mesg(m_thumbGen->DeleteThumbs(files));
1330 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1332 return QStringList(
"OK");
1348 template <
class DBFS>
1355 const QString separator = defs.takeFirst();
1360 QHash<QString, int> idMap;
1365 for (
const auto& def : std::as_const(defs))
1367 QStringList aDef = def.split(separator);
1370 if (aDef.size() != 6)
1373 LOG(VB_GENERAL, LOG_ERR,
1374 LOC + QString(
"Bad definition: (%1)").arg(def));
1378 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Creating %1").arg(aDef.join(
",")));
1380 im.
m_type = aDef[1].toInt();
1387 int newId = DBFS::InsertDbImage(im,
true);
1390 idMap.insert(aDef[0], newId);
1392 HandleScanRequest(
"START");
1407 template <
class DBFS>
1409 const QString &srcPath,
1410 QString destPath)
const
1413 if (destPath.contains(
".."))
1414 RESULT_ERR(
"Invalid path", QString(
"Invalid path %1").arg(destPath))
1420 bool ok = DBFS::GetDescendants(ids, files, dirs);
1421 images << dirs << files;
1423 if (!ok || images.isEmpty())
1424 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1426 if (!destPath.isEmpty() && !destPath.endsWith(QChar(
'/')))
1427 destPath.append(
"/");
1430 for (
const auto& im : std::as_const(images))
1432 QString old = im->m_filePath;
1434 if (srcPath.isEmpty())
1437 im->m_filePath.prepend(destPath);
1439 else if (im->m_filePath.startsWith(srcPath))
1442 im->m_filePath.replace(srcPath, destPath);
1447 LOG(VB_GENERAL, LOG_ERR,
1448 LOC + QString(
"Bad image: (%1 -> %2)").arg(srcPath, destPath));
1452 LOG(VB_FILE, LOG_DEBUG,
1453 LOC + QString(
"Db Renaming %1 -> %2").arg(old, im->m_filePath));
1455 DBFS::UpdateDbImage(*im);
1459 m_thumbGen->MoveThumbnail(im);
1461 HandleScanRequest(
"START");
1463 RESULT_OK(QString(
"Moved %1 from %2 -> %3").arg(ids, srcPath, destPath))
1474 template <
class DBFS>
1477 if (!DBFS::SetHidden(hide, ids))
1478 RESULT_ERR(
"Hide failed", QString(
"Db hide failed for %1").arg(ids))
1481 QStringList mesg = QStringList(
"") << ids;
1482 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1484 RESULT_OK(QString(
"Images %1 now %2hidden").arg(ids, hide ?
"" :
"un"))
1496 template <
class DBFS>
1498 const QString &ids)
const
1501 RESULT_ERR(
"Transform failed", QString(
"Bad transform %1").arg(transform))
1505 if (DBFS::GetImages(ids, files, dirs) < 1 || files.isEmpty())
1506 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1509 for (
const auto& im : std::as_const(files))
1511 int old = im->m_orientation;
1515 if (DBFS::SetOrientation(im->m_id, im->m_orientation))
1517 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Transformed %1 from %2 to %3")
1518 .arg(im->m_filePath).arg(old).arg(im->m_orientation));
1523 QStringList mesg(
"");
1526 mesg << m_thumbGen->DeleteThumbs(files);
1529 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1531 return QStringList(
"OK");
1543 template <
class DBFS>
1546 const QStringList &relPaths)
const
1551 if (DBFS::GetImages(destId, files, dirs) != 1 || dirs.isEmpty())
1553 QString(
"Image %1 not in Db").arg(destId))
1556 QString destPath = DBFS::GetAbsFilePath(dirs[0]);
1557 if (destPath.isEmpty())
1559 QString(
"Dest dir %1 not found").arg(dirs[0]->m_filePath))
1561 QDir destDir(destPath);
1562 bool succeeded =
false;
1563 for (
const auto& relPath : std::as_const(relPaths))
1566 if (relPath.isEmpty() || relPath.contains(
"..") || relPath.startsWith(QChar(
'/')))
1569 QString newPath = DBFS::ConstructPath(destDir.absolutePath(), relPath);
1570 if (!destDir.mkpath(relPath))
1572 LOG(VB_GENERAL, LOG_ERR,
1573 LOC + QString(
"Failed to create dir %1").arg(newPath));
1576 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Dir %1 created").arg(newPath));
1582 RESULT_ERR(
"Invalid Name", QString(
"Invalid name %1")
1583 .arg(relPaths.join(
",")))
1587 HandleScanRequest(
"START");
1589 return QStringList(
"OK");
1599 template <
class DBFS>
1602 if (!DBFS::SetCover(dir, cover))
1604 QString(
"Failed to set %1 to cover %2").arg(dir).arg(cover))
1607 QStringList mesg = QStringList(
"") << QString::number(dir);
1608 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1610 RESULT_OK(QString(
"Cover of %1 is now %2").arg(dir).arg(cover));
1622 template <
class DBFS>
1629 HandleScanRequest(
"START");
1631 RESULT_OK(QString(
"Using exclusions '%1'").arg(exclusions))
1642 template <
class DBFS>
1647 RESULT_ERR(
"Missing Scanner",
"Missing Scanner");
1649 if (command ==
"START")
1652 if (m_scanner->IsScanning())
1655 m_scanner->ChangeState(
true);
1658 else if (command ==
"STOP")
1661 if (!m_scanner->IsScanning())
1662 RESULT_ERR(
"Scanner not running",
"Scanner not running");
1664 m_scanner->ChangeState(
false);
1667 else if (command ==
"QUERY")
1669 return QStringList(
"OK") << m_scanner->GetProgress();
1671 else if (command.startsWith(QString(
"DEVICE")))
1673 m_scanner->EnqueueClear(devId, command);
1674 RESULT_OK(QString(
"Clearing device %1 %2").arg(command).arg(devId))
1676 RESULT_ERR(
"Unknown command", QString(
"Unknown command %1").arg(command));
1687 template <
class DBFS>
1689 (
const QStringList &message)
const
1691 if (message.size() != 2)
1693 QString(
"Bad request: %1").arg(message.join(
"|")))
1695 int priority = message.at(0).toInt()
1701 DBFS::GetImages(message.at(1), files, dirs);
1703 for (
const auto& im : std::as_const(files))
1705 m_thumbGen->CreateThumbnail(im, priority,
true);
1707 return QStringList(
"OK");
1720 template <
class DBFS>
1723 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1724 QMutableVectorIterator<ImagePtr> it(images);
1726 QMutableListIterator<ImagePtr> it(images);
1729 while (it.hasPrevious())
1734 QString absFilename = DBFS::GetAbsFilePath(im);
1736 bool success = !absFilename.isEmpty()
1737 && (im->IsFile() ? QFile::remove(absFilename)
1738 : QDir::root().rmdir(absFilename));
1740 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Deleted %1").arg(absFilename));
1743 LOG(VB_GENERAL, LOG_ERR,
LOC +
1744 QString(
"Can't delete %1").arg(absFilename));
1778 "CASE WHEN type <= %1 THEN %4, "
1779 "CASE WHEN type > %1 THEN %5 ")
1831 count += ImageHandler::GetDirectory(
id, parent, files, dirs,
m_refineClause);
1859 if (!lists.second.isEmpty())
1862 return ImageHandler::GetImages(lists.first, files, dirs,
m_refineClause);
1881 count += ImageHandler::GetChildren(QString::number(
id), files, dirs,
1899 if (!lists.second.isEmpty())
1902 ImageHandler::GetDescendants(lists.first, files, dirs);
1930 int &videos,
int &sizeKb)
const
1937 ImageHandler::GetDescendantCount(
id,
true, dirs, pics, videos, sizeKb);
1943 dirs, pics, videos, sizeKb);
1948 ImageHandler::GetDescendantCount(
id,
false, dirs, pics, videos, sizeKb);
2004 if (!lists.second.isEmpty())
2006 LOG(VB_FILE, LOG_DEBUG,
LOC +
2007 QString(
"Sending CREATE_THUMBNAILS %1 (forFolder %2)")
2008 .arg(lists.second).arg(forFolder));
2010 QStringList message;
2011 message << QString::number(static_cast<int>(forFolder)) << lists.second;
2015 if (!lists.first.isEmpty())
2017 LOG(VB_FILE, LOG_DEBUG,
LOC +
2018 QString(
"Creating local thumbnails %1 (forFolder %2)")
2019 .arg(lists.first).arg(forFolder));
2021 QStringList message;
2022 message << QString::number(static_cast<int>(forFolder)) << lists.first;
2036 QStringList command;
2037 command << (start ?
"START" :
"STOP");
2041 command.push_front(
"IMAGE_SCAN");
2043 return ok ?
"" : command[1];
2048 return "Couldn't create database";
2051 return err[0] ==
"OK" ?
"" : err[1];
2062 QStringList strList;
2063 strList <<
"IMAGE_SCAN" <<
"QUERY";
2067 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Scan query failed : %1")
2068 .arg(strList.join(
",")));
2084 QString result =
"";
2086 if (!lists.second.isEmpty())
2088 QStringList message;
2089 message <<
"IMAGE_HIDE" << QString::number(
static_cast<int>(hidden)) << lists.second;
2092 result = message[1];
2095 if (!lists.first.isEmpty())
2097 QStringList err =
HandleHide(hidden, lists.first);
2116 QString result =
"";
2118 if (!lists.second.isEmpty())
2120 QStringList message;
2121 message <<
"IMAGE_TRANSFORM" << QString::number(transform) << lists.second;
2124 result = message[1];
2127 if (!lists.first.isEmpty())
2147 QStringList message;
2148 message <<
"IMAGE_COVER" << QString::number(parent) << QString::number(cover);
2151 return ok ?
"" : message[1];
2155 return err[0] ==
"OK" ?
"" : err[1];
2175 QStringList message(
"IMAGE_SCAN");
2176 message <<
"DEVICE CLEAR ALL";
2189 QStringList message(
"IMAGE_IGNORE");
2190 message << excludes;
2192 return ok ?
"" : message[1];
2205 QString destId = QString::number(parent);
2209 QStringList message(
"IMAGE_CREATE_DIRS");
2210 message << destId << QString::number(static_cast<int>(rescan)) << names;
2212 return ok ?
"" : message[1];
2214 QStringList err =
HandleDirs(destId, rescan, names);
2215 return (err[0] ==
"OK") ?
"" : err[1];
2229 QStringList message(
"IMAGE_RENAME");
2230 message << QString::number(im->m_id) << name;
2232 return ok ?
"" : message[1];
2234 QStringList err =
HandleRename(QString::number(im->m_id), name);
2235 return (err[0] ==
"OK") ?
"" : err[1];
2247 if (images.isEmpty())
2251 const QString seperator(
"...");
2252 QStringList imageDefs(seperator);
2254 for (
const auto& im : std::as_const(images))
2260 aDef << QString::number(im->m_id)
2261 << QString::number(im->m_type)
2263 << QString::number(
static_cast<int>(im->m_isHidden))
2264 << QString::number(im->m_orientation)
2265 << QString::number(im->m_userThumbnail);
2267 imageDefs << aDef.join(seperator);
2274 return (err[0] ==
"OK") ?
"" : err[1];
2276 imageDefs.prepend(
"IMAGE_COPY");
2278 return ok ?
"" : imageDefs[1];
2290 const QString &srcPath)
2293 for (
const auto& im : std::as_const(images))
2294 idents << QString::number(im->m_id);
2297 if (destDir->IsLocal())
2299 QStringList err =
HandleDbMove(idents.join(
","), srcPath,
2300 destDir->m_filePath);
2301 return (err[0] ==
"OK") ?
"" : err[1];
2304 QStringList message(
"IMAGE_MOVE");
2305 message << idents.join(
",") << srcPath << destDir->m_filePath;
2307 return ok ?
"" : message[1];
2320 QString result =
"";
2321 if (!lists.second.isEmpty())
2323 QStringList message(
"IMAGE_DELETE");
2324 message << lists.second;
2328 result = message[1];
2330 if (!lists.first.isEmpty())
2351 std::chrono::seconds secs = 0s;
2354 if (im->m_date > 0s)
2361 secs = im->m_modTime;
2379 std::chrono::seconds secs(im->m_date > 0s ? im->m_date : im->m_modTime);
2392 return tr(
"Gallery");
2394 return tr(
"Photographs");
2424 return dev + path.replace(
"/",
" > ");
2430 QString reason {
"DEVICE REMOVE" };
2432 reason =
"DEVICE CLOSE ALL";
2434 reason =
"DEVICE EJECT";
2450 QList<MythMediaDevice*> devices
2453 for (
auto *dev : std::as_const(devices))
2456 OpenDevice(dev->getDeviceModel(), dev->getMountPath(), dev);
2465 for (
int devId : std::as_const(absentees))
2471 LOG(VB_GENERAL, LOG_ERR,
LOC + err);
2485 if (!event || !monitor)
2495 LOG(VB_FILE, LOG_DEBUG,
LOC +
2496 QString(
"Media event for %1 (%2) at %3, type %4, status %5 (was %6)")
2502 LOG(VB_FILE, LOG_DEBUG,
LOC +
2503 QString(
"Ignoring event - wrong type %1").arg(
type));
2531 auto *
tmp =
new QTemporaryDir(QDir::tempPath() %
"/" %
IMPORTDIR %
"-XXXXXX");
2532 if (!
tmp->isValid())
2538 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.
QString m_hostname
Host of SG.
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.
QString MakeFileUrl(const QString &path) const
Construct URL of a remote image.
QHash< QString, ImagePtr > ImageHash
static constexpr const char * THUMBNAIL_STORAGE_GROUP
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 QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
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.
This class contains the runtime context for MythTV.
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.
QString MakeThumbUrl(const QString &devPath, const QString &path="") const
Construct URL of the thumbnail of a remote image.
@ 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.