12#define LOC QString("SG(%1): ").arg(m_groupname)
23 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"LiveTV")
25 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"DB Backups")
26 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Videos")
27 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Trailers")
28 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Coverart")
29 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Fanart")
30 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Screenshots")
31 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Banners")
32 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Photographs")
33 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Music")
34 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"MusicArt")
50 m_allowFallback(allowFallback)
54 if (qEnvironmentVariableIsSet(
"MYTHTV_NOSGFALLBACK"))
78 QDir qdir(it.value());
80 qdir.mkpath(it.value());
84 LOG(VB_GENERAL, LOG_ERR,
85 QString(
"SG() Error: Could not create builtin"
86 "Storage Group directory '%1' for '%2'")
87 .arg(it.value(), it.key()));
107 const bool allowFallback)
121 if (!testdir.exists())
124 if (testdir.exists())
126 m_dirlist.prepend(testdir.absolutePath());
134 LOG(VB_FILE, LOG_NOTICE,
LOC +
135 QString(
"Unable to find any directories for the local "
136 "storage group '%1' on '%2', trying directories on "
137 "all hosts!").arg(group,
hostname));
146 LOG(VB_FILE, LOG_NOTICE,
LOC +
147 QString(
"Unable to find storage group '%1', trying "
148 "'Default' group!").arg(group));
156 LOG(VB_FILE, LOG_NOTICE,
LOC +
157 QString(
"Unable to find any directories for the local "
158 "Default storage group on '%1', trying directories "
159 "in all Default groups!").arg(
hostname));
171 QString msg =
"Unable to find any Storage Group Directories. ";
175 msg += QString(
"Using old 'RecordFilePrefix' value of '%1'")
181 msg += QString(
"Using hardcoded default value of '%1'")
184 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
203 const QString &lbase,
204 bool recursive,
bool onlyDirs)
207 QString base = lbase;
213 if (base.split(
"/").size() > 20)
215 LOG(VB_GENERAL, LOG_ERR,
LOC +
"GetDirFileList(), 20 levels deep, "
216 "possible directory loop detected.");
226 d.entryList(QDir::Dirs|QDir::NoDotAndDotDot|QDir::Readable);
228 for (
const auto&
p : std::as_const(list))
230 LOG(VB_FILE, LOG_DEBUG,
LOC +
231 QString(
"GetDirFileList: Dir: %1/%2").arg(base,
p));
234 files.append(base +
p);
242 QStringList list =
d.entryList(QDir::Files|QDir::Readable);
243 for (
const auto&
p : std::as_const(list))
245 LOG(VB_FILE, LOG_DEBUG,
LOC +
246 QString(
"GetDirFileList: File: %1%2").arg(base,
p));
248 files.append(base +
p);
261 for (
const auto& dir : std::as_const(
m_dirlist))
277 for (
const auto& dir : std::as_const(
m_dirlist))
295 if (Path.isEmpty() || Path ==
"/")
297 for (
const auto& dir : std::as_const(
m_dirlist))
298 files << QString(
"sgdir::%1").arg(dir);
303 for (
const auto& dir : std::as_const(
m_dirlist))
305 if (Path.startsWith(dir))
308 relPath.replace(dir,
"");
309 if (relPath.startsWith(
"/"))
310 relPath.replace(0,1,
"");
315 LOG(VB_FILE, LOG_INFO,
LOC +
316 QString(
"GetFileInfoList: Reading '%1'").arg(Path));
325 d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
326 QFileInfoList list =
d.entryInfoList();
330 for (
const auto& entry : std::as_const(list))
332 if (entry.fileName() ==
"Thumbs.db")
338 tmp = QString(
"dir::%1::0").arg(entry.fileName());
341 tmp = QString(
"file::%1::%2::%3%4").arg(entry.fileName())
343 .arg(relPath, entry.fileName());
346 LOG(VB_FILE, LOG_DEBUG,
LOC +
347 QString(
"GetFileInfoList: (%1)").arg(
tmp));
356 LOG(VB_FILE, LOG_DEBUG,
LOC +
357 QString(
"FileExist: Testing for '%1'").arg(
filename));
363 for (
const auto & dir : std::as_const(
m_dirlist))
383 LOG(VB_FILE, LOG_DEBUG,
LOC +
384 QString(
"GetFileInfo: For '%1'") .arg(
filename));
387 bool searched =
false;
395 if ((searched && !
filename.isEmpty()) ||
401 if (fInfo.lastModified().isValid()) {
402 details << QString(
"%1").arg(fInfo.lastModified().toSecsSinceEpoch());
404 details << QString::number(UINT_MAX);
406 details << QString(
"%1").arg(fInfo.size());
425 LOG(VB_FILE, LOG_DEBUG,
426 QString(
"StorageGroup::GetRelativePathname(%1)").arg(
filename));
434 if (qurl.hasFragment())
435 result = qurl.path() +
"#" + qurl.fragment();
437 result = qurl.path();
439 if (result.startsWith(
"/"))
440 result.replace(0, 1,
"");
445 query.
prepare(
"SELECT DISTINCT dirname FROM storagegroup "
446 "ORDER BY dirname DESC;");
455 dirname = QString::fromUtf8(query.
value(0)
456 .toByteArray().constData());
460 result.replace(0, dirname.length(),
"");
461 if (result.startsWith(
"/"))
462 result.replace(0, 1,
"");
464 LOG(VB_FILE, LOG_DEBUG,
465 QString(
"StorageGroup::GetRelativePathname(%1) = '%2'")
472 query.
prepare(
"SELECT DISTINCT data FROM settings WHERE "
473 "value = 'VideoStartupDir';");
478 QString videostartupdir = query.
value(0).toString();
479 QStringList videodirs = videostartupdir.split(
':',
481 for (
const auto& directory : std::as_const(videodirs))
486 result.replace(0, directory.length(),
"");
487 if (result.startsWith(
"/"))
488 result.replace(0, 1,
"");
490 LOG(VB_FILE, LOG_DEBUG,
491 QString(
"StorageGroup::GetRelativePathname(%1) = '%2'")
505 const QString& directory = group;
509 result.replace(0, directory.length(),
"");
510 if (result.startsWith(
"/"))
511 result.replace(0, 1,
"");
513 LOG(VB_FILE, LOG_DEBUG,
514 QString(
"StorageGroup::GetRelativePathname(%1) = '%2'")
533 QStringList *dirlist)
541 QString sql =
"SELECT DISTINCT dirname "
542 "FROM storagegroup ";
544 if (!group.isEmpty())
546 sql.append(
"WHERE groupname = :GROUP");
548 sql.append(
" AND hostname = :HOSTNAME");
552 if (!group.isEmpty())
567 dirname = QString::fromUtf8(query.
value(0)
568 .toByteArray().constData());
569 dirname = dirname.trimmed();
570 if (dirname.endsWith(
"/"))
571 dirname.remove(dirname.length() - 1, 1);
573 if (
nullptr == dirlist)
575 (*dirlist) << dirname;
582 if (testdir.exists())
584 if (dirlist && !dirlist->contains(testdir.absolutePath()))
585 (*dirlist) << testdir.absolutePath();
595 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"FindFile: Searching for '%1'")
601 if (!recDir.isEmpty())
604 LOG(VB_FILE, LOG_INFO,
LOC +
605 QString(
"FindFile: Found '%1'") .arg(result));
609 LOG(VB_FILE, LOG_ERR,
LOC +
610 QString(
"FindFile: Unable to find '%1'!") .arg(
filename));
619 QFileInfo checkFile(
"");
625 LOG(VB_FILE, LOG_DEBUG,
LOC +
626 QString(
"FindFileDir: Checking '%1' for '%2'")
628 checkFile.setFile(testFile);
629 if (checkFile.exists() || checkFile.isSymLink())
640 checkFile.setFile(tmpFile);
641 if (checkFile.exists() || checkFile.isSymLink())
649 result = (tmpFile.isEmpty()) ? result : tmpFile;
656 result = (tmpFile.isEmpty()) ? result : tmpFile;
665 int64_t nextDirFree = 0;
667 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"FindNextDirMostFree: Starting"));
672 for (
const auto & dir : std::as_const(
m_dirlist))
676 LOG(VB_GENERAL, LOG_ERR,
LOC +
677 QString(
"FindNextDirMostFree: '%1' does not exist!").arg(dir));
682 LOG(VB_FILE, LOG_DEBUG,
LOC +
683 QString(
"FindNextDirMostFree: '%1' has %2 KiB free")
684 .arg(dir, QString::number(thisDirFree)));
686 if (thisDirFree > nextDirFree)
689 nextDirFree = thisDirFree;
693 if (nextDir.isEmpty())
695 LOG(VB_FILE, LOG_ERR,
LOC +
696 "FindNextDirMostFree: Unable to find any directories to use.");
700 LOG(VB_FILE, LOG_DEBUG,
LOC +
701 QString(
"FindNextDirMostFree: Using '%1'").arg(nextDir));
713 query.
prepare(
"SELECT groupname, dirname "
715 "WHERE hostname = :HOSTNAME;");
723 LOG(VB_FILE, LOG_DEBUG,
LOC +
724 "CheckAllStorageGroupDirs(): Checking All Storage Group directories");
734 dirname = QString::fromUtf8(query.
value(1)
735 .toByteArray().constData());
736 dirname = dirname.trimmed();
738 LOG(VB_FILE, LOG_DEBUG,
LOC +
739 QString(
"Checking directory '%1' in group '%2'.")
742 testDir.setPath(dirname);
743 if (!testDir.exists())
745 LOG(VB_FILE, LOG_WARNING,
LOC +
746 QString(
"Group '%1' references directory '%2' but this "
747 "directory does not exist. This directory "
748 "will not be used on this server.")
753 testFile.setFileName(dirname +
"/.test");
754 if (testFile.open(QIODevice::WriteOnly))
758 LOG(VB_GENERAL, LOG_ERR,
LOC +
759 QString(
"Group '%1' wants to use directory '%2', but "
760 "this directory is not writeable.")
773 QString sql =
"SELECT DISTINCT groupname "
775 "WHERE groupname NOT IN (";
777 sql.append(QString(
" '%1',").arg(group));
778 sql = sql.left(sql.length() - 1);
786 groups += query.
value(0).toString();
803 addHost =
" AND hostname = :HOSTNAME";
807 QString sql = QString(
"SELECT dirname,hostname "
809 "WHERE groupname = :GROUPNAME %1").arg(addHost);
812 query.
bindValue(
":GROUPNAME", groupname);
825 dirname = QString::fromUtf8(query.
value(0)
826 .toByteArray().constData());
845 const QString &host,
const QString &sgroup)
847 QString tmpGroup = sgroup;
848 QString groupKey = QString(
"%1:%2").arg(sgroup, host);
864 LOG(VB_FILE, LOG_DEBUG,
865 QString(
"GetGroupToUse(): "
866 "falling back to Videos Storage Group for host %1 "
867 "since it does not have a %2 Storage Group.")
int64_t getFreeSpace() const
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
bool isActive(void) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
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.
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
int GetBackendServerPort(void)
Returns the locally defined backend control port.
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
static void DBError(const QString &where, const MSqlQuery &query)
QStringList GetDirFileList(const QString &dir, const QString &base, bool recursive=false, bool onlyDirs=false)
QStringList GetDirList(void) const
QString FindFileDir(const QString &filename)
void Init(const QString &group="Default", const QString &hostname="", bool allowFallback=true)
Initilizes the groupname, hostname, and dirlist.
QString GetFirstDir(bool appendSlash=false) const
static QStringList getRecordingsGroups(void)
static QStringList getGroupDirs(const QString &groupname, const QString &host)
bool FileExists(const QString &filename)
static const char * kDefaultStorageDir
static bool FindDirs(const QString &group="Default", const QString &hostname="", QStringList *dirlist=nullptr)
Finds and and optionally initialize a directory list associated with a Storage Group.
static QMap< QString, QString > m_builtinGroups
static QString GetGroupToUse(const QString &host, const QString &sgroup)
static QHash< QString, QString > s_groupToUseCache
static void ClearGroupToUseCache(void)
QStringList GetFileInfo(const QString &filename)
QString FindFile(const QString &filename)
static const QStringList kSpecialGroups
static void CheckAllStorageGroupDirs(void)
StorageGroup(QString group="", QString hostname="", bool allowFallback=true)
StorageGroup constructor.
static QMutex m_staticInitLock
static void StaticInit(void)
static QMutex s_groupToUseLock
QStringList GetFileInfoList(const QString &Path)
static QString generate_file_url(const QString &storage_group, const QString &host, const QString &path)
QString FindNextDirMostFree(void)
QStringList GetFileList(const QString &Path, bool recursive=false)
static bool m_staticInitDone
static QString GetRelativePathname(const QString &filename)
Returns the relative pathname of a file by comparing the filename against all Storage Group directori...
static const iso6937table * d
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)