13 #define LOC QString("SG(%1): ").arg(m_groupname) 24 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"LiveTV")
26 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"DB Backups")
27 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Videos")
28 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Trailers")
29 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Coverart")
30 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Fanart")
31 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Screenshots")
32 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Banners")
33 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Photographs")
34 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"Music")
35 << QT_TRANSLATE_NOOP(
"(StorageGroups)",
"MusicArt")
50 m_groupname(group), m_hostname(
hostname), m_allowFallback(allowFallback)
54 if (getenv(
"MYTHTV_NOSGFALLBACK"))
78 QDir qdir(it.value());
80 qdir.mkpath(it.value());
83 LOG(VB_GENERAL, LOG_ERR,
84 QString(
"SG() Error: Could not create builtin" 85 "Storage Group directory '%1' for '%2'").arg(it.value())
105 const bool allowFallback)
119 if (!testdir.exists())
122 if (testdir.exists())
124 m_dirlist.prepend(testdir.absolutePath());
132 LOG(VB_FILE, LOG_NOTICE,
LOC +
133 QString(
"Unable to find any directories for the local " 134 "storage group '%1' on '%2', trying directories on " 135 "all hosts!").arg(group).arg(
hostname));
144 LOG(VB_FILE, LOG_NOTICE,
LOC +
145 QString(
"Unable to find storage group '%1', trying " 146 "'Default' group!").arg(group));
154 LOG(VB_FILE, LOG_NOTICE,
LOC +
155 QString(
"Unable to find any directories for the local " 156 "Default storage group on '%1', trying directories " 157 "in all Default groups!").arg(
hostname));
169 QString msg =
"Unable to find any Storage Group Directories. ";
173 msg += QString(
"Using old 'RecordFilePrefix' value of '%1'")
179 msg += QString(
"Using hardcoded default value of '%1'")
182 LOG(VB_GENERAL, LOG_ERR,
LOC + msg);
201 const QString &lbase,
202 bool recursive,
bool onlyDirs)
205 QString base = lbase;
211 if (base.split(
"/").size() > 20)
213 LOG(VB_GENERAL, LOG_ERR,
LOC +
"GetDirFileList(), 20 levels deep, " 214 "possible directory loop detected.");
224 d.entryList(QDir::Dirs|QDir::NoDotAndDotDot|QDir::Readable);
226 for (QStringList::iterator p = list.begin(); p != list.end(); ++p)
228 LOG(VB_FILE, LOG_DEBUG,
LOC +
229 QString(
"GetDirFileList: Dir: %1/%2").arg(base).arg(*p));
232 files.append(base + *p);
234 files <<
GetDirFileList(dir +
"/" + *p, base + *p,
true, onlyDirs);
240 QStringList list =
d.entryList(QDir::Files|QDir::Readable);
241 for (QStringList::iterator p = list.begin(); p != list.end(); ++p)
243 LOG(VB_FILE, LOG_DEBUG,
LOC +
244 QString(
"GetDirFileList: File: %1%2").arg(base).arg(*p));
246 files.append(base + *p);
293 if (Path.isEmpty() || Path ==
"/")
296 files << QString(
"sgdir::%1").arg(*it);
303 if (Path.startsWith(*it))
306 relPath.replace(*it,
"");
307 if (relPath.startsWith(
"/"))
308 relPath.replace(0,1,
"");
313 LOG(VB_FILE, LOG_INFO,
LOC +
314 QString(
"GetFileInfoList: Reading '%1'").arg(Path));
323 d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
324 QFileInfoList list =
d.entryInfoList();
328 for (QFileInfoList::iterator p = list.begin(); p != list.end(); ++p)
330 if (p->fileName() ==
"Thumbs.db")
336 tmp = QString(
"dir::%1::0").arg(p->fileName());
338 tmp = QString(
"file::%1::%2::%3%4").arg(p->fileName()).arg(p->size())
339 .arg(relPath).arg(p->fileName());
341 LOG(VB_FILE, LOG_DEBUG,
LOC +
342 QString(
"GetFileInfoList: (%1)").arg(
tmp));
351 LOG(VB_FILE, LOG_DEBUG,
LOC +
352 QString(
"FileExist: Testing for '%1'").arg(filename));
355 if (filename.isEmpty())
360 if (filename.startsWith(*it))
371 QFile checkFile(filename);
372 if (checkFile.exists(filename))
383 QString filename = lfilename;
384 LOG(VB_FILE, LOG_DEBUG,
LOC +
385 QString(
"GetFileInfo: For '%1'") .arg(filename));
388 bool searched =
false;
396 if ((searched && !filename.isEmpty()) ||
399 QFileInfo fInfo(filename);
402 #if QT_VERSION < QT_VERSION_CHECK(5,8,0) 403 details << QString(
"%1").arg(fInfo.lastModified().toTime_t());
405 if (fInfo.lastModified().isValid()) {
406 details << QString(
"%1").arg(fInfo.lastModified().toSecsSinceEpoch());
408 details << QString((
uint)-1);
411 details << QString(
"%1").arg(fInfo.size());
427 QString result = filename;
430 LOG(VB_FILE, LOG_DEBUG,
431 QString(
"StorageGroup::GetRelativePathname(%1)").arg(filename));
435 if (filename.startsWith(
"myth://"))
439 if (qurl.hasFragment())
440 result = qurl.path() +
"#" + qurl.fragment();
442 result = qurl.path();
444 if (result.startsWith(
"/"))
445 result.replace(0, 1,
"");
450 query.
prepare(
"SELECT DISTINCT dirname FROM storagegroup " 451 "ORDER BY dirname DESC;");
460 dirname = QString::fromUtf8(query.
value(0)
461 .toByteArray().constData());
462 if (filename.startsWith(dirname))
465 result.replace(0, dirname.length(),
"");
466 if (result.startsWith(
"/"))
467 result.replace(0, 1,
"");
469 LOG(VB_FILE, LOG_DEBUG,
470 QString(
"StorageGroup::GetRelativePathname(%1) = '%2'")
471 .arg(filename).arg(result));
477 query.
prepare(
"SELECT DISTINCT data FROM settings WHERE " 478 "value = 'VideoStartupDir';");
483 QString videostartupdir = query.
value(0).toString();
484 QStringList videodirs = videostartupdir.split(
':',
485 QString::SkipEmptyParts);
487 for (QStringList::Iterator it = videodirs.begin();
488 it != videodirs.end(); ++it)
491 if (filename.startsWith(directory))
494 result.replace(0, directory.length(),
"");
495 if (result.startsWith(
"/"))
496 result.replace(0, 1,
"");
498 LOG(VB_FILE, LOG_DEBUG,
499 QString(
"StorageGroup::GetRelativePathname(%1) = '%2'")
500 .arg(filename).arg(result));
510 QDir qdir(it.value());
512 qdir.mkpath(it.value());
514 QString directory = it.value();
515 if (filename.startsWith(directory))
518 result.replace(0, directory.length(),
"");
519 if (result.startsWith(
"/"))
520 result.replace(0, 1,
"");
522 LOG(VB_FILE, LOG_DEBUG,
523 QString(
"StorageGroup::GetRelativePathname(%1) = '%2'")
524 .arg(filename).arg(result));
542 QStringList *dirlist)
550 QString sql =
"SELECT DISTINCT dirname " 551 "FROM storagegroup ";
553 if (!group.isEmpty())
555 sql.append(
"WHERE groupname = :GROUP");
557 sql.append(
" AND hostname = :HOSTNAME");
561 if (!group.isEmpty())
570 else if (query.
next())
577 dirname = QString::fromUtf8(query.
value(0)
578 .toByteArray().constData());
579 dirname.replace(QRegExp(
"^\\s*"),
"");
580 dirname.replace(QRegExp(
"\\s*$"),
"");
581 if (dirname.endsWith(
"/"))
582 dirname.remove(dirname.length() - 1, 1);
585 (*dirlist) << dirname;
589 while (query.
next());
596 if (testdir.exists())
598 if (dirlist && !dirlist->contains(testdir.absolutePath()))
599 (*dirlist) << testdir.absolutePath();
609 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"FindFile: Searching for '%1'")
615 if (!recDir.isEmpty())
617 result = recDir +
"/" + filename;
618 LOG(VB_FILE, LOG_INFO,
LOC +
619 QString(
"FindFile: Found '%1'") .arg(result));
623 LOG(VB_FILE, LOG_ERR,
LOC +
624 QString(
"FindFile: Unable to find '%1'!") .arg(filename));
633 QFileInfo checkFile(
"");
638 QString testFile =
m_dirlist[curDir] +
"/" + filename;
639 LOG(VB_FILE, LOG_DEBUG,
LOC +
640 QString(
"FindFileDir: Checking '%1' for '%2'")
642 checkFile.setFile(testFile);
643 if (checkFile.exists() || checkFile.isSymLink())
654 checkFile.setFile(tmpFile);
655 if (checkFile.exists() || checkFile.isSymLink())
663 result = (tmpFile.isEmpty()) ? result : tmpFile;
670 result = (tmpFile.isEmpty()) ? result : tmpFile;
679 int64_t nextDirFree = 0;
680 int64_t thisDirTotal;
684 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"FindNextDirMostFree: Starting"));
697 if (!checkDir.exists())
699 LOG(VB_GENERAL, LOG_ERR,
LOC +
700 QString(
"FindNextDirMostFree: '%1' does not exist!")
708 LOG(VB_FILE, LOG_DEBUG,
LOC +
709 QString(
"FindNextDirMostFree: '%1' has %2 KiB free")
711 .arg(QString::number(thisDirFree)));
713 if (thisDirFree > nextDirFree)
716 nextDirFree = thisDirFree;
721 if (nextDir.isEmpty())
722 LOG(VB_FILE, LOG_ERR,
LOC +
723 "FindNextDirMostFree: Unable to find any directories to use.");
725 LOG(VB_FILE, LOG_DEBUG,
LOC +
726 QString(
"FindNextDirMostFree: Using '%1'").arg(nextDir));
737 query.
prepare(
"SELECT groupname, dirname " 739 "WHERE hostname = :HOSTNAME;");
747 LOG(VB_FILE, LOG_DEBUG,
LOC +
748 "CheckAllStorageGroupDirs(): Checking All Storage Group directories");
758 dirname = QString::fromUtf8(query.
value(1)
759 .toByteArray().constData());
761 dirname.replace(QRegExp(
"^\\s*"),
"");
762 dirname.replace(QRegExp(
"\\s*$"),
"");
764 LOG(VB_FILE, LOG_DEBUG,
LOC +
765 QString(
"Checking directory '%1' in group '%2'.")
768 testDir.setPath(dirname);
769 if (!testDir.exists())
771 LOG(VB_FILE, LOG_WARNING,
LOC +
772 QString(
"Group '%1' references directory '%2' but this " 773 "directory does not exist. This directory " 774 "will not be used on this server.")
779 testFile.setFileName(dirname +
"/.test");
780 if (testFile.open(QIODevice::WriteOnly))
783 LOG(VB_GENERAL, LOG_ERR,
LOC +
784 QString(
"Group '%1' wants to use directory '%2', but " 785 "this directory is not writeable.")
797 QString sql =
"SELECT DISTINCT groupname " 799 "WHERE groupname NOT IN (";
802 sql.append(QString(
" '%1',").arg(*it));
803 sql = sql.left(sql.length() - 1);
811 groups += query.
value(0).toString();
828 addHost =
" AND hostname = :HOSTNAME";
832 QString sql = QString(
"SELECT dirname,hostname " 834 "WHERE groupname = :GROUPNAME %1").arg(addHost);
837 query.
bindValue(
":GROUPNAME", groupname);
850 dirname = QString::fromUtf8(query.
value(0)
851 .toByteArray().constData());
870 const QString &host,
const QString &sgroup)
872 QString tmpGroup = sgroup;
873 QString groupKey = QString(
"%1:%2").arg(sgroup).arg(host);
889 LOG(VB_FILE, LOG_DEBUG,
890 QString(
"GetGroupToUse(): " 891 "falling back to Videos Storage Group for host %1 " 892 "since it does not have a %2 Storage Group.")
893 .arg(host).arg(sgroup));
StorageGroup(const QString &group="", const QString &hostname="", const bool allowFallback=true)
StorageGroup constructor.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
void bindValue(const QString &placeholder, const QVariant &val)
QStringList GetDirList(void) const
QString GenMythURL(QString host=QString(), QString port=QString(), QString path=QString(), QString storageGroup=QString())
void Init(const QString &group="Default", const QString &hostname="", const bool allowFallback=true)
Initilizes the groupname, hostname, and dirlist.
QString GetFirstDir(bool appendSlash=false) const
static QHash< QString, QString > s_groupToUseCache
static QMutex m_staticInitLock
QSqlQuery wrapper that fetches a DB connection from the connection pool.
QStringList GetFileInfoList(const QString &Path)
static QStringList getRecordingsGroups(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool m_staticInitDone
static const QStringList kSpecialGroups
QStringList GetDirFileList(const QString &dir, const QString &base, bool recursive=false, bool onlyDirs=false)
static QStringList getGroupDirs(const QString &groupname, const QString &host)
static QMap< QString, QString > m_builtinGroups
QVariant value(int i) const
static void CheckAllStorageGroupDirs(void)
static const uint16_t * d
QString GetSetting(const QString &key, const QString &defaultval="")
static QMutex s_groupToUseLock
QString FindNextDirMostFree(void)
static const char * kDefaultStorageDir
bool isActive(void) const
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
QString FindFileDir(const QString &filename)
int64_t getDiskSpace(const QString &file_on_disk, int64_t &total, int64_t &used)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
#define LOG(_MASK_, _LEVEL_, _STRING_)
static QString GetGroupToUse(const QString &host, const QString &sgroup)
QString FindFile(const QString &filename)
bool FileExists(const QString &filename)
static void ClearGroupToUseCache(void)
QStringList GetFileList(const QString &Path, bool recursive=false)
static QString GetRelativePathname(const QString &filename)
Returns the relative pathname of a file by comparing the filename against all Storage Group directori...
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
static void DBError(const QString &where, const MSqlQuery &query)
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.
QString GetHostName(void)
static void StaticInit(void)
QStringList GetFileInfo(const QString &filename)