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")
207 else if (
action ==
"DEVICE CLEAR ALL")
210 for (
const auto *dev : qAsConst(
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 #if QT_VERSION < QT_VERSION_CHECK(5,15,0)
257 for (
auto it =
m_devices.constKeyValueBegin();
258 it !=
m_devices.constKeyValueEnd(); ++it)
261 paths.insert((*it).first, (*it).second->m_mount);
264 for (
auto it =
m_devices.constKeyValueBegin();
265 it !=
m_devices.constKeyValueEnd(); ++it)
268 paths.insert(it->first, it->second->m_mount);
279 #if QT_VERSION < QT_VERSION_CHECK(5,15,0)
280 for (
auto it =
m_devices.constKeyValueBegin();
281 it !=
m_devices.constKeyValueEnd(); it++)
283 Device *dev = (*it).second;
285 absent << (*it).first;
288 for (
auto it =
m_devices.constKeyValueBegin();
289 it !=
m_devices.constKeyValueEnd(); it++)
304 m_imageFileExt(SupportedImages()),
305 m_videoFileExt(SupportedVideos())
310 for (
const auto& ext : qAsConst(allExt))
315 m_dirFilter.setFilter(QDir::AllDirs | QDir::Files | QDir::Readable |
316 QDir::NoDotAndDotDot | QDir::NoSymLinks);
320 m_dirFilter.setSorting(QDir::DirsLast | QDir::Time | QDir::Reversed);
331 QList<QByteArray> supported = QImageReader::supportedImageFormats();
332 for (
const auto& ext : qAsConst(supported))
348 for (
const auto & fa : faList)
350 if (!fa.use_default && fa.playcommand ==
"Internal")
351 formats << QString(fa.extension);
366 int devId,
const QString & )
const
370 im->m_parentId = parentId;
371 im->m_device = devId;
372 im->m_filePath = fi.absoluteFilePath();
377 auto secs = im->m_filePath.contains(
IMPORTDIR)
378 ? fi.lastModified().toSecsSinceEpoch()
379 : QDateTime::currentSecsSinceEpoch();
380 im->m_date = std::chrono::seconds(secs);
381 im->m_modTime = im->m_date;
386 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
394 im->m_extension = fi.suffix().toLower();
404 im->m_size = fi.size();
416 const QStringList &extra)
432 int ,
const QString &base)
const
437 im->m_parentId = parentId;
444 im->m_date = std::chrono::seconds(QDateTime::currentSecsSinceEpoch());
445 im->m_modTime = im->m_date;
450 im->m_filePath = fi.absoluteFilePath().mid(base.size() + 1);
451 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
459 im->m_extension = fi.suffix().toLower();
469 im->m_size = fi.size();
481 const QStringList &extra)
496 for (
const auto& path : qAsConst(
paths))
497 map.insert(i++, path);
512 return im->m_filePath.startsWith(
"/") ? im->m_filePath
519 "file_id, filename, name, dir_id, type, modtime, size, "
520 "extension, date, hidden, orientation, angle, path, zoom"
536 im->m_filePath = query.
value(1).toString();
537 im->m_baseName = query.
value(2).toString();
538 im->m_parentId = FS::ImageId(query.
value(3).toInt());
539 im->m_type = query.
value(4).toInt();
540 im->m_modTime = std::chrono::seconds(query.
value(5).toInt());
541 im->m_size = query.
value(6).toInt();
542 im->m_extension = query.
value(7).toString();
543 im->m_date = std::chrono::seconds(query.
value(8).toUInt());
544 im->m_isHidden = query.
value(9).toBool();
545 im->m_orientation = query.
value(10).toInt();
546 im->m_userThumbnail = FS::ImageId(query.
value(11).toInt());
547 im->m_comment = query.
value(12).toString();
548 im->m_device = query.
value(13).toInt();
549 im->m_url = FS::MakeFileUrl(im->m_filePath);
554 QString thumbPath(FS::ThumbPath(*im));
555 QString devPath(FS::ThumbDir(im->m_device));
556 QString url(FS::MakeThumbUrl(devPath, thumbPath));
558 im->m_thumbPath = FS::GetAbsThumbPath(devPath, thumbPath);
559 im->m_thumbNails.append(qMakePair(im->m_id, url));
575 const QString &refine)
const
580 QString select = QString(
"file_id IN (%1) %2").arg(FS::DbIds(ids), refine);
581 return ReadImages(dirs, files, select);
595 const QString &refine)
const
597 QString select = QString(
"dir_id IN (%1) %2").arg(FS::DbIds(ids), refine);
598 return ReadImages(dirs, files, select);
614 const QString &refine)
const
617 query.
prepare(QString(
"SELECT %1 FROM %2 "
618 "WHERE (dir_id = :ID1 OR file_id = :ID2) "
622 int dbId = FS::DbId(
id);
637 else if (im->m_id ==
id)
660 if (ReadImages(dirs, files, QString(
"file_id IN (%1)").arg(FS::DbIds(ids))) < 0)
666 ", LENGTH(filename) - LENGTH(REPLACE(filename, '/', ''))"
668 "FROM %2 WHERE filename LIKE :PREFIX "
671 for (
const auto& im1 : qAsConst(dirs))
674 query.
bindValue(
":PREFIX", im1->m_filePath +
"/%");
685 if (im2->IsDirectory())
707 if (GetChildren(QString::number(
id), files, dirs, refine) < 0)
710 for (
const auto& im : qAsConst(dirs))
711 if (!GetImageTree(im->m_id, files, refine))
737 if (im->IsDirectory())
738 dirs.insert(im->m_filePath, im);
740 files.insert(im->m_filePath, im);
756 if (
action ==
"DEVICE CLOSE ALL")
762 if (
action ==
"DEVICE CLEAR ALL")
765 query.
prepare(QString(
"TRUNCATE TABLE %1;").arg(m_table));
773 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE zoom = :FS;").arg(m_table));
794 if (checkForDuplicate)
796 query.
prepare(QString(
"SELECT file_id FROM %1 WHERE filename = :NAME;")
807 if (query.
size() > 0)
809 LOG(VB_FILE, LOG_DEBUG, QString(
"Image: %1 already exists in Db")
811 return query.
value(0).toInt();
815 query.
prepare(QString(
"INSERT INTO %1 (%2) VALUES (0, "
816 ":FILEPATH, :NAME, :PARENT, :TYPE, :MODTIME, "
817 ":SIZE, :EXTENSION, :DATE, :HIDDEN, :ORIENT, "
818 ":COVER, :COMMENT, :FS);").arg(m_table,
kDBColumns));
853 "filename = :FILEPATH, name = :NAME, "
854 "dir_id = :PARENT, type = :TYPE, "
855 "modtime = :MODTIME, size = :SIZE, "
856 "extension = :EXTENSION, date = :DATE, zoom = :FS, "
857 "hidden = :HIDDEN, orientation = :ORIENT, "
858 "angle = :COVER, path = :COMMENT "
859 "WHERE file_id = :ID;").arg(m_table));
894 if (!imList.isEmpty())
896 for (
const auto& im : qAsConst(imList))
897 ids << QString::number(FS::DbId(im->m_id));
899 QString idents = ids.join(
",");
901 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE file_id IN (%2);")
902 .arg(m_table, idents));
928 query.
prepare(QString(
"UPDATE %1 SET "
930 "WHERE file_id IN (%2);").arg(m_table, FS::DbIds(ids)));
931 query.
bindValue(
":HIDDEN", hide ? 1 : 0);
951 query.
prepare(QString(
"UPDATE %1 SET "
953 "WHERE file_id = :DIR").arg(m_table));
975 query.
prepare(QString(
"UPDATE %1 SET ").arg(m_table) +
976 "orientation = :ORIENTATION "
977 "WHERE file_id = :ID");
978 query.
bindValue(
":ORIENTATION", orientation);
998 const QString &selector)
const
1001 query.
prepare(QString(
"SELECT %1 FROM %2 WHERE %3")
1009 while (query.
next())
1018 return query.
size();
1033 int &pics,
int &videos,
int &sizeKb)
const
1035 QString whereClause;
1038 whereClause =
"WHERE filename LIKE "
1039 "( SELECT CONCAT(filename, '/%') "
1040 " FROM %2 WHERE file_id = :ID);";
1044 query.
prepare(QString(
"SELECT SUM(type <= :FLDR) AS Fldr, "
1045 " SUM(type = :PIC) AS Pics, "
1046 " SUM(type = :VID) AS Vids, "
1047 " SUM(size / 1024) "
1048 "FROM %2 %1;").arg(whereClause, m_table));
1060 else if (query.
next())
1062 dirs += query.
value(0).toInt();
1063 pics += query.
value(1).toInt();
1064 videos += query.
value(2).toInt();
1065 sizeKb += query.
value(3).toInt();
1120 query.
prepare(QString(
"ALTER TABLE %1 ENGINE = MEMORY;").arg(
m_table));
1124 LOG(VB_FILE, LOG_DEBUG, QString(
"Created Db table %1").arg(
m_table));
1149 QString orientation;
1165 tags.prepend(QString::number(
m_im->m_id));
1196 template <
class DBFS>
1202 if (DBFS::GetImages(
id, files, dirs) != 1)
1203 RESULT_ERR(
"Image not found", QString(
"Unknown image %1").arg(
id))
1205 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1207 QString absPath = DBFS::GetAbsFilePath(im);
1208 if (absPath.isEmpty())
1210 QString(
"File %1 not found").arg(im->m_filePath))
1216 RESULT_OK(QString(
"Fetching metadata for %1").arg(
id))
1227 template <
class DBFS>
1229 const QString &newBase)
const
1232 if (newBase.isEmpty() || newBase.contains(
"/") || newBase.contains(
"\\"))
1233 RESULT_ERR(
"Invalid name", QString(
"Invalid name %1").arg(newBase))
1238 if (DBFS::GetImages(
id, files, dirs) != 1)
1239 RESULT_ERR(
"Image not found", QString(
"Image %1 not in Db").arg(
id))
1241 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1244 QString oldPath = DBFS::GetAbsFilePath(im);
1245 if (oldPath.isEmpty())
1247 QString(
"File %1 not found").arg(im->m_filePath))
1250 QFileInfo oldFi = QFileInfo(oldPath);
1251 QString newName = im->IsDirectory()
1252 ? newBase : QString(
"%1.%2").arg(newBase, oldFi.suffix());
1254 im->m_filePath = DBFS::ConstructPath(DBFS::PathOf(im->m_filePath), newName);
1259 QString existPath = DBFS::GetAbsFilePath(im);
1260 if (!existPath.isEmpty())
1262 QString(
"Renaming %1 to %2 will create a duplicate of %3")
1263 .arg(oldPath, im->m_filePath, existPath))
1267 QString newPath = oldFi.dir().absoluteFilePath(newName);
1268 if (!QFile::rename(oldPath, newPath))
1270 QString(
"Rename of %1 -> %2 failed").arg(oldPath, newPath))
1272 if (im->IsDirectory())
1275 HandleScanRequest(
"START");
1280 DBFS::UpdateDbImage(*im);
1283 QStringList mesg(
"");
1284 mesg << QString::number(im->m_id);
1287 m_thumbGen->MoveThumbnail(im);
1290 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1292 RESULT_OK(QString(
"Renamed %1 -> %2").arg(oldPath, newPath))
1303 template <
class DBFS>
1310 DBFS::GetDescendants(ids, files, dirs);
1318 if (files.isEmpty() && dirs.isEmpty())
1319 RESULT_ERR(
"Delete failed", QString(
"Delete of %1 failed").arg(ids))
1325 QStringList mesg(m_thumbGen->DeleteThumbs(files));
1328 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1330 return QStringList(
"OK");
1346 template <
class DBFS>
1353 const QString separator = defs.takeFirst();
1358 QHash<QString, int> idMap;
1363 for (
const auto& def : qAsConst(defs))
1365 QStringList aDef = def.split(separator);
1368 if (aDef.size() != 6)
1371 LOG(VB_GENERAL, LOG_ERR,
1372 LOC + QString(
"Bad definition: (%1)").arg(def));
1376 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Creating %1").arg(aDef.join(
",")));
1378 im.
m_type = aDef[1].toInt();
1385 int newId = DBFS::InsertDbImage(im,
true);
1388 idMap.insert(aDef[0], newId);
1390 HandleScanRequest(
"START");
1405 template <
class DBFS>
1407 const QString &srcPath,
1408 QString destPath)
const
1411 if (destPath.contains(
".."))
1412 RESULT_ERR(
"Invalid path", QString(
"Invalid path %1").arg(destPath))
1418 bool ok = DBFS::GetDescendants(ids, files, dirs);
1419 images << dirs << files;
1421 if (!ok || images.isEmpty())
1422 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1424 if (!destPath.isEmpty() && !destPath.endsWith(QChar(
'/')))
1425 destPath.append(
"/");
1428 for (
const auto& im : qAsConst(images))
1430 QString old = im->m_filePath;
1432 if (srcPath.isEmpty())
1435 im->m_filePath.prepend(destPath);
1437 else if (im->m_filePath.startsWith(srcPath))
1440 im->m_filePath.replace(srcPath, destPath);
1445 LOG(VB_GENERAL, LOG_ERR,
1446 LOC + QString(
"Bad image: (%1 -> %2)").arg(srcPath, destPath));
1450 LOG(VB_FILE, LOG_DEBUG,
1451 LOC + QString(
"Db Renaming %1 -> %2").arg(old, im->m_filePath));
1453 DBFS::UpdateDbImage(*im);
1457 m_thumbGen->MoveThumbnail(im);
1459 HandleScanRequest(
"START");
1461 RESULT_OK(QString(
"Moved %1 from %2 -> %3").arg(ids, srcPath, destPath))
1472 template <
class DBFS>
1475 if (!DBFS::SetHidden(hide, ids))
1476 RESULT_ERR(
"Hide failed", QString(
"Db hide failed for %1").arg(ids))
1479 QStringList mesg = QStringList(
"") << ids;
1480 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1482 RESULT_OK(QString(
"Images %1 now %2hidden").arg(ids, hide ?
"" :
"un"))
1494 template <
class DBFS>
1496 const QString &ids)
const
1499 RESULT_ERR(
"Transform failed", QString(
"Bad transform %1").arg(transform))
1503 if (DBFS::GetImages(ids, files, dirs) < 1 || files.isEmpty())
1504 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1507 for (
const auto& im : qAsConst(files))
1509 int old = im->m_orientation;
1513 if (DBFS::SetOrientation(im->m_id, im->m_orientation))
1515 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Transformed %1 from %2 to %3")
1516 .arg(im->m_filePath).arg(old).arg(im->m_orientation));
1521 QStringList mesg(
"");
1524 mesg << m_thumbGen->DeleteThumbs(files);
1527 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1529 return QStringList(
"OK");
1541 template <
class DBFS>
1544 const QStringList &relPaths)
const
1549 if (DBFS::GetImages(destId, files, dirs) != 1 || dirs.isEmpty())
1551 QString(
"Image %1 not in Db").arg(destId))
1554 QString destPath = DBFS::GetAbsFilePath(dirs[0]);
1555 if (destPath.isEmpty())
1557 QString(
"Dest dir %1 not found").arg(dirs[0]->m_filePath))
1559 QDir destDir(destPath);
1560 bool succeeded =
false;
1561 for (
const auto& relPath : qAsConst(relPaths))
1564 if (relPath.isEmpty() || relPath.contains(
"..") || relPath.startsWith(QChar(
'/')))
1567 QString newPath = DBFS::ConstructPath(destDir.absolutePath(), relPath);
1568 if (!destDir.mkpath(relPath))
1570 LOG(VB_GENERAL, LOG_ERR,
1571 LOC + QString(
"Failed to create dir %1").arg(newPath));
1574 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Dir %1 created").arg(newPath));
1580 RESULT_ERR(
"Invalid Name", QString(
"Invalid name %1")
1581 .arg(relPaths.join(
",")))
1585 HandleScanRequest(
"START");
1587 return QStringList(
"OK");
1597 template <
class DBFS>
1600 if (!DBFS::SetCover(dir, cover))
1602 QString(
"Failed to set %1 to cover %2").arg(dir).arg(cover))
1605 QStringList mesg = QStringList(
"") << QString::number(dir);
1606 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1608 RESULT_OK(QString(
"Cover of %1 is now %2").arg(dir).arg(cover));
1620 template <
class DBFS>
1627 HandleScanRequest(
"START");
1629 RESULT_OK(QString(
"Using exclusions '%1'").arg(exclusions))
1640 template <
class DBFS>
1645 RESULT_ERR(
"Missing Scanner",
"Missing Scanner");
1647 if (command ==
"START")
1650 if (m_scanner->IsScanning())
1653 m_scanner->ChangeState(
true);
1656 else if (command ==
"STOP")
1659 if (!m_scanner->IsScanning())
1660 RESULT_ERR(
"Scanner not running",
"Scanner not running");
1662 m_scanner->ChangeState(
false);
1665 else if (command ==
"QUERY")
1667 return QStringList(
"OK") << m_scanner->GetProgress();
1669 else if (command.startsWith(QString(
"DEVICE")))
1671 m_scanner->EnqueueClear(devId, command);
1672 RESULT_OK(QString(
"Clearing device %1 %2").arg(command).arg(devId))
1674 RESULT_ERR(
"Unknown command", QString(
"Unknown command %1").arg(command));
1685 template <
class DBFS>
1687 (
const QStringList &message)
const
1689 if (message.size() != 2)
1691 QString(
"Bad request: %1").arg(message.join(
"|")))
1693 int priority = message.at(0).toInt()
1699 DBFS::GetImages(message.at(1), files, dirs);
1701 for (
const auto& im : qAsConst(files))
1703 m_thumbGen->CreateThumbnail(im, priority,
true);
1705 return QStringList(
"OK");
1718 template <
class DBFS>
1721 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1722 QMutableVectorIterator<ImagePtr> it(images);
1724 QMutableListIterator<ImagePtr> it(images);
1727 while (it.hasPrevious())
1732 QString absFilename = DBFS::GetAbsFilePath(im);
1734 bool success = !absFilename.isEmpty()
1735 && (im->IsFile() ? QFile::remove(absFilename)
1736 : QDir::root().rmdir(absFilename));
1738 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Deleted %1").arg(absFilename));
1741 LOG(VB_GENERAL, LOG_ERR,
LOC +
1742 QString(
"Can't delete %1").arg(absFilename));
1776 "CASE WHEN type <= %1 THEN %4, "
1777 "CASE WHEN type > %1 THEN %5 ")
1829 count += ImageHandler::GetDirectory(
id, parent, files, dirs,
m_refineClause);
1857 if (!lists.second.isEmpty())
1860 return ImageHandler::GetImages(lists.first, files, dirs,
m_refineClause);
1879 count += ImageHandler::GetChildren(QString::number(
id), files, dirs,
1897 if (!lists.second.isEmpty())
1900 ImageHandler::GetDescendants(lists.first, files, dirs);
1928 int &videos,
int &sizeKb)
const
1935 ImageHandler::GetDescendantCount(
id,
true, dirs, pics, videos, sizeKb);
1941 dirs, pics, videos, sizeKb);
1946 ImageHandler::GetDescendantCount(
id,
false, dirs, pics, videos, sizeKb);
2002 if (!lists.second.isEmpty())
2004 LOG(VB_FILE, LOG_DEBUG,
LOC +
2005 QString(
"Sending CREATE_THUMBNAILS %1 (forFolder %2)")
2006 .arg(lists.second).arg(forFolder));
2008 QStringList message;
2009 message << QString::number(static_cast<int>(forFolder)) << lists.second;
2013 if (!lists.first.isEmpty())
2015 LOG(VB_FILE, LOG_DEBUG,
LOC +
2016 QString(
"Creating local thumbnails %1 (forFolder %2)")
2017 .arg(lists.first).arg(forFolder));
2019 QStringList message;
2020 message << QString::number(static_cast<int>(forFolder)) << lists.first;
2034 QStringList command;
2035 command << (start ?
"START" :
"STOP");
2039 command.push_front(
"IMAGE_SCAN");
2041 return ok ?
"" : command[1];
2046 return "Couldn't create database";
2049 return err[0] ==
"OK" ?
"" : err[1];
2060 QStringList strList;
2061 strList <<
"IMAGE_SCAN" <<
"QUERY";
2065 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Scan query failed : %1")
2066 .arg(strList.join(
",")));
2082 QString result =
"";
2084 if (!lists.second.isEmpty())
2086 QStringList message;
2087 message <<
"IMAGE_HIDE" << QString::number(
static_cast<int>(hidden)) << lists.second;
2090 result = message[1];
2093 if (!lists.first.isEmpty())
2095 QStringList err =
HandleHide(hidden, lists.first);
2114 QString result =
"";
2116 if (!lists.second.isEmpty())
2118 QStringList message;
2119 message <<
"IMAGE_TRANSFORM" << QString::number(transform) << lists.second;
2122 result = message[1];
2125 if (!lists.first.isEmpty())
2145 QStringList message;
2146 message <<
"IMAGE_COVER" << QString::number(parent) << QString::number(cover);
2149 return ok ?
"" : message[1];
2153 return err[0] ==
"OK" ?
"" : err[1];
2173 QStringList message(
"IMAGE_SCAN");
2174 message <<
"DEVICE CLEAR ALL";
2187 QStringList message(
"IMAGE_IGNORE");
2188 message << excludes;
2190 return ok ?
"" : message[1];
2203 QString destId = QString::number(parent);
2207 QStringList message(
"IMAGE_CREATE_DIRS");
2208 message << destId << QString::number(static_cast<int>(rescan)) << names;
2210 return ok ?
"" : message[1];
2212 QStringList err =
HandleDirs(destId, rescan, names);
2213 return (err[0] ==
"OK") ?
"" : err[1];
2227 QStringList message(
"IMAGE_RENAME");
2228 message << QString::number(im->m_id) << name;
2230 return ok ?
"" : message[1];
2232 QStringList err =
HandleRename(QString::number(im->m_id), name);
2233 return (err[0] ==
"OK") ?
"" : err[1];
2245 if (images.isEmpty())
2249 const QString seperator(
"...");
2250 QStringList imageDefs(seperator);
2252 for (
const auto& im : qAsConst(images))
2258 aDef << QString::number(im->m_id)
2259 << QString::number(im->m_type)
2261 << QString::number(
static_cast<int>(im->m_isHidden))
2262 << QString::number(im->m_orientation)
2263 << QString::number(im->m_userThumbnail);
2265 imageDefs << aDef.join(seperator);
2272 return (err[0] ==
"OK") ?
"" : err[1];
2274 imageDefs.prepend(
"IMAGE_COPY");
2276 return ok ?
"" : imageDefs[1];
2288 const QString &srcPath)
2291 for (
const auto& im : qAsConst(images))
2292 idents << QString::number(im->m_id);
2295 if (destDir->IsLocal())
2297 QStringList err =
HandleDbMove(idents.join(
","), srcPath,
2298 destDir->m_filePath);
2299 return (err[0] ==
"OK") ?
"" : err[1];
2302 QStringList message(
"IMAGE_MOVE");
2303 message << idents.join(
",") << srcPath << destDir->m_filePath;
2305 return ok ?
"" : message[1];
2318 QString result =
"";
2319 if (!lists.second.isEmpty())
2321 QStringList message(
"IMAGE_DELETE");
2322 message << lists.second;
2326 result = message[1];
2328 if (!lists.first.isEmpty())
2349 std::chrono::seconds secs = 0s;
2352 if (im->m_date > 0s)
2358 secs = im->m_modTime;
2375 std::chrono::seconds secs(im->m_date > 0s ? im->m_date : im->m_modTime);
2388 return tr(
"Gallery");
2390 return tr(
"Photographs");
2420 return dev + path.replace(
"/",
" > ");
2427 ?
"DEVICE CLOSE ALL"
2428 : eject ?
"DEVICE EJECT" :
"DEVICE REMOVE";
2444 QList<MythMediaDevice*> devices
2447 for (
auto *dev : qAsConst(devices))
2450 OpenDevice(dev->getDeviceModel(), dev->getMountPath(), dev);
2459 for (
int devId : qAsConst(absentees))
2465 LOG(VB_GENERAL, LOG_ERR,
LOC + err);
2479 if (!event || !monitor)
2489 LOG(VB_FILE, LOG_DEBUG,
LOC +
2490 QString(
"Media event for %1 (%2) at %3, type %4, status %5 (was %6)")
2496 LOG(VB_FILE, LOG_DEBUG,
LOC +
2497 QString(
"Ignoring event - wrong type %1").arg(
type));
2523 auto *
tmp =
new QTemporaryDir(QDir::tempPath() %
"/" %
IMPORTDIR %
"-XXXXXX");
2524 if (!
tmp->isValid())
2530 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.