Go to the documentation of this file.
3 #include <QImageReader>
13 #define LOC QString("ImageManager: ")
14 #define DBLOC QString("ImageDb(%1): ").arg(m_table)
17 #define STORAGE_GROUP_MOUNT ""
19 #define DB_TABLE "gallery_files"
21 #define RESULT_ERR(ERR, MESG) \
22 { LOG(VB_GENERAL, LOG_ERR, LOC + (MESG)); \
23 return QStringList("ERROR") << (ERR); }
25 #define RESULT_OK(MESG) \
26 { LOG(VB_FILE, LOG_DEBUG, LOC + (MESG)); \
27 return QStringList("OK"); }
29 #define IMPORTDIR "Import"
71 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Ejecting '%1' at '%2'")
76 LOG(VB_MEDIA, LOG_DEBUG,
LOC + QString(
"Unlocked '%1'").arg(
m_name));
90 QDir(path).removeRecursively();
99 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Removing thumbnails in %1").arg(dir));
101 QDir::root().rmpath(dir);
163 QString state(
"Known");
179 LOG(VB_GENERAL, LOG_INFO,
LOC +
180 QString(
"%1 device %2 mounted at '%3' [Id %4]")
181 .arg(state, name, mount).arg(
id));
197 if (
action ==
"DEVICE CLOSE ALL")
204 else if (
action ==
"DEVICE CLEAR ALL")
207 for (
const auto *dev : qAsConst(
m_devices)) {
210 clear << dev->m_mount;
221 if (
action ==
"DEVICE EJECT")
238 DeviceMap::const_iterator it =
m_devices.constBegin();
241 if (it.value()->m_mount == mount)
253 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
260 #elif QT_VERSION < QT_VERSION_CHECK(5,15,0)
261 for (
auto it =
m_devices.constKeyValueBegin();
262 it !=
m_devices.constKeyValueEnd(); ++it)
265 paths.insert((*it).first, (*it).second->m_mount);
268 for (
auto it =
m_devices.constKeyValueBegin();
269 it !=
m_devices.constKeyValueEnd(); ++it)
272 paths.insert(it->first, it->second->m_mount);
283 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
290 #elif QT_VERSION < QT_VERSION_CHECK(5,15,0)
291 for (
auto it =
m_devices.constKeyValueBegin();
292 it !=
m_devices.constKeyValueEnd(); it++)
294 Device *dev = (*it).second;
296 absent << (*it).first;
299 for (
auto it =
m_devices.constKeyValueBegin();
300 it !=
m_devices.constKeyValueEnd(); it++)
315 m_imageFileExt(SupportedImages()),
316 m_videoFileExt(SupportedVideos())
321 for (
const auto& ext : qAsConst(allExt))
326 m_dirFilter.setFilter(QDir::AllDirs | QDir::Files | QDir::Readable |
327 QDir::NoDotAndDotDot | QDir::NoSymLinks);
331 m_dirFilter.setSorting(QDir::DirsLast | QDir::Time | QDir::Reversed);
342 QList<QByteArray> supported = QImageReader::supportedImageFormats();
343 for (
const auto& ext : qAsConst(supported))
359 for (
const auto & fa : faList)
361 if (!fa.use_default && fa.playcommand ==
"Internal")
362 formats << QString(fa.extension);
377 int devId,
const QString & )
const
381 im->m_parentId = parentId;
382 im->m_device = devId;
383 im->m_filePath = fi.absoluteFilePath();
388 auto secs = im->m_filePath.contains(
IMPORTDIR)
389 ? fi.lastModified().toSecsSinceEpoch()
390 : QDateTime::currentSecsSinceEpoch();
391 im->m_date = std::chrono::seconds(secs);
392 im->m_modTime = im->m_date;
397 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
405 im->m_extension = fi.suffix().toLower();
415 im->m_size = fi.size();
427 const QStringList &extra)
443 int ,
const QString &base)
const
448 im->m_parentId = parentId;
455 im->m_date = std::chrono::seconds(QDateTime::currentSecsSinceEpoch());
456 im->m_modTime = im->m_date;
461 im->m_filePath = fi.absoluteFilePath().mid(base.size() + 1);
462 im->m_modTime = std::chrono::seconds(fi.lastModified().toSecsSinceEpoch());
470 im->m_extension = fi.suffix().toLower();
480 im->m_size = fi.size();
492 const QStringList &extra)
507 for (
const auto& path : qAsConst(
paths))
508 map.insert(i++, path);
523 return im->m_filePath.startsWith(
"/") ? im->m_filePath
530 "file_id, filename, name, dir_id, type, modtime, size, " \
531 "extension, date, hidden, orientation, angle, path, zoom"
547 im->m_filePath = query.
value(1).toString();
548 im->m_baseName = query.
value(2).toString();
549 im->m_parentId = FS::ImageId(query.
value(3).toInt());
550 im->m_type = query.
value(4).toInt();
551 im->m_modTime = std::chrono::seconds(query.
value(5).toInt());
552 im->m_size = query.
value(6).toInt();
553 im->m_extension = query.
value(7).toString();
554 im->m_date = std::chrono::seconds(query.
value(8).toUInt());
555 im->m_isHidden = query.
value(9).toBool();
556 im->m_orientation = query.
value(10).toInt();
557 im->m_userThumbnail = FS::ImageId(query.
value(11).toInt());
558 im->m_comment = query.
value(12).toString();
559 im->m_device = query.
value(13).toInt();
560 im->m_url = FS::MakeFileUrl(im->m_filePath);
565 QString thumbPath(FS::ThumbPath(*im));
566 QString devPath(FS::ThumbDir(im->m_device));
567 QString url(FS::MakeThumbUrl(devPath, thumbPath));
569 im->m_thumbPath = FS::GetAbsThumbPath(devPath, thumbPath);
570 im->m_thumbNails.append(qMakePair(im->m_id, url));
586 const QString &refine)
const
591 QString select = QString(
"file_id IN (%1) %2").arg(FS::DbIds(ids), refine);
592 return ReadImages(dirs, files, select);
606 const QString &refine)
const
608 QString select = QString(
"dir_id IN (%1) %2").arg(FS::DbIds(ids), refine);
609 return ReadImages(dirs, files, select);
625 const QString &refine)
const
629 "WHERE (dir_id = :ID1 OR file_id = :ID2) "
630 "%2;").arg(m_table, refine));
633 int dbId = FS::DbId(
id);
648 else if (im->m_id ==
id)
671 if (ReadImages(dirs, files, QString(
"file_id IN (%1)").arg(FS::DbIds(ids))) < 0)
677 ", LENGTH(filename) - LENGTH(REPLACE(filename, '/', ''))"
679 "FROM %1 WHERE filename LIKE :PREFIX "
680 "ORDER BY depth;").arg(m_table);
682 for (
const auto& im1 : qAsConst(dirs))
685 query.
bindValue(
":PREFIX", im1->m_filePath +
"/%");
696 if (im2->IsDirectory())
718 if (GetChildren(QString::number(
id), files, dirs, refine) < 0)
721 for (
const auto& im : qAsConst(dirs))
722 if (!GetImageTree(im->m_id, files, refine))
748 if (im->IsDirectory())
749 dirs.insert(im->m_filePath, im);
751 files.insert(im->m_filePath, im);
767 if (
action ==
"DEVICE CLOSE ALL")
773 if (
action ==
"DEVICE CLEAR ALL")
776 query.
prepare(QString(
"TRUNCATE TABLE %1;").arg(m_table));
784 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE zoom = :FS;").arg(m_table));
805 if (checkForDuplicate)
807 query.
prepare(QString(
"SELECT file_id FROM %1 WHERE filename = :NAME;")
818 if (query.
size() > 0)
820 LOG(VB_FILE, LOG_DEBUG, QString(
"Image: %1 already exists in Db")
822 return query.
value(0).toInt();
827 ":FILEPATH, :NAME, :PARENT, :TYPE, :MODTIME, "
828 ":SIZE, :EXTENSION, :DATE, :HIDDEN, :ORIENT, "
829 ":COVER, :COMMENT, :FS);").arg(m_table));
864 "filename = :FILEPATH, name = :NAME, "
865 "dir_id = :PARENT, type = :TYPE, "
866 "modtime = :MODTIME, size = :SIZE, "
867 "extension = :EXTENSION, date = :DATE, zoom = :FS, "
868 "hidden = :HIDDEN, orientation = :ORIENT, "
869 "angle = :COVER, path = :COMMENT "
870 "WHERE file_id = :ID;").arg(m_table));
905 if (!imList.isEmpty())
907 for (
const auto& im : qAsConst(imList))
908 ids << QString::number(FS::DbId(im->m_id));
910 QString idents = ids.join(
",");
912 query.
prepare(QString(
"DELETE IGNORE FROM %1 WHERE file_id IN (%2);")
913 .arg(m_table, idents));
939 query.
prepare(QString(
"UPDATE %1 SET "
941 "WHERE file_id IN (%2);").arg(m_table, FS::DbIds(ids)));
942 query.
bindValue(
":HIDDEN", hide ? 1 : 0);
962 query.
prepare(QString(
"UPDATE %1 SET "
964 "WHERE file_id = :DIR").arg(m_table));
986 query.
prepare(QString(
"UPDATE %1 SET ").arg(m_table) +
987 "orientation = :ORIENTATION "
988 "WHERE file_id = :ID");
989 query.
bindValue(
":ORIENTATION", orientation);
1009 const QString &selector)
const
1013 .arg(m_table, selector));
1020 while (query.
next())
1029 return query.
size();
1044 int &pics,
int &videos,
int &sizeKb)
const
1046 QString whereClause;
1049 whereClause =
"WHERE filename LIKE "
1050 "( SELECT CONCAT(filename, '/%') "
1051 " FROM %2 WHERE file_id = :ID);";
1055 query.
prepare(QString(
"SELECT SUM(type <= :FLDR) AS Fldr, "
1056 " SUM(type = :PIC) AS Pics, "
1057 " SUM(type = :VID) AS Vids, "
1058 " SUM(size / 1024) "
1059 "FROM %2 %1;").arg(whereClause, m_table));
1071 else if (query.
next())
1073 dirs += query.
value(0).toInt();
1074 pics += query.
value(1).toInt();
1075 videos += query.
value(2).toInt();
1076 sizeKb += query.
value(3).toInt();
1131 query.
prepare(QString(
"ALTER TABLE %1 ENGINE = MEMORY;").arg(
m_table));
1135 LOG(VB_FILE, LOG_DEBUG, QString(
"Created Db table %1").arg(
m_table));
1160 QString orientation;
1176 tags.prepend(QString::number(
m_im->m_id));
1207 template <
class DBFS>
1213 if (DBFS::GetImages(
id, files, dirs) != 1)
1214 RESULT_ERR(
"Image not found", QString(
"Unknown image %1").arg(
id))
1216 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1218 QString absPath = DBFS::GetAbsFilePath(im);
1219 if (absPath.isEmpty())
1221 QString(
"File %1 not found").arg(im->m_filePath))
1227 RESULT_OK(QString(
"Fetching metadata for %1").arg(
id))
1238 template <
class DBFS>
1240 const QString &newBase)
const
1243 if (newBase.isEmpty() || newBase.contains(
"/") || newBase.contains(
"\\"))
1244 RESULT_ERR(
"Invalid name", QString(
"Invalid name %1").arg(newBase))
1249 if (DBFS::GetImages(
id, files, dirs) != 1)
1250 RESULT_ERR(
"Image not found", QString(
"Image %1 not in Db").arg(
id))
1252 ImagePtr im = files.isEmpty() ? dirs[0] : files[0];
1255 QString oldPath = DBFS::GetAbsFilePath(im);
1256 if (oldPath.isEmpty())
1258 QString(
"File %1 not found").arg(im->m_filePath))
1261 QFileInfo oldFi = QFileInfo(oldPath);
1262 QString newName = im->IsDirectory()
1263 ? newBase : QString(
"%1.%2").arg(newBase, oldFi.suffix());
1265 im->m_filePath = DBFS::ConstructPath(DBFS::PathOf(im->m_filePath), newName);
1270 QString existPath = DBFS::GetAbsFilePath(im);
1271 if (!existPath.isEmpty())
1273 QString(
"Renaming %1 to %2 will create a duplicate of %3")
1274 .arg(oldPath, im->m_filePath, existPath))
1278 QString newPath = oldFi.dir().absoluteFilePath(newName);
1279 if (!QFile::rename(oldPath, newPath))
1281 QString(
"Rename of %1 -> %2 failed").arg(oldPath, newPath))
1283 if (im->IsDirectory())
1286 HandleScanRequest(
"START");
1291 DBFS::UpdateDbImage(*im);
1294 QStringList mesg(
"");
1295 mesg << QString::number(im->m_id);
1298 m_thumbGen->MoveThumbnail(im);
1301 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1303 RESULT_OK(QString(
"Renamed %1 -> %2").arg(oldPath, newPath))
1314 template <
class DBFS>
1321 DBFS::GetDescendants(ids, files, dirs);
1329 if (files.isEmpty() && dirs.isEmpty())
1330 RESULT_ERR(
"Delete failed", QString(
"Delete of %1 failed").arg(ids))
1336 QStringList mesg(m_thumbGen->DeleteThumbs(files));
1339 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1341 return QStringList(
"OK");
1357 template <
class DBFS>
1364 const QString separator = defs.takeFirst();
1369 QHash<QString, int> idMap;
1374 for (
const auto& def : qAsConst(defs))
1376 QStringList aDef = def.split(separator);
1379 if (aDef.size() != 6)
1382 LOG(VB_GENERAL, LOG_ERR,
1383 LOC + QString(
"Bad definition: (%1)").arg(def));
1387 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Creating %1").arg(aDef.join(
",")));
1389 im.
m_type = aDef[1].toInt();
1396 int newId = DBFS::InsertDbImage(im,
true);
1399 idMap.insert(aDef[0], newId);
1401 HandleScanRequest(
"START");
1416 template <
class DBFS>
1418 const QString &srcPath,
1419 QString destPath)
const
1422 if (destPath.contains(
".."))
1423 RESULT_ERR(
"Invalid path", QString(
"Invalid path %1").arg(destPath))
1429 bool ok = DBFS::GetDescendants(ids, files, dirs);
1430 images << dirs << files;
1432 if (!ok || images.isEmpty())
1433 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1435 if (!destPath.isEmpty() && !destPath.endsWith(QChar(
'/')))
1436 destPath.append(
"/");
1439 for (
const auto& im : qAsConst(images))
1441 QString old = im->m_filePath;
1443 if (srcPath.isEmpty())
1446 im->m_filePath.prepend(destPath);
1448 else if (im->m_filePath.startsWith(srcPath))
1451 im->m_filePath.replace(srcPath, destPath);
1456 LOG(VB_GENERAL, LOG_ERR,
1457 LOC + QString(
"Bad image: (%1 -> %2)").arg(srcPath, destPath));
1461 LOG(VB_FILE, LOG_DEBUG,
1462 LOC + QString(
"Db Renaming %1 -> %2").arg(old, im->m_filePath));
1464 DBFS::UpdateDbImage(*im);
1468 m_thumbGen->MoveThumbnail(im);
1470 HandleScanRequest(
"START");
1472 RESULT_OK(QString(
"Moved %1 from %2 -> %3").arg(ids, srcPath, destPath))
1483 template <
class DBFS>
1486 if (!DBFS::SetHidden(hide, ids))
1487 RESULT_ERR(
"Hide failed", QString(
"Db hide failed for %1").arg(ids))
1490 QStringList mesg = QStringList(
"") << ids;
1491 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1493 RESULT_OK(QString(
"Images %1 now %2hidden").arg(ids, hide ?
"" :
"un"))
1505 template <
class DBFS>
1507 const QString &ids)
const
1510 RESULT_ERR(
"Transform failed", QString(
"Bad transform %1").arg(transform))
1514 if (DBFS::GetImages(ids, files, dirs) < 1 || files.isEmpty())
1515 RESULT_ERR(
"Image not found", QString(
"Images %1 not in Db").arg(ids))
1518 for (
const auto& im : qAsConst(files))
1520 int old = im->m_orientation;
1524 if (DBFS::SetOrientation(im->m_id, im->m_orientation))
1526 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Transformed %1 from %2 to %3")
1527 .arg(im->m_filePath).arg(old).arg(im->m_orientation));
1532 QStringList mesg(
"");
1535 mesg << m_thumbGen->DeleteThumbs(files);
1538 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1540 return QStringList(
"OK");
1552 template <
class DBFS>
1555 const QStringList &relPaths)
const
1560 if (DBFS::GetImages(destId, files, dirs) != 1 || dirs.isEmpty())
1562 QString(
"Image %1 not in Db").arg(destId))
1565 QString destPath = DBFS::GetAbsFilePath(dirs[0]);
1566 if (destPath.isEmpty())
1568 QString(
"Dest dir %1 not found").arg(dirs[0]->m_filePath))
1570 QDir destDir(destPath);
1571 bool succeeded =
false;
1572 for (
const auto& relPath : qAsConst(relPaths))
1575 if (relPath.isEmpty() || relPath.contains(
"..") || relPath.startsWith(QChar(
'/')))
1578 QString newPath = DBFS::ConstructPath(destDir.absolutePath(), relPath);
1579 if (!destDir.mkpath(relPath))
1581 LOG(VB_GENERAL, LOG_ERR,
1582 LOC + QString(
"Failed to create dir %1").arg(newPath));
1585 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Dir %1 created").arg(newPath));
1591 RESULT_ERR(
"Invalid Name", QString(
"Invalid name %1")
1592 .arg(relPaths.join(
",")))
1596 HandleScanRequest(
"START");
1598 return QStringList(
"OK");
1608 template <
class DBFS>
1611 if (!DBFS::SetCover(dir, cover))
1613 QString(
"Failed to set %1 to cover %2").arg(dir).arg(cover))
1616 QStringList mesg = QStringList(
"") << QString::number(dir);
1617 DBFS::Notify(
"IMAGE_DB_CHANGED", mesg);
1619 RESULT_OK(QString(
"Cover of %1 is now %2").arg(dir).arg(cover));
1631 template <
class DBFS>
1638 HandleScanRequest(
"START");
1640 RESULT_OK(QString(
"Using exclusions '%1'").arg(exclusions))
1651 template <
class DBFS>
1656 RESULT_ERR(
"Missing Scanner",
"Missing Scanner");
1658 if (command ==
"START")
1661 if (m_scanner->IsScanning())
1664 m_scanner->ChangeState(
true);
1667 else if (command ==
"STOP")
1670 if (!m_scanner->IsScanning())
1671 RESULT_ERR(
"Scanner not running",
"Scanner not running");
1673 m_scanner->ChangeState(
false);
1676 else if (command ==
"QUERY")
1678 return QStringList(
"OK") << m_scanner->GetProgress();
1680 else if (command.startsWith(QString(
"DEVICE")))
1682 m_scanner->EnqueueClear(devId, command);
1683 RESULT_OK(QString(
"Clearing device %1 %2").arg(command).arg(devId))
1685 RESULT_ERR(
"Unknown command", QString(
"Unknown command %1").arg(command));
1696 template <
class DBFS>
1698 (
const QStringList &message)
const
1700 if (message.size() != 2)
1702 QString(
"Bad request: %1").arg(message.join(
"|")))
1704 int priority = message.at(0).toInt()
1710 DBFS::GetImages(message.at(1), files, dirs);
1712 for (
const auto& im : qAsConst(files))
1714 m_thumbGen->CreateThumbnail(im, priority,
true);
1716 return QStringList(
"OK");
1729 template <
class DBFS>
1732 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1733 QMutableVectorIterator<ImagePtr> it(images);
1735 QMutableListIterator<ImagePtr> it(images);
1738 while (it.hasPrevious())
1743 QString absFilename = DBFS::GetAbsFilePath(im);
1745 bool success = !absFilename.isEmpty()
1746 && (im->IsFile() ? QFile::remove(absFilename)
1747 : QDir::root().rmdir(absFilename));
1749 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"Deleted %1").arg(absFilename));
1752 LOG(VB_GENERAL, LOG_ERR,
LOC +
1753 QString(
"Can't delete %1").arg(absFilename));
1787 "CASE WHEN type <= %1 THEN %4, "
1788 "CASE WHEN type > %1 THEN %5 ")
1840 count += ImageHandler::GetDirectory(
id, parent, files, dirs,
m_refineClause);
1868 if (!lists.second.isEmpty())
1871 return ImageHandler::GetImages(lists.first, files, dirs,
m_refineClause);
1890 count += ImageHandler::GetChildren(QString::number(
id), files, dirs,
1908 if (!lists.second.isEmpty())
1911 ImageHandler::GetDescendants(lists.first, files, dirs);
1939 int &videos,
int &sizeKb)
const
1946 ImageHandler::GetDescendantCount(
id,
true, dirs, pics, videos, sizeKb);
1952 dirs, pics, videos, sizeKb);
1957 ImageHandler::GetDescendantCount(
id,
false, dirs, pics, videos, sizeKb);
2013 if (!lists.second.isEmpty())
2015 LOG(VB_FILE, LOG_DEBUG,
LOC +
2016 QString(
"Sending CREATE_THUMBNAILS %1 (forFolder %2)")
2017 .arg(lists.second).arg(forFolder));
2019 QStringList message;
2020 message << QString::number(static_cast<int>(forFolder)) << lists.second;
2024 if (!lists.first.isEmpty())
2026 LOG(VB_FILE, LOG_DEBUG,
LOC +
2027 QString(
"Creating local thumbnails %1 (forFolder %2)")
2028 .arg(lists.first).arg(forFolder));
2030 QStringList message;
2031 message << QString::number(static_cast<int>(forFolder)) << lists.first;
2045 QStringList command;
2046 command << (start ?
"START" :
"STOP");
2050 command.push_front(
"IMAGE_SCAN");
2052 return ok ?
"" : command[1];
2057 return "Couldn't create database";
2060 return err[0] ==
"OK" ?
"" : err[1];
2071 QStringList strList;
2072 strList <<
"IMAGE_SCAN" <<
"QUERY";
2076 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Scan query failed : %1")
2077 .arg(strList.join(
",")));
2093 QString result =
"";
2095 if (!lists.second.isEmpty())
2097 QStringList message;
2098 message <<
"IMAGE_HIDE" << QString::number(
static_cast<int>(hidden)) << lists.second;
2101 result = message[1];
2104 if (!lists.first.isEmpty())
2106 QStringList err =
HandleHide(hidden, lists.first);
2125 QString result =
"";
2127 if (!lists.second.isEmpty())
2129 QStringList message;
2130 message <<
"IMAGE_TRANSFORM" << QString::number(transform) << lists.second;
2133 result = message[1];
2136 if (!lists.first.isEmpty())
2156 QStringList message;
2157 message <<
"IMAGE_COVER" << QString::number(parent) << QString::number(cover);
2160 return ok ?
"" : message[1];
2164 return err[0] ==
"OK" ?
"" : err[1];
2184 QStringList message(
"IMAGE_SCAN");
2185 message <<
"DEVICE CLEAR ALL";
2198 QStringList message(
"IMAGE_IGNORE");
2199 message << excludes;
2201 return ok ?
"" : message[1];
2214 QString destId = QString::number(parent);
2218 QStringList message(
"IMAGE_CREATE_DIRS");
2219 message << destId << QString::number(static_cast<int>(rescan)) << names;
2221 return ok ?
"" : message[1];
2223 QStringList err =
HandleDirs(destId, rescan, names);
2224 return (err[0] ==
"OK") ?
"" : err[1];
2238 QStringList message(
"IMAGE_RENAME");
2239 message << QString::number(im->m_id) << name;
2241 return ok ?
"" : message[1];
2243 QStringList err =
HandleRename(QString::number(im->m_id), name);
2244 return (err[0] ==
"OK") ?
"" : err[1];
2256 if (images.isEmpty())
2260 const QString seperator(
"...");
2261 QStringList imageDefs(seperator);
2263 for (
const auto& im : qAsConst(images))
2269 aDef << QString::number(im->m_id)
2270 << QString::number(im->m_type)
2272 << QString::number(
static_cast<int>(im->m_isHidden))
2273 << QString::number(im->m_orientation)
2274 << QString::number(im->m_userThumbnail);
2276 imageDefs << aDef.join(seperator);
2283 return (err[0] ==
"OK") ?
"" : err[1];
2285 imageDefs.prepend(
"IMAGE_COPY");
2287 return ok ?
"" : imageDefs[1];
2299 const QString &srcPath)
2302 for (
const auto& im : qAsConst(images))
2303 idents << QString::number(im->m_id);
2306 if (destDir->IsLocal())
2308 QStringList err =
HandleDbMove(idents.join(
","), srcPath,
2309 destDir->m_filePath);
2310 return (err[0] ==
"OK") ?
"" : err[1];
2313 QStringList message(
"IMAGE_MOVE");
2314 message << idents.join(
",") << srcPath << destDir->m_filePath;
2316 return ok ?
"" : message[1];
2329 QString result =
"";
2330 if (!lists.second.isEmpty())
2332 QStringList message(
"IMAGE_DELETE");
2333 message << lists.second;
2337 result = message[1];
2339 if (!lists.first.isEmpty())
2360 std::chrono::seconds secs = 0s;
2363 if (im->m_date > 0s)
2369 secs = im->m_modTime;
2386 std::chrono::seconds secs(im->m_date > 0s ? im->m_date : im->m_modTime);
2399 return tr(
"Gallery");
2401 return tr(
"Photographs");
2431 return dev + path.replace(
"/",
" > ");
2438 ?
"DEVICE CLOSE ALL"
2439 : eject ?
"DEVICE EJECT" :
"DEVICE REMOVE";
2455 QList<MythMediaDevice*> devices
2458 for (
auto *dev : qAsConst(devices))
2461 OpenDevice(dev->getDeviceModel(), dev->getMountPath(), dev);
2470 for (
int devId : qAsConst(absentees))
2476 LOG(VB_GENERAL, LOG_ERR,
LOC + err);
2490 if (!event || !monitor)
2500 LOG(VB_FILE, LOG_DEBUG,
LOC +
2501 QString(
"Media event for %1 (%2) at %3, type %4, status %5 (was %6)")
2507 LOG(VB_FILE, LOG_DEBUG,
LOC +
2508 QString(
"Ignoring event - wrong type %1").arg(
type));
2534 auto *
tmp =
new QTemporaryDir(QDir::tempPath() %
"/" IMPORTDIR "-XXXXXX");
2535 if (!
tmp->isValid())
2541 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.
#define IMAGE_STORAGE_GROUP
StorageGroup m_sg
Images storage group.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
const association_list & getList() const
#define STORAGE_GROUP_MOUNT
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.
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.
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.
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.
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.
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.
@ 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.
Encapsulates Exif orientation processing.
void start(QRunnable *runnable, const QString &debugName, int priority=0)
int m_size
Filesize (files only)
@ kVideoOnly
Hide pictures.
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.