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'")
78 LOG(VB_MEDIA, LOG_DEBUG,
LOC + QString(
"Unlocked '%1'").arg(
m_name));
92 QDir(path).removeRecursively();
100 QString dirFmt = QString(
"%1/") %
TEMP_SUBDIR %
"/%2";
102 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Removing thumbnails in %1").arg(dir));
104 QDir::root().rmpath(dir);
166 QString state(
"Known");
182 LOG(VB_GENERAL, LOG_INFO,
LOC +
183 QString(
"%1 device %2 mounted at '%3' [Id %4]")
184 .arg(state, name, mount).arg(
id));
200 if (
action ==
"DEVICE CLOSE ALL")
203 for (
auto *dev : std::as_const(
m_devices))
207 else if (
action ==
"DEVICE CLEAR ALL")
210 for (
const auto *dev : std::as_const(
m_devices)) {
213 clear << dev->m_mount;
224 if (
action ==
"DEVICE EJECT")
241 DeviceMap::const_iterator it =
m_devices.constBegin();
244 if (it.value()->m_mount == mount)
256 for (
auto it =
m_devices.constKeyValueBegin();
257 it !=
m_devices.constKeyValueEnd(); ++it)
260 paths.insert(it->first, it->second->m_mount);
270 for (
auto it =
m_devices.constKeyValueBegin();
271 it !=
m_devices.constKeyValueEnd(); it++)
285 m_imageFileExt(SupportedImages()),
286 m_videoFileExt(SupportedVideos())
291 for (
const auto& ext : std::as_const(allExt))
296 m_dirFilter.setFilter(QDir::AllDirs | QDir::Files | QDir::Readable |
297 QDir::NoDotAndDotDot | QDir::NoSymLinks);
301 m_dirFilter.setSorting(QDir::DirsLast | QDir::Time | QDir::Reversed);
312 QList<QByteArray> supported = QImageReader::supportedImageFormats();
313 for (
const auto& ext : std::as_const(supported))
329 for (
const auto & fa : faList)
331 if (!fa.use_default && fa.playcommand ==
"Internal")
332 formats << QString(fa.extension);
347 int devId,
const QString & )
const
351 im->m_parentId = parentId;
352 im->m_device = devId;
353 im->m_filePath = fi.absoluteFilePath();
358 auto secs = im->m_filePath.contains(
IMPORTDIR)
359 ? fi.lastModified().toSecsSinceEpoch()
360 : QDateTime::currentSecsSinceEpoch();
361 im->m_date = std::chrono::seconds(secs);
362 im->m_modTime = im->m_date;
367 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
375 im->m_extension = fi.suffix().toLower();
385 im->m_size = fi.size();
397 const QStringList &extra)
413 int ,
const QString &base)
const
418 im->m_parentId = parentId;
425 im->m_date = std::chrono::seconds(QDateTime::currentSecsSinceEpoch());
426 im->m_modTime = im->m_date;
431 im->m_filePath = fi.absoluteFilePath().mid(base.size() + 1);
432 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
440 im->m_extension = fi.suffix().toLower();
450 im->m_size = fi.size();
462 const QStringList &extra)
477 for (
const auto& path : std::as_const(
paths))
478 map.insert(i++, path);
493 return im->m_filePath.startsWith(
"/") ? im->m_filePath
500 "file_id, filename, name, dir_id, type, modtime, size, "
501 "extension, date, hidden, orientation, angle, path, zoom"
517 im->m_filePath = query.
value(1).toString();
518 im->m_baseName = query.
value(2).toString();
519 im->m_parentId = FS::ImageId(query.
value(3).toInt());
520 im->m_type = query.
value(4).toInt();
521 im->m_modTime = std::chrono::seconds(query.
value(5).toInt());
522 im->m_size = query.
value(6).toInt();
523 im->m_extension = query.
value(7).toString();
524 im->m_date = std::chrono::seconds(query.
value(8).toUInt());
525 im->m_isHidden = query.
value(9).toBool();
526 im->m_orientation = query.
value(10).toInt();
527 im->m_userThumbnail = FS::ImageId(query.
value(11).toInt());
528 im->m_comment = query.
value(12).toString();
529 im->m_device = query.
value(13).toInt();
530 im->m_url = FS::MakeFileUrl(im->m_filePath);
535 QString thumbPath(FS::ThumbPath(*im));
536 QString devPath(FS::ThumbDir(im->m_device));
537 QString url(FS::MakeThumbUrl(devPath, thumbPath));
539 im->m_thumbPath = FS::GetAbsThumbPath(devPath, thumbPath);
540 im->m_thumbNails.append(qMakePair(im->m_id, url));
556 const QString &refine)
const
561 QString select = QString(
"file_id IN (%1) %2").arg(FS::DbIds(ids), refine);
562 return ReadImages(dirs, files, select);
576 const QString &refine)
const
578 QString select = QString(
"dir_id IN (%1) %2").arg(FS::DbIds(ids), refine);
579 return ReadImages(dirs, files, select);
595 const QString &refine)
const
598 query.
prepare(QString(
"SELECT %1 FROM %2 "
599 "WHERE (dir_id = :ID1 OR file_id = :ID2) "
603 int dbId = FS::DbId(
id);
618 else if (im->m_id ==
id)
641 if (ReadImages(dirs, files, QString(
"file_id IN (%1)").arg(FS::DbIds(ids))) < 0)
647 ", LENGTH(filename) - LENGTH(REPLACE(filename, '/', ''))"
649 "FROM %2 WHERE filename LIKE :PREFIX "
652 for (
const auto& im1 : std::as_const(dirs))
655 query.
bindValue(
":PREFIX", im1->m_filePath +
"/%");
666 if (im2->IsDirectory())
688 if (GetChildren(QString::number(
id), files, dirs, refine) < 0)
691 for (
const auto& im : std::as_const(dirs))
692 if (!GetImageTree(im->m_id, files, refine))
718 if (im->IsDirectory())
719 dirs.insert(im->m_filePath, im);
721 files.insert(im->m_filePath, im);
737 if (
action ==
"DEVICE CLOSE ALL")
743 if (
action ==
"DEVICE CLEAR ALL")
746 query.
prepare(QString(
"TRUNCATE TABLE %1;").arg(m_table));
754 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE zoom = :FS;").arg(m_table));
775 if (checkForDuplicate)
777 query.
prepare(QString(
"SELECT file_id FROM %1 WHERE filename = :NAME;")
788 if (query.
size() > 0)
790 LOG(VB_FILE, LOG_DEBUG, QString(
"Image: %1 already exists in Db")
792 return query.
value(0).toInt();
796 query.
prepare(QString(
"INSERT INTO %1 (%2) VALUES (0, "
797 ":FILEPATH, :NAME, :PARENT, :TYPE, :MODTIME, "
798 ":SIZE, :EXTENSION, :DATE, :HIDDEN, :ORIENT, "
799 ":COVER, :COMMENT, :FS);").arg(m_table,
kDBColumns));
834 "filename = :FILEPATH, name = :NAME, "
835 "dir_id = :PARENT, type = :TYPE, "
836 "modtime = :MODTIME, size = :SIZE, "
837 "extension = :EXTENSION, date = :DATE, zoom = :FS, "
838 "hidden = :HIDDEN, orientation = :ORIENT, "
839 "angle = :COVER, path = :COMMENT "
840 "WHERE file_id = :ID;").arg(m_table));
875 if (!imList.isEmpty())
877 for (
const auto& im : std::as_const(imList))
878 ids << QString::number(FS::DbId(im->m_id));
880 QString idents = ids.join(
",");
882 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE file_id IN (%2);")
883 .arg(m_table, idents));
909 query.
prepare(QString(
"UPDATE %1 SET "
911 "WHERE file_id IN (%2);").arg(m_table, FS::DbIds(ids)));
912 query.
bindValue(
":HIDDEN", hide ? 1 : 0);
932 query.
prepare(QString(
"UPDATE %1 SET "
934 "WHERE file_id = :DIR").arg(m_table));
956 query.
prepare(QString(
"UPDATE %1 SET ").arg(m_table) +
957 "orientation = :ORIENTATION "
958 "WHERE file_id = :ID");
959 query.
bindValue(
":ORIENTATION", orientation);
979 const QString &selector)
const
982 query.
prepare(QString(
"SELECT %1 FROM %2 WHERE %3")
1014 int &pics,
int &videos,
int &sizeKb)
const
1016 QString whereClause;
1019 whereClause =
"WHERE filename LIKE "
1020 "( SELECT CONCAT(filename, '/%') "
1021 " FROM %2 WHERE file_id = :ID);";
1025 query.
prepare(QString(
"SELECT SUM(type <= :FLDR) AS Fldr, "
1026 " SUM(type = :PIC) AS Pics, "
1027 " SUM(type = :VID) AS Vids, "
1028 " SUM(size / 1024) "
1029 "FROM %2 %1;").arg(whereClause, m_table));
1041 else if (query.
next())
1043 dirs += query.
value(0).toInt();
1044 pics += query.
value(1).toInt();
1045 videos += query.
value(2).toInt();
1046 sizeKb += query.
value(3).toInt();
1101 query.
prepare(QString(
"ALTER TABLE %1 ENGINE = MEMORY;").arg(
m_table));
1105 LOG(VB_FILE, LOG_DEBUG, QString(
"Created Db table %1").arg(
m_table));
1130 QString orientation;
1146 tags.prepend(QString::number(
m_im->m_id));
1177 template <
class DBFS>
1183 if (DBFS::GetImages(
id, files, dirs) != 1)
1184 RESULT_ERR(
"Image not found", QString(
"Unknown image %1").arg(
id))
1186 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1188 QString absPath = DBFS::GetAbsFilePath(im);
1189 if (absPath.isEmpty())
1191 QString(
"File %1 not found").arg(im->m_filePath))
1197 RESULT_OK(QString(
"Fetching metadata for %1").arg(
id))
1208 template <
class DBFS>
1210 const QString &newBase)
const
1213 if (newBase.isEmpty() || newBase.contains(
"/") || newBase.contains(
"\\"))
1214 RESULT_ERR(
"Invalid name", QString(
"Invalid name %1").arg(newBase))
1219 if (DBFS::GetImages(
id, files, dirs) != 1)
1220 RESULT_ERR(
"Image not found", QString(
"Image %1 not in Db").arg(
id))
1222 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1225 QString oldPath = DBFS::GetAbsFilePath(im);
1226 if (oldPath.isEmpty())
1228 QString(
"File %1 not found").arg(im->m_filePath))
1231 QFileInfo oldFi = QFileInfo(oldPath);
1232 QString newName = im->IsDirectory()
1233 ? newBase : QString(
"%1.%2").arg(newBase, oldFi.suffix());
1235 im->m_filePath = DBFS::ConstructPath(DBFS::PathOf(im->m_filePath), newName);
1240 QString existPath = DBFS::GetAbsFilePath(im);
1241 if (!existPath.isEmpty())
1243 QString(
"Renaming %1 to %2 will create a duplicate of %3")
1244 .arg(oldPath, im->m_filePath, existPath))
1248 QString newPath = oldFi.dir().absoluteFilePath(newName);
1249 if (!QFile::rename(oldPath, newPath))
1251 QString(
"Rename of %1 -> %2 failed").arg(oldPath, newPath))
1253 if (im->IsDirectory())
1256 HandleScanRequest(
"START");
1261 DBFS::UpdateDbImage(*im);
1264 QStringList mesg(
"");
1265 mesg << QString::number(im->m_id);
1268 m_thumbGen->MoveThumbnail(im);
1271 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1273 RESULT_OK(QString(
"Renamed %1 -> %2").arg(oldPath, newPath))
1284 template <
class DBFS>
1291 DBFS::GetDescendants(ids, files, dirs);
1299 if (files.isEmpty() && dirs.isEmpty())
1300 RESULT_ERR(
"Delete failed", QString(
"Delete of %1 failed").arg(ids))
1306 QStringList mesg(m_thumbGen->DeleteThumbs(files));
1309 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1311 return QStringList(
"OK");
1327 template <
class DBFS>
1334 const QString separator = defs.takeFirst();
1339 QHash<QString, int> idMap;
1344 for (
const auto& def : std::as_const(defs))
1346 QStringList aDef = def.split(separator);
1349 if (aDef.size() != 6)
1352 LOG(VB_GENERAL, LOG_ERR,
1353 LOC + QString(
"Bad definition: (%1)").arg(def));
1357 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Creating %1").arg(aDef.join(
",")));
1359 im.
m_type = aDef[1].toInt();
1366 int newId = DBFS::InsertDbImage(im,
true);
1369 idMap.insert(aDef[0], newId);
1371 HandleScanRequest(
"START");
1386 template <
class DBFS>
1388 const QString &srcPath,
1389 QString destPath)
const
1392 if (destPath.contains(
".."))
1393 RESULT_ERR(
"Invalid path", QString(
"Invalid path %1").arg(destPath))
1399 bool ok = DBFS::GetDescendants(ids, files, dirs);
1400 images << dirs << files;
1402 if (!ok || images.isEmpty())
1403 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1405 if (!destPath.isEmpty() && !destPath.endsWith(QChar(
'/')))
1406 destPath.append(
"/");
1409 for (
const auto& im : std::as_const(images))
1411 QString old = im->m_filePath;
1413 if (srcPath.isEmpty())
1416 im->m_filePath.prepend(destPath);
1418 else if (im->m_filePath.startsWith(srcPath))
1421 im->m_filePath.replace(srcPath, destPath);
1426 LOG(VB_GENERAL, LOG_ERR,
1427 LOC + QString(
"Bad image: (%1 -> %2)").arg(srcPath, destPath));
1431 LOG(VB_FILE, LOG_DEBUG,
1432 LOC + QString(
"Db Renaming %1 -> %2").arg(old, im->m_filePath));
1434 DBFS::UpdateDbImage(*im);
1438 m_thumbGen->MoveThumbnail(im);
1440 HandleScanRequest(
"START");
1442 RESULT_OK(QString(
"Moved %1 from %2 -> %3").arg(ids, srcPath, destPath))
1453 template <
class DBFS>
1456 if (!DBFS::SetHidden(hide, ids))
1457 RESULT_ERR(
"Hide failed", QString(
"Db hide failed for %1").arg(ids))
1460 QStringList mesg = QStringList(
"") << ids;
1461 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1463 RESULT_OK(QString(
"Images %1 now %2hidden").arg(ids, hide ?
"" :
"un"))
1475 template <
class DBFS>
1477 const QString &ids)
const
1480 RESULT_ERR(
"Transform failed", QString(
"Bad transform %1").arg(transform))
1484 if (DBFS::GetImages(ids, files, dirs) < 1 || files.isEmpty())
1485 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1488 for (
const auto& im : std::as_const(files))
1490 int old = im->m_orientation;
1494 if (DBFS::SetOrientation(im->m_id, im->m_orientation))
1496 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Transformed %1 from %2 to %3")
1497 .arg(im->m_filePath).arg(old).arg(im->m_orientation));
1502 QStringList mesg(
"");
1505 mesg << m_thumbGen->DeleteThumbs(files);
1508 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1510 return QStringList(
"OK");
1522 template <
class DBFS>
1525 const QStringList &relPaths)
const
1530 if (DBFS::GetImages(destId, files, dirs) != 1 || dirs.isEmpty())
1532 QString(
"Image %1 not in Db").arg(destId))
1535 QString destPath = DBFS::GetAbsFilePath(dirs[0]);
1536 if (destPath.isEmpty())
1538 QString(
"Dest dir %1 not found").arg(dirs[0]->m_filePath))
1540 QDir destDir(destPath);
1541 bool succeeded =
false;
1542 for (
const auto& relPath : std::as_const(relPaths))
1545 if (relPath.isEmpty() || relPath.contains(
"..") || relPath.startsWith(QChar(
'/')))
1548 QString newPath = DBFS::ConstructPath(destDir.absolutePath(), relPath);
1549 if (!destDir.mkpath(relPath))
1551 LOG(VB_GENERAL, LOG_ERR,
1552 LOC + QString(
"Failed to create dir %1").arg(newPath));
1555 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Dir %1 created").arg(newPath));
1561 RESULT_ERR(
"Invalid Name", QString(
"Invalid name %1")
1562 .arg(relPaths.join(
",")))
1566 HandleScanRequest(
"START");
1568 return QStringList(
"OK");
1578 template <
class DBFS>
1581 if (!DBFS::SetCover(dir, cover))
1583 QString(
"Failed to set %1 to cover %2").arg(dir).arg(cover))
1586 QStringList mesg = QStringList(
"") << QString::number(dir);
1587 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1589 RESULT_OK(QString(
"Cover of %1 is now %2").arg(dir).arg(cover));
1601 template <
class DBFS>
1608 HandleScanRequest(
"START");
1610 RESULT_OK(QString(
"Using exclusions '%1'").arg(exclusions))
1621 template <
class DBFS>
1626 RESULT_ERR(
"Missing Scanner",
"Missing Scanner");
1628 if (command ==
"START")
1631 if (m_scanner->IsScanning())
1634 m_scanner->ChangeState(
true);
1637 else if (command ==
"STOP")
1640 if (!m_scanner->IsScanning())
1641 RESULT_ERR(
"Scanner not running",
"Scanner not running");
1643 m_scanner->ChangeState(
false);
1646 else if (command ==
"QUERY")
1648 return QStringList(
"OK") << m_scanner->GetProgress();
1650 else if (command.startsWith(QString(
"DEVICE")))
1652 m_scanner->EnqueueClear(devId, command);
1653 RESULT_OK(QString(
"Clearing device %1 %2").arg(command).arg(devId))
1655 RESULT_ERR(
"Unknown command", QString(
"Unknown command %1").arg(command));
1666 template <
class DBFS>
1668 (
const QStringList &message)
const
1670 if (message.size() != 2)
1672 QString(
"Bad request: %1").arg(message.join(
"|")))
1674 int priority = message.at(0).toInt()
1680 DBFS::GetImages(message.at(1), files, dirs);
1682 for (
const auto& im : std::as_const(files))
1684 m_thumbGen->CreateThumbnail(im, priority,
true);
1686 return QStringList(
"OK");
1699 template <
class DBFS>
1702 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1703 QMutableVectorIterator<ImagePtr> it(images);
1705 QMutableListIterator<ImagePtr> it(images);
1708 while (it.hasPrevious())
1713 QString absFilename = DBFS::GetAbsFilePath(im);
1715 bool success = !absFilename.isEmpty()
1716 && (im->IsFile() ? QFile::remove(absFilename)
1717 : QDir::root().rmdir(absFilename));
1719 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Deleted %1").arg(absFilename));
1722 LOG(VB_GENERAL, LOG_ERR,
LOC +
1723 QString(
"Can't delete %1").arg(absFilename));
1757 "CASE WHEN type <= %1 THEN %4, "
1758 "CASE WHEN type > %1 THEN %5 ")
1810 count += ImageHandler::GetDirectory(
id, parent, files, dirs,
m_refineClause);
1838 if (!lists.second.isEmpty())
1841 return ImageHandler::GetImages(lists.first, files, dirs,
m_refineClause);
1860 count += ImageHandler::GetChildren(QString::number(
id), files, dirs,
1878 if (!lists.second.isEmpty())
1881 ImageHandler::GetDescendants(lists.first, files, dirs);
1909 int &videos,
int &sizeKb)
const
1916 ImageHandler::GetDescendantCount(
id,
true, dirs, pics, videos, sizeKb);
1922 dirs, pics, videos, sizeKb);
1927 ImageHandler::GetDescendantCount(
id,
false, dirs, pics, videos, sizeKb);
1983 if (!lists.second.isEmpty())
1985 LOG(VB_FILE, LOG_DEBUG,
LOC +
1986 QString(
"Sending CREATE_THUMBNAILS %1 (forFolder %2)")
1987 .arg(lists.second).arg(forFolder));
1989 QStringList message;
1990 message << QString::number(static_cast<int>(forFolder)) << lists.second;
1994 if (!lists.first.isEmpty())
1996 LOG(VB_FILE, LOG_DEBUG,
LOC +
1997 QString(
"Creating local thumbnails %1 (forFolder %2)")
1998 .arg(lists.first).arg(forFolder));
2000 QStringList message;
2001 message << QString::number(static_cast<int>(forFolder)) << lists.first;
2015 QStringList command;
2016 command << (start ?
"START" :
"STOP");
2020 command.push_front(
"IMAGE_SCAN");
2022 return ok ?
"" : command[1];
2027 return "Couldn't create database";
2030 return err[0] ==
"OK" ?
"" : err[1];
2041 QStringList strList;
2042 strList <<
"IMAGE_SCAN" <<
"QUERY";
2046 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Scan query failed : %1")
2047 .arg(strList.join(
",")));
2063 QString result =
"";
2065 if (!lists.second.isEmpty())
2067 QStringList message;
2068 message <<
"IMAGE_HIDE" << QString::number(
static_cast<int>(hidden)) << lists.second;
2071 result = message[1];
2074 if (!lists.first.isEmpty())
2076 QStringList err =
HandleHide(hidden, lists.first);
2095 QString result =
"";
2097 if (!lists.second.isEmpty())
2099 QStringList message;
2100 message <<
"IMAGE_TRANSFORM" << QString::number(transform) << lists.second;
2103 result = message[1];
2106 if (!lists.first.isEmpty())
2126 QStringList message;
2127 message <<
"IMAGE_COVER" << QString::number(parent) << QString::number(cover);
2130 return ok ?
"" : message[1];
2134 return err[0] ==
"OK" ?
"" : err[1];
2154 QStringList message(
"IMAGE_SCAN");
2155 message <<
"DEVICE CLEAR ALL";
2168 QStringList message(
"IMAGE_IGNORE");
2169 message << excludes;
2171 return ok ?
"" : message[1];
2184 QString destId = QString::number(parent);
2188 QStringList message(
"IMAGE_CREATE_DIRS");
2189 message << destId << QString::number(static_cast<int>(rescan)) << names;
2191 return ok ?
"" : message[1];
2193 QStringList err =
HandleDirs(destId, rescan, names);
2194 return (err[0] ==
"OK") ?
"" : err[1];
2208 QStringList message(
"IMAGE_RENAME");
2209 message << QString::number(im->m_id) << name;
2211 return ok ?
"" : message[1];
2213 QStringList err =
HandleRename(QString::number(im->m_id), name);
2214 return (err[0] ==
"OK") ?
"" : err[1];
2226 if (images.isEmpty())
2230 const QString seperator(
"...");
2231 QStringList imageDefs(seperator);
2233 for (
const auto& im : std::as_const(images))
2239 aDef << QString::number(im->m_id)
2240 << QString::number(im->m_type)
2242 << QString::number(
static_cast<int>(im->m_isHidden))
2243 << QString::number(im->m_orientation)
2244 << QString::number(im->m_userThumbnail);
2246 imageDefs << aDef.join(seperator);
2253 return (err[0] ==
"OK") ?
"" : err[1];
2255 imageDefs.prepend(
"IMAGE_COPY");
2257 return ok ?
"" : imageDefs[1];
2269 const QString &srcPath)
2272 for (
const auto& im : std::as_const(images))
2273 idents << QString::number(im->m_id);
2276 if (destDir->IsLocal())
2278 QStringList err =
HandleDbMove(idents.join(
","), srcPath,
2279 destDir->m_filePath);
2280 return (err[0] ==
"OK") ?
"" : err[1];
2283 QStringList message(
"IMAGE_MOVE");
2284 message << idents.join(
",") << srcPath << destDir->m_filePath;
2286 return ok ?
"" : message[1];
2299 QString result =
"";
2300 if (!lists.second.isEmpty())
2302 QStringList message(
"IMAGE_DELETE");
2303 message << lists.second;
2307 result = message[1];
2309 if (!lists.first.isEmpty())
2330 std::chrono::seconds secs = 0s;
2333 if (im->m_date > 0s)
2339 secs = im->m_modTime;
2356 std::chrono::seconds secs(im->m_date > 0s ? im->m_date : im->m_modTime);
2369 return tr(
"Gallery");
2371 return tr(
"Photographs");
2401 return dev + path.replace(
"/",
" > ");
2408 ?
"DEVICE CLOSE ALL"
2409 : eject ?
"DEVICE EJECT" :
"DEVICE REMOVE";
2425 QList<MythMediaDevice*> devices
2428 for (
auto *dev : std::as_const(devices))
2431 OpenDevice(dev->getDeviceModel(), dev->getMountPath(), dev);
2440 for (
int devId : std::as_const(absentees))
2446 LOG(VB_GENERAL, LOG_ERR,
LOC + err);
2460 if (!event || !monitor)
2470 LOG(VB_FILE, LOG_DEBUG,
LOC +
2471 QString(
"Media event for %1 (%2) at %3, type %4, status %5 (was %6)")
2477 LOG(VB_FILE, LOG_DEBUG,
LOC +
2478 QString(
"Ignoring event - wrong type %1").arg(
type));
2504 auto *
tmp =
new QTemporaryDir(QDir::tempPath() %
"/" %
IMPORTDIR %
"-XXXXXX");
2505 if (!
tmp->isValid())
2511 QString time(QDateTime::currentDateTime().
toString(
"mm:ss"));
ImageAdapterBase()
Constructor.
static ImageManagerBe * s_instance
BE Gallery instance.
@ kPicAndVideo
Show Pictures & Videos.
@ kSortByNameDesc
Name Z-A.
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.
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.
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.
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.
@ kSortByExtDesc
Extension Z-A.
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).
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.
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)
@ kDirectory
A device sub directory.
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.
@ kDirRequestPriority
Client request to display a directory thumbnail.
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.
static constexpr const char * IMPORTDIR
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.
QStringList HandleTransform(int transform, const QString &ids) const
Change orientation of pictures by applying a transformation.
static ImageManagerFe & getInstance()
Get Frontend Gallery.
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.
@ kSortByDateDesc
Exif date Latest -> Earliest.
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...
QStringList HandleDbMove(const QString &ids, const QString &srcPath, QString destPath) const
Updates images that have been renamed.
@ kSortByNameAsc
Name A-Z.
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.
@ kSortByExtAsc
Extension A-Z.
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)
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.
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()
@ kSortByModTimeDesc
File modified time Latest -> Earliest.
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.
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.
@ kSortByDateAsc
Exif date Earliest -> Latest.
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.
void SetRefinementClause()
Sets filter/ordering SQL clause used when reading database according to current filter/sort settings.
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.
@ kSortByModTimeAsc
File modified time Earliest -> Latest.
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
@ kDevice
Storage Group and local mounted media.
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.
@ kSortBySizeAsc
File size Smallest -> Largest.
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.
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.
@ kUnknown
Unprocessable file type.
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.
@ kSortBySizeDesc
File size Largest -> Smallest.
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)
@ kVideoOnly
Hide pictures.
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.
QString m_filePath
Absolute for local images. Usually SG-relative for remotes.
@ kPicRequestPriority
Client request to display an image thumbnail.
bool DetectLocalDevices()
Detect and scan local devices.