15#define LOC QString("ImageManager: ")
16#define DBLOC QString("ImageDb(%1): ").arg(m_table)
21static 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"); }
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();
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())
294 QStringList allExt = m_imageFileExt + m_videoFileExt;
295 for (
const auto& ext : std::as_const(allExt))
299 m_dirFilter.setNameFilters(glob);
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));
1151 QString orientation;
1167 tags.prepend(QString::number(
m_im->m_id));
1198template <
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))
1229template <
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))
1305template <
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");
1348template <
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");
1407template <
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))
1474template <
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"))
1496template <
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");
1543template <
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");
1599template <
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));
1622template <
class DBFS>
1629 HandleScanRequest(
"START");
1631 RESULT_OK(QString(
"Using exclusions '%1'").arg(exclusions))
1642template <
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));
1687template <
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");
1720template <
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"));
bool RemoveFromDB(Bookmark *site)
QList< int > GetAbsentees()
Get list of mountpoints for non-import devices.
int LocateMount(const QString &mount) const
Find the id of a device.
QString ThumbDir(int fs) const
QStringList CloseDevices(int devId, const QString &action)
Remove a device (or all devices)
QString DeviceMount(int devId) const
Get path at which the device is mounted.
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...
QString DeviceName(int devId) const
Get model name of the device.
DeviceMap m_devices
Device store.
StringMap GetDeviceDirs() const
Get all known devices.
A device containing images (ie. USB stick, CD, storage group etc)
void RemoveThumbs(void) const
Delete thumbnails associated with device.
void setPresent(MythMediaDevice *media)
QString m_thumbs
Dir sub-path of device thumbnails.
bool m_present
True when gallery UI is running & device is useable. Always true for imports.
Device(QString name, QString mount, MythMediaDevice *media=nullptr, QTemporaryDir *import=nullptr)
void Close(bool eject=false)
Releases device.
MythMediaDevice * m_media
Set for MediaMonitor devices only.
~Device()
Delete device, its thumbnails and any imported images.
static void RemoveDirContents(const QString &path)
Clears all files and sub-dirs within a directory.
QString m_name
Device model/volume/id.
QString m_mount
Mountpoint.
QTemporaryDir * m_dir
Dir path of images: import devices only.
static FileAssociations & getFileAssociation()
const association_list & getList() const
std::vector< file_association > association_list
static QString ThumbPath(const ImageItem &im)
Thumbnails of videos are a JPEG snapshot with jpg suffix appended.
static QString PathOf(const QString &path)
Extracts path from a filepath.
static QStringList SupportedImages()
Return recognised pictures.
ImageNodeType GetImageType(const QString &ext) const
Determine file type from its extension.
static QString FormatSize(int sizeKib)
static QStringList SupportedVideos()
Return recognised video extensions.
ImageAdapterBase()
Constructor.
static QString BaseNameOf(const QString &path)
Extracts file name (incl extension) from a filepath.
static QString GetAbsThumbPath(const QString &devPath, const QString &path)
Get absolute filepath for thumbnail of an image.
static void Notify(const QString &mesg, const QStringList &extra)
Send local message to UI about local ids.
ImageItem * CreateItem(const QFileInfo &fi, int parentId, int devId, const QString &base) const
Construct a local image from a file.
StringMap GetScanDirs() const
Returns SG dirs.
StorageGroup m_sg
Images storage group.
QString m_hostname
Host of SG.
QString MakeFileUrl(const QString &path) const
Construct URL of a remote image.
QString MakeThumbUrl(const QString &devPath, const QString &path="") const
Construct URL of the thumbnail of a remote image.
ImageItem * CreateItem(const QFileInfo &fi, int parentId, int devId, const QString &base) const
Construct a remote image from a file.
QString GetAbsFilePath(const ImagePtrK &im) const
Get absolute filepath for a remote image.
static void Notify(const QString &mesg, const QStringList &extra)
Send message to all clients about remote ids.
ImageDbLocal()
Local database constructor.
bool CreateTable()
Create local database table, if it doesn't exist.
void DropTable()
Remove local image table.
void GetDescendants(const ImageIdList &ids, ImageList &files, ImageList &dirs) const
Return all (local or remote) images that are direct children of a dir.
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 m_refineClause
SQL clause for image filtering/ordering.
int m_fileOrder
Display ordering of pics/videos.
static QString OrderSelector(int order)
Generate SQL ordering clause.
ImageDbSg * m_remote
Remote database access.
int GetImages(const ImageIdList &ids, ImageList &files, ImageList &dirs) const
Returns images (local or remote but not a combination)
bool m_showHidden
Whether hidden images are displayed.
static QString TypeSelector(int type)
Generate SQL type filter clause.
void GetImageTree(int id, ImageList &files) const
Return all files (local or remote) in the sub-trees of a dir.
void SetRefinementClause()
Sets filter/ordering SQL clause used when reading database according to current filter/sort settings.
int GetChildren(int id, ImageList &files, ImageList &dirs) const
Return (local or remote) images that are direct children of a dir.
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.
int m_showType
Type of images to display - pic only/video only/both.
int m_dirOrder
Display ordering of dirs.
ImageDbSg()
SG database constructor.
void ClearDb(int devId, const QString &action)
Clear Db for device & remove device.
int ReadImages(ImageList &dirs, ImageList &files, const QString &selector) const
Read selected database images/dirs.
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.
int GetDirectory(int id, ImagePtr &parent, ImageList &files, ImageList &dirs, const QString &refine) const
Read a dir and its immediate children from Db.
bool SetOrientation(int id, int orientation) const
Sets image orientation in Db.
int GetChildren(const QString &ids, ImageList &files, ImageList &dirs, const QString &refine="") const
Read immediate children of a dir.
bool SetHidden(bool hide, const QString &ids) const
Sets hidden status of an image/dir in database.
bool GetImageTree(int id, ImageList &files, const QString &refine) const
Returns all files in the sub-tree of a dir.
ImageItem * CreateImage(const MSqlQuery &query) const
Create image from Db query data.
bool UpdateDbImage(ImageItemK &im) const
Updates or creates database image or dir.
int InsertDbImage(ImageItemK &im, bool checkForDuplicate=false) const
Adds new image to database, optionally checking for existing filepath.
bool GetDescendants(const QString &ids, ImageList &files, ImageList &dirs) const
Return images and all of their descendants.
bool ReadAllImages(ImageHash &files, ImageHash &dirs) const
Read all database images and dirs as map. No filters or ordering applied.
QString m_table
Db table name.
int GetImages(const QString &ids, ImageList &files, ImageList &dirs, const QString &refine="") const
Read database images/dirs by id.
bool SetCover(int dir, int id) const
Set the thumbnail(s) to be used for a dir.
QStringList RemoveFromDB(const ImageList &imList) const
Remove images/dirs from database.
QStringList HandleDbCreate(QStringList defs) const
Creates images for files created by a copy operation.
QStringList HandleCover(int dir, int cover) const
Updates/resets cover thumbnail for an image dir.
QStringList HandleDelete(const QString &ids) const
Deletes images/dirs.
void RemoveFiles(ImageList &images) const
Deletes images and dirs from the filesystem.
QStringList HandleTransform(int transform, const QString &ids) const
Change orientation of pictures by applying a transformation.
QStringList HandleHide(bool hide, const QString &ids) const
Hides/unhides images/dirs.
QStringList HandleDirs(const QString &destId, bool rescan, const QStringList &relPaths) const
Creates new image directories.
QStringList HandleCreateThumbnails(const QStringList &message) const
Creates thumbnails on-demand.
QStringList HandleGetMetadata(const QString &id) const
Read meta data for an image.
QStringList HandleScanRequest(const QString &command, int devId=DEVICE_INVALID) const
Process scan requests.
QStringList HandleDbMove(const QString &ids, const QString &srcPath, QString destPath) const
Updates images that have been renamed.
QStringList HandleRename(const QString &id, const QString &newBase) const
Change name of an image/dir.
QStringList HandleIgnore(const QString &exclusions) const
Updates exclusion list for images.
Represents a picture, video or directory.
static bool IsLocalParent(int id)
Parents of locals are locals or root.
int m_id
Uniquely identifies an image (file/dir).
QString m_extension
Image file extension.
std::chrono::seconds m_date
Image creation date, from Exif metadata.
int m_size
Filesize (files only)
static bool IsLocalId(int id)
Determine image type (local/remote) from its id. Root/Gallery is remote.
bool m_isHidden
If true, image won't be shown.
QString m_comment
User comment, from Exif metadata.
QString m_baseName
File/Dir name with extension (no path)
int m_parentId
Id of parent dir.
int m_device
Id of media device. Always 0 (SG) for remotes, 1+ for local devices.
static StringPair PartitionIds(const ImageIdList &ids)
Separates list of ids into a list of local ids and a list of remote ids.
QString m_filePath
Absolute for local images. Usually SG-relative for remotes.
std::chrono::seconds m_modTime
Filesystem modified datestamp.
int m_orientation
Image orientation.
int m_userThumbnail
Id of thumbnail to use as cover (dirs only)
int m_type
Type of node: dir, video etc.
The image manager to be used by the Backend.
static ImageManagerBe * s_instance
BE Gallery instance.
static ImageManagerBe * getInstance()
Get Backend Gallery.
The image manager for use by Frontends.
QString ShortDateOf(const ImagePtrK &im) const
Return a short datestamp for thumbnail captions.
void RequestMetaData(int id)
Requests all exif/ffmpeg tags for an image, which returns by event.
QString ChangeOrientation(ImageFileTransform transform, const ImageIdList &ids)
Apply an orientation transform to images.
QString CreateImages(int destId, const ImageListK &images)
Copies database images (but not the files themselves).
ImageManagerFe(int order, int dirOrder, bool showAll, int showType, QString dateFormat)
static void ClearStorageGroup()
Clear database & thumbnails of Storage Group images.
QString MoveDbImages(const ImagePtrK &destDir, ImageListK &images, const QString &srcPath)
Moves database images (but not the files themselves).
QString CrumbName(ImageItemK &im, bool getPath=false) const
Return a displayable name (with optional path) for an image.
static QString LongDateOf(const ImagePtrK &im)
Return a timestamp/datestamp for an image or dir.
static QStringList ScanQuery()
Returns storage group scanner status.
void DeviceEvent(MythMediaEvent *event)
Manage events for local devices.
QString DeleteFiles(const ImageIdList &ids)
Delete images.
QString DeviceCaption(ImageItemK &im) const
Return translated device name.
QString MakeDir(int parent, const QStringList &names, bool rescan=true)
Create directories.
QString SetCover(int parent, int cover)
Set image to use as a cover thumbnail(s)
static ImageManagerFe * s_instance
FE Gallery instance.
QString HideFiles(bool hidden, const ImageIdList &ids)
Hide/unhide images.
void CloseDevices(int devId=DEVICE_INVALID, bool eject=false)
static QString IgnoreDirs(const QString &excludes)
Set directories to ignore during scans of the storage group.
static ImageManagerFe & getInstance()
Get Frontend Gallery.
void CreateThumbnails(const ImageIdList &ids, bool forFolder)
Create thumbnails or verify that they already exist.
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...
QString ScanImagesAction(bool start, bool local=false)
Handle scanner start/stop commands.
QString m_dateFormat
UI format for thumbnail date captions.
QString RenameFile(const ImagePtrK &im, const QString &name)
Rename an image.
bool DetectLocalDevices()
Detect and scan local devices.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
QVariant lastInsertId()
Return the id of the last inserted row.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
static MThreadPool * globalInstance(void)
void start(QRunnable *runnable, const QString &debugName, int priority=0)
This class contains the runtime context for MythTV.
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
int GetNumSetting(const QString &key, int defaultval=0)
void SendEvent(const MythEvent &event)
bool GetBoolSetting(const QString &key, bool defaultval=false)
static void DBError(const QString &where, const MSqlQuery &query)
This class is used as a container for messages.
Encapsulates Exif orientation processing.
int Transform(int transform)
Adjust orientation to apply a transform to an image.
QString Description() const
Generate text description of orientation.
QStringList GetDirList(void) const
QString FindFile(const QString &filename)
QString FindNextDirMostFree(void)
static constexpr const char * STORAGE_GROUP_MOUNT
static Device kNullDevice
#define RESULT_ERR(ERR, MESG)
static constexpr const char * DB_TABLE
static constexpr const char * IMPORTDIR
static constexpr const char * kDBColumns
Manages a collection of images.
static constexpr const char * IMAGE_STORAGE_GROUP
static constexpr int DEVICE_INVALID
static constexpr const char * TEMP_SUBDIR
@ kVideoOnly
Hide pictures.
@ kPicAndVideo
Show Pictures & Videos.
static constexpr const char * THUMBNAIL_SUBDIR
static constexpr const char * THUMBNAIL_STORAGE_GROUP
@ kPicRequestPriority
Client request to display an image thumbnail.
@ kDirRequestPriority
Client request to display a directory thumbnail.
QHash< QString, ImagePtr > ImageHash
QVector< ImagePtr > ImageList
@ kSortBySizeAsc
File size Smallest -> Largest.
@ kSortByNameAsc
Name A-Z.
@ kSortByDateAsc
Exif date Earliest -> Latest.
@ kSortByExtAsc
Extension A-Z.
@ kSortByExtDesc
Extension Z-A.
@ kSortByNameDesc
Name Z-A.
@ kSortBySizeDesc
File size Largest -> Smallest.
@ kSortByModTimeAsc
File modified time Earliest -> Latest.
@ kSortByModTimeDesc
File modified time Latest -> Earliest.
@ kSortByDateDesc
Exif date Latest -> Earliest.
QList< ImagePtrK > ImageListK
QSharedPointer< ImageItemK > ImagePtrK
QMap< int, QString > StringMap
static constexpr int GALLERY_DB_ID
static constexpr int PHOTO_DB_ID
@ kDevice
Storage Group and local mounted media.
@ kDirectory
A device sub directory.
@ kUnknown
Unprocessable file type.
QSharedPointer< ImageItem > ImagePtr
QPair< QString, QString > StringPair
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static void clear(SettingsMap &cache, SettingsMap &overrides, const QString &myKey)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
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...
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
@ kDateFull
Default local time.
@ kTime
Default local time.
@ kAddYear
Add year to string if not included.
const std::array< const std::string, 8 > formats