MythTV  master
storagegroup.cpp
Go to the documentation of this file.
1 #include <QDir>
2 #include <QFile>
3 #include <QRegExp>
4 #include <QUrl>
5 
6 #include "storagegroup.h"
7 #include "mythcorecontext.h"
8 #include "mythdb.h"
9 #include "mythlogging.h"
10 #include "mythcoreutil.h"
11 #include "mythdirs.h"
12 
13 #define LOC QString("SG(%1): ").arg(m_groupname)
14 
15 const char *StorageGroup::kDefaultStorageDir = "/mnt/store";
16 
19 QMap<QString, QString> StorageGroup::m_builtinGroups;
21 QHash<QString,QString> StorageGroup::s_groupToUseCache;
22 
23 const QStringList StorageGroup::kSpecialGroups = QStringList()
24  << QT_TRANSLATE_NOOP("(StorageGroups)", "LiveTV")
25 // << "Thumbnails"
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")
36  ;
37 
38 /****************************************************************************/
39 
48 StorageGroup::StorageGroup(QString group, QString hostname,
49  bool allowFallback) :
50  m_groupname(std::move(group)), m_hostname(std::move(hostname)),
51  m_allowFallback(allowFallback)
52 {
53  m_dirlist.clear();
54 
55  if (getenv("MYTHTV_NOSGFALLBACK"))
56  m_allowFallback = false;
57 
59 }
60 
62 {
63  QMutexLocker locker(&m_staticInitLock);
64 
65  if (m_staticInitDone)
66  return;
67 
68  m_staticInitDone = true;
69 
70  m_builtinGroups["ChannelIcons"] = GetConfDir() + "/channels";
71  m_builtinGroups["Themes"] = GetConfDir() + "/themes";
72  m_builtinGroups["Temp"] = GetConfDir() + "/tmp";
73  m_builtinGroups["Streaming"] = GetConfDir() + "/tmp/hls";
74  m_builtinGroups["3rdParty"] = GetConfDir() + "/3rdParty";
75 
76  QMap<QString, QString>::iterator it = m_builtinGroups.begin();
77  for (; it != m_builtinGroups.end(); ++it)
78  {
79  QDir qdir(it.value());
80  if (!qdir.exists())
81  qdir.mkpath(it.value());
82 
83  if (!qdir.exists())
84  LOG(VB_GENERAL, LOG_ERR,
85  QString("SG() Error: Could not create builtin"
86  "Storage Group directory '%1' for '%2'").arg(it.value())
87  .arg(it.key()));
88  }
89 }
90 
105 void StorageGroup::Init(const QString &group, const QString &hostname,
106  const bool allowFallback)
107 {
108  m_groupname = group;
110  m_allowFallback = allowFallback;
111  m_dirlist.clear();
112 
113  StaticInit();
114 
115  bool found = FindDirs(m_groupname, m_hostname, &m_dirlist);
116 
117  if (!found && m_builtinGroups.contains(group))
118  {
119  QDir testdir(m_builtinGroups[group]);
120  if (!testdir.exists())
121  testdir.mkpath(m_builtinGroups[group]);
122 
123  if (testdir.exists())
124  {
125  m_dirlist.prepend(testdir.absolutePath());
126  found = true;
127  }
128  }
129 
130  if ((!found) && m_allowFallback && (m_groupname != "LiveTV") &&
131  (!hostname.isEmpty()))
132  {
133  LOG(VB_FILE, LOG_NOTICE, LOC +
134  QString("Unable to find any directories for the local "
135  "storage group '%1' on '%2', trying directories on "
136  "all hosts!").arg(group).arg(hostname));
137  found = FindDirs(m_groupname, "", &m_dirlist);
138  if (found)
139  {
140  m_hostname = "";
141  }
142  }
143  if ((!found) && m_allowFallback && (group != "Default"))
144  {
145  LOG(VB_FILE, LOG_NOTICE, LOC +
146  QString("Unable to find storage group '%1', trying "
147  "'Default' group!").arg(group));
148  found = FindDirs("Default", m_hostname, &m_dirlist);
149  if(found)
150  {
151  m_groupname = "Default";
152  }
153  else if (!hostname.isEmpty())
154  {
155  LOG(VB_FILE, LOG_NOTICE, LOC +
156  QString("Unable to find any directories for the local "
157  "Default storage group on '%1', trying directories "
158  "in all Default groups!").arg(hostname));
159  found = FindDirs("Default", "", &m_dirlist);
160  if(found)
161  {
162  m_groupname = "Default";
163  m_hostname = "";
164  }
165  }
166  }
167 
168  if (allowFallback && m_dirlist.empty())
169  {
170  QString msg = "Unable to find any Storage Group Directories. ";
171  QString tmpDir = gCoreContext->GetSetting("RecordFilePrefix");
172  if (tmpDir != "")
173  {
174  msg += QString("Using old 'RecordFilePrefix' value of '%1'")
175  .arg(tmpDir);
176  }
177  else
178  {
179  tmpDir = kDefaultStorageDir;
180  msg += QString("Using hardcoded default value of '%1'")
181  .arg(kDefaultStorageDir);
182  }
183  LOG(VB_GENERAL, LOG_ERR, LOC + msg);
184  m_dirlist << tmpDir;
185  }
186 }
187 
188 QString StorageGroup::GetFirstDir(bool appendSlash) const
189 {
190  if (m_dirlist.isEmpty())
191  return QString();
192 
193  QString tmp = m_dirlist[0];
194 
195  if (appendSlash)
196  tmp += "/";
197 
198  return tmp;
199 }
200 
201 QStringList StorageGroup::GetDirFileList(const QString &dir,
202  const QString &lbase,
203  bool recursive, bool onlyDirs)
204 {
205  QStringList files;
206  QString base = lbase;
207  QDir d(dir);
208 
209  if (!d.exists())
210  return files;
211 
212  if (base.split("/").size() > 20)
213  {
214  LOG(VB_GENERAL, LOG_ERR, LOC + "GetDirFileList(), 20 levels deep, "
215  "possible directory loop detected.");
216  return files;
217  }
218 
219  if (!base.isEmpty())
220  base += "/";
221 
222  if (recursive)
223  {
224  QStringList list =
225  d.entryList(QDir::Dirs|QDir::NoDotAndDotDot|QDir::Readable);
226 
227  for (QStringList::iterator p = list.begin(); p != list.end(); ++p)
228  {
229  LOG(VB_FILE, LOG_DEBUG, LOC +
230  QString("GetDirFileList: Dir: %1/%2").arg(base).arg(*p));
231 
232  if (onlyDirs)
233  files.append(base + *p);
234 
235  files << GetDirFileList(dir + "/" + *p, base + *p, true, onlyDirs);
236  }
237  }
238 
239  if (!onlyDirs)
240  {
241  QStringList list = d.entryList(QDir::Files|QDir::Readable);
242  for (QStringList::iterator p = list.begin(); p != list.end(); ++p)
243  {
244  LOG(VB_FILE, LOG_DEBUG, LOC +
245  QString("GetDirFileList: File: %1%2").arg(base).arg(*p));
246  if (recursive)
247  files.append(base + *p);
248  else
249  files.append(*p);
250  }
251  }
252  return files;
253 }
254 
255 QStringList StorageGroup::GetDirList(const QString &Path, bool recursive)
256 {
257  QStringList files;
258  QString tmpDir;
259  QDir d;
260  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
261  {
262  tmpDir = *it + Path;
263  d.setPath(tmpDir);
264  if (d.exists())
265  files << GetDirFileList(tmpDir, Path, recursive, true);
266  }
267  return files;
268 }
269 
270 QStringList StorageGroup::GetFileList(const QString &Path, bool recursive)
271 {
272  QStringList files;
273  QString tmpDir;
274  QDir d;
275 
276  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
277  {
278  tmpDir = *it + Path;
279 
280  d.setPath(tmpDir);
281  if (d.exists())
282  files << GetDirFileList(tmpDir, Path, recursive, false);
283  }
284 
285  return files;
286 }
287 
288 QStringList StorageGroup::GetFileInfoList(const QString &Path)
289 {
290  QStringList files;
291  QString relPath;
292  bool badPath = true;
293 
294  if (Path.isEmpty() || Path == "/")
295  {
296  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
297  files << QString("sgdir::%1").arg(*it);
298 
299  return files;
300  }
301 
302  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
303  {
304  if (Path.startsWith(*it))
305  {
306  relPath = Path;
307  relPath.replace(*it,"");
308  if (relPath.startsWith("/"))
309  relPath.replace(0,1,"");
310  badPath = false;
311  }
312  }
313 
314  LOG(VB_FILE, LOG_INFO, LOC +
315  QString("GetFileInfoList: Reading '%1'").arg(Path));
316 
317  if (badPath)
318  return files;
319 
320  QDir d(Path);
321  if (!d.exists())
322  return files;
323 
324  d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
325  QFileInfoList list = d.entryInfoList();
326  if (list.isEmpty())
327  return files;
328 
329  for (QFileInfoList::iterator p = list.begin(); p != list.end(); ++p)
330  {
331  if (p->fileName() == "Thumbs.db")
332  continue;
333 
334  QString tmp;
335 
336  if (p->isDir())
337  tmp = QString("dir::%1::0").arg(p->fileName());
338  else
339  tmp = QString("file::%1::%2::%3%4").arg(p->fileName()).arg(p->size())
340  .arg(relPath).arg(p->fileName());
341 
342  LOG(VB_FILE, LOG_DEBUG, LOC +
343  QString("GetFileInfoList: (%1)").arg(tmp));
344  files.append(tmp);
345  }
346 
347  return files;
348 }
349 
350 bool StorageGroup::FileExists(const QString &filename)
351 {
352  LOG(VB_FILE, LOG_DEBUG, LOC +
353  QString("FileExist: Testing for '%1'").arg(filename));
354  bool badPath = true;
355 
356  if (filename.isEmpty())
357  return false;
358 
359  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
360  {
361  if (filename.startsWith(*it))
362  {
363  badPath = false;
364  }
365  }
366 
367  if (badPath)
368  return false;
369 
370  return QFile::exists(filename);
371 }
372 
373 
374 // Returns a string list of details about the file
375 // in the order FILENAME, DATE, SIZE
376 QStringList StorageGroup::GetFileInfo(const QString &lfilename)
377 {
378  QString filename = lfilename;
379  LOG(VB_FILE, LOG_DEBUG, LOC +
380  QString("GetFileInfo: For '%1'") .arg(filename));
381 
382  QStringList details;
383  bool searched = false;
384 
385  if (!FileExists(filename))
386  {
387  searched = true;
389  }
390 
391  if ((searched && !filename.isEmpty()) ||
392  (FileExists(filename)))
393  {
394  QFileInfo fInfo(filename);
395 
396  details << filename;
397 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
398  details << QString("%1").arg(fInfo.lastModified().toTime_t());
399 #else
400  if (fInfo.lastModified().isValid()) {
401  details << QString("%1").arg(fInfo.lastModified().toSecsSinceEpoch());
402  } else {
403  details << QString((uint)-1);
404  }
405 #endif
406  details << QString("%1").arg(fInfo.size());
407  }
408 
409  return details;
410 }
411 
421 {
422  QString result = filename;
423  MSqlQuery query(MSqlQuery::InitCon());
424 
425  LOG(VB_FILE, LOG_DEBUG,
426  QString("StorageGroup::GetRelativePathname(%1)").arg(filename));
427 
428  StaticInit();
429 
430  if (filename.startsWith("myth://"))
431  {
432  QUrl qurl(filename);
433 
434  if (qurl.hasFragment())
435  result = qurl.path() + "#" + qurl.fragment();
436  else
437  result = qurl.path();
438 
439  if (result.startsWith("/"))
440  result.replace(0, 1, "");
441 
442  return result;
443  }
444 
445  query.prepare("SELECT DISTINCT dirname FROM storagegroup "
446  "ORDER BY dirname DESC;");
447  if (query.exec())
448  {
449  QString dirname;
450  while (query.next())
451  {
452  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
453  * uses QString::fromLatin1() for toString(). Explicitly convert the
454  * value using QString::fromUtf8() to prevent corruption. */
455  dirname = QString::fromUtf8(query.value(0)
456  .toByteArray().constData());
457  if (filename.startsWith(dirname))
458  {
459  result = filename;
460  result.replace(0, dirname.length(), "");
461  if (result.startsWith("/"))
462  result.replace(0, 1, "");
463 
464  LOG(VB_FILE, LOG_DEBUG,
465  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
466  .arg(filename).arg(result));
467  return result;
468  }
469  }
470  }
471 
472  query.prepare("SELECT DISTINCT data FROM settings WHERE "
473  "value = 'VideoStartupDir';");
474  if (query.exec())
475  {
476  while (query.next())
477  {
478  QString videostartupdir = query.value(0).toString();
479  QStringList videodirs = videostartupdir.split(':',
480  QString::SkipEmptyParts);
481  QString directory;
482  for (QStringList::Iterator it = videodirs.begin();
483  it != videodirs.end(); ++it)
484  {
485  directory = *it;
486  if (filename.startsWith(directory))
487  {
488  result = filename;
489  result.replace(0, directory.length(), "");
490  if (result.startsWith("/"))
491  result.replace(0, 1, "");
492 
493  LOG(VB_FILE, LOG_DEBUG,
494  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
495  .arg(filename).arg(result));
496  return result;
497  }
498  }
499  }
500  }
501 
502  QMap<QString, QString>::iterator it = m_builtinGroups.begin();
503  for (; it != m_builtinGroups.end(); ++it)
504  {
505  QDir qdir(it.value());
506  if (!qdir.exists())
507  qdir.mkpath(it.value());
508 
509  QString directory = it.value();
510  if (filename.startsWith(directory))
511  {
512  result = filename;
513  result.replace(0, directory.length(), "");
514  if (result.startsWith("/"))
515  result.replace(0, 1, "");
516 
517  LOG(VB_FILE, LOG_DEBUG,
518  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
519  .arg(filename).arg(result));
520  return result;
521  }
522  }
523 
524  return result;
525 }
526 
536 bool StorageGroup::FindDirs(const QString &group, const QString &hostname,
537  QStringList *dirlist)
538 {
539  bool found = false;
540  QString dirname;
541  MSqlQuery query(MSqlQuery::InitCon());
542 
543  StaticInit();
544 
545  QString sql = "SELECT DISTINCT dirname "
546  "FROM storagegroup ";
547 
548  if (!group.isEmpty())
549  {
550  sql.append("WHERE groupname = :GROUP");
551  if (!hostname.isEmpty())
552  sql.append(" AND hostname = :HOSTNAME");
553  }
554 
555  query.prepare(sql);
556  if (!group.isEmpty())
557  {
558  query.bindValue(":GROUP", group);
559  if (!hostname.isEmpty())
560  query.bindValue(":HOSTNAME", hostname);
561  }
562 
563  if (!query.exec() || !query.isActive())
564  MythDB::DBError("StorageGroup::StorageGroup()", query);
565  else if (query.next())
566  {
567  do
568  {
569  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
570  * uses QString::fromLatin1() for toString(). Explicitly convert the
571  * value using QString::fromUtf8() to prevent corruption. */
572  dirname = QString::fromUtf8(query.value(0)
573  .toByteArray().constData());
574  dirname.replace(QRegExp("^\\s*"), "");
575  dirname.replace(QRegExp("\\s*$"), "");
576  if (dirname.endsWith("/"))
577  dirname.remove(dirname.length() - 1, 1);
578 
579  if (dirlist)
580  (*dirlist) << dirname;
581  else
582  return true;
583  }
584  while (query.next());
585  found = true;
586  }
587 
588  if (m_builtinGroups.contains(group))
589  {
590  QDir testdir(m_builtinGroups[group]);
591  if (testdir.exists())
592  {
593  if (dirlist && !dirlist->contains(testdir.absolutePath()))
594  (*dirlist) << testdir.absolutePath();
595  found = true;
596  }
597  }
598 
599  return found;
600 }
601 
602 QString StorageGroup::FindFile(const QString &filename)
603 {
604  LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindFile: Searching for '%1'")
605  .arg(filename));
606 
607  QString recDir = FindFileDir(filename);
608  QString result = "";
609 
610  if (!recDir.isEmpty())
611  {
612  result = recDir + "/" + filename;
613  LOG(VB_FILE, LOG_INFO, LOC +
614  QString("FindFile: Found '%1'") .arg(result));
615  }
616  else
617  {
618  LOG(VB_FILE, LOG_ERR, LOC +
619  QString("FindFile: Unable to find '%1'!") .arg(filename));
620  }
621 
622  return result;
623 }
624 
625 QString StorageGroup::FindFileDir(const QString &filename)
626 {
627  QString result = "";
628  QFileInfo checkFile("");
629 
630  int curDir = 0;
631  while (curDir < m_dirlist.size())
632  {
633  QString testFile = m_dirlist[curDir] + "/" + filename;
634  LOG(VB_FILE, LOG_DEBUG, LOC +
635  QString("FindFileDir: Checking '%1' for '%2'")
636  .arg(m_dirlist[curDir]).arg(testFile));
637  checkFile.setFile(testFile);
638  if (checkFile.exists() || checkFile.isSymLink())
639  return m_dirlist[curDir];
640 
641  curDir++;
642  }
643 
644  if (m_groupname.isEmpty() || !m_allowFallback)
645  {
646  // Not found in any dir, so try RecordFilePrefix if it exists
647  QString tmpFile =
648  gCoreContext->GetSetting("RecordFilePrefix") + "/" + filename;
649  checkFile.setFile(tmpFile);
650  if (checkFile.exists() || checkFile.isSymLink())
651  result = tmpFile;
652  }
653  else if (m_groupname != "Default")
654  {
655  // Not found in current group so try Default
656  StorageGroup sgroup("Default");
657  QString tmpFile = sgroup.FindFileDir(filename);
658  result = (tmpFile.isEmpty()) ? result : tmpFile;
659  }
660  else
661  {
662  // Not found in Default so try any dir
663  StorageGroup sgroup;
664  QString tmpFile = sgroup.FindFileDir(filename);
665  result = (tmpFile.isEmpty()) ? result : tmpFile;
666  }
667 
668  return result;
669 }
670 
672 {
673  QString nextDir;
674  int64_t nextDirFree = 0;
675  int64_t thisDirTotal = 0;
676  int64_t thisDirUsed = 0;
677  int64_t thisDirFree = 0;
678 
679  LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindNextDirMostFree: Starting"));
680 
681  if (m_allowFallback)
682  nextDir = kDefaultStorageDir;
683 
684  if (!m_dirlist.empty())
685  nextDir = m_dirlist[0];
686 
687  QDir checkDir("");
688  int curDir = 0;
689  while (curDir < m_dirlist.size())
690  {
691  checkDir.setPath(m_dirlist[curDir]);
692  if (!checkDir.exists())
693  {
694  LOG(VB_GENERAL, LOG_ERR, LOC +
695  QString("FindNextDirMostFree: '%1' does not exist!")
696  .arg(m_dirlist[curDir]));
697  curDir++;
698  continue;
699  }
700 
701  thisDirFree = getDiskSpace(m_dirlist[curDir], thisDirTotal,
702  thisDirUsed);
703  LOG(VB_FILE, LOG_DEBUG, LOC +
704  QString("FindNextDirMostFree: '%1' has %2 KiB free")
705  .arg(m_dirlist[curDir])
706  .arg(QString::number(thisDirFree)));
707 
708  if (thisDirFree > nextDirFree)
709  {
710  nextDir = m_dirlist[curDir];
711  nextDirFree = thisDirFree;
712  }
713  curDir++;
714  }
715 
716  if (nextDir.isEmpty())
717  LOG(VB_FILE, LOG_ERR, LOC +
718  "FindNextDirMostFree: Unable to find any directories to use.");
719  else
720  LOG(VB_FILE, LOG_DEBUG, LOC +
721  QString("FindNextDirMostFree: Using '%1'").arg(nextDir));
722 
723  return nextDir;
724 }
725 
727 {
728  QString m_groupname;
729  QString dirname;
730  MSqlQuery query(MSqlQuery::InitCon());
731 
732  query.prepare("SELECT groupname, dirname "
733  "FROM storagegroup "
734  "WHERE hostname = :HOSTNAME;");
735  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
736  if (!query.exec() || !query.isActive())
737  {
738  MythDB::DBError("StorageGroup::CheckAllStorageGroupDirs()", query);
739  return;
740  }
741 
742  LOG(VB_FILE, LOG_DEBUG, LOC +
743  "CheckAllStorageGroupDirs(): Checking All Storage Group directories");
744 
745  QFile testFile("");
746  QDir testDir("");
747  while (query.next())
748  {
749  m_groupname = query.value(0).toString();
750  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
751  * uses QString::fromLatin1() for toString(). Explicitly convert the
752  * value using QString::fromUtf8() to prevent corruption. */
753  dirname = QString::fromUtf8(query.value(1)
754  .toByteArray().constData());
755 
756  dirname.replace(QRegExp("^\\s*"), "");
757  dirname.replace(QRegExp("\\s*$"), "");
758 
759  LOG(VB_FILE, LOG_DEBUG, LOC +
760  QString("Checking directory '%1' in group '%2'.")
761  .arg(dirname).arg(m_groupname));
762 
763  testDir.setPath(dirname);
764  if (!testDir.exists())
765  {
766  LOG(VB_FILE, LOG_WARNING, LOC +
767  QString("Group '%1' references directory '%2' but this "
768  "directory does not exist. This directory "
769  "will not be used on this server.")
770  .arg(m_groupname).arg(dirname));
771  }
772  else
773  {
774  testFile.setFileName(dirname + "/.test");
775  if (testFile.open(QIODevice::WriteOnly))
776  testFile.remove();
777  else
778  LOG(VB_GENERAL, LOG_ERR, LOC +
779  QString("Group '%1' wants to use directory '%2', but "
780  "this directory is not writeable.")
781  .arg(m_groupname).arg(dirname));
782  }
783  }
784 }
785 
787 {
788  QStringList groups;
789 
790  MSqlQuery query(MSqlQuery::InitCon());
791 
792  QString sql = "SELECT DISTINCT groupname "
793  "FROM storagegroup "
794  "WHERE groupname NOT IN (";
795  for (QStringList::const_iterator it = StorageGroup::kSpecialGroups.begin();
796  it != StorageGroup::kSpecialGroups.end(); ++it)
797  sql.append(QString(" '%1',").arg(*it));
798  sql = sql.left(sql.length() - 1);
799  sql.append(" );");
800 
801  query.prepare(sql);
802  if (query.exec())
803  {
804  while (query.next())
805  {
806  groups += query.value(0).toString();
807  }
808  }
809 
810  groups.sort();
811  return groups;
812 }
813 
814 QStringList StorageGroup::getGroupDirs(const QString &groupname,
815  const QString &host)
816 {
817  QStringList groups;
818  QString addHost;
819 
820  MSqlQuery query(MSqlQuery::InitCon());
821 
822  if (!host.isEmpty())
823  addHost = " AND hostname = :HOSTNAME";
824  else
825  addHost = "";
826 
827  QString sql = QString("SELECT dirname,hostname "
828  "FROM storagegroup "
829  "WHERE groupname = :GROUPNAME %1").arg(addHost);
830 
831  query.prepare(sql);
832  query.bindValue(":GROUPNAME", groupname);
833 
834  if (!host.isEmpty())
835  query.bindValue(":HOSTNAME", host);
836 
837  if (query.exec())
838  {
839  QString dirname;
840  while (query.next())
841  {
842  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
843  * uses QString::fromLatin1() for toString(). Explicitly convert the
844  * value using QString::fromUtf8() to prevent corruption. */
845  dirname = QString::fromUtf8(query.value(0)
846  .toByteArray().constData());
847  groups += MythCoreContext::GenMythURL(query.value(1).toString(),
848  0,
849  dirname,
850  groupname);
851  }
852  }
853 
854  groups.sort();
855  return groups;
856 }
857 
859 {
860  QMutexLocker locker(&s_groupToUseLock);
861  s_groupToUseCache.clear();
862 }
863 
865  const QString &host, const QString &sgroup)
866 {
867  QString tmpGroup = sgroup;
868  QString groupKey = QString("%1:%2").arg(sgroup).arg(host);
869 
870  QMutexLocker locker(&s_groupToUseLock);
871 
872  if (s_groupToUseCache.contains(groupKey))
873  {
874  tmpGroup = s_groupToUseCache[groupKey];
875  }
876  else
877  {
878  if (StorageGroup::FindDirs(sgroup, host))
879  {
880  s_groupToUseCache[groupKey] = sgroup;
881  }
882  else
883  {
884  LOG(VB_FILE, LOG_DEBUG,
885  QString("GetGroupToUse(): "
886  "falling back to Videos Storage Group for host %1 "
887  "since it does not have a %2 Storage Group.")
888  .arg(host).arg(sgroup));
889 
890  tmpGroup = "Videos";
891  s_groupToUseCache[groupKey] = tmpGroup;
892  }
893  }
894 
895  return tmpGroup;
896 }
897 
898 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:781
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:862
QStringList GetDirList(void) const
Definition: storagegroup.h:23
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
Definition: storagegroup.h:69
static QMutex m_staticInitLock
Definition: storagegroup.h:59
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
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
Definition: storagegroup.h:58
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
static guint32 * tmp
Definition: goom_core.c:35
#define LOC
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
Definition: storagegroup.h:66
QString GetConfDir(void)
Definition: mythdirs.cpp:224
QVariant value(int i) const
Definition: mythdbcon.h:198
static void CheckAllStorageGroupDirs(void)
StorageGroup(QString group="", QString hostname="", const bool allowFallback=true)
StorageGroup constructor.
static const uint16_t * d
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
QString GetSetting(const QString &key, const QString &defaultval="")
static QMutex s_groupToUseLock
Definition: storagegroup.h:68
QString FindNextDirMostFree(void)
static const char * kDefaultStorageDir
Definition: storagegroup.h:45
bool isActive(void) const
Definition: mythdbcon.h:204
string hostname
Definition: caa.py:17
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
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.
Definition: mythdbcon.cpp:806
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static QString GetGroupToUse(const QString &host, const QString &sgroup)
bool m_allowFallback
Definition: storagegroup.h:63
QString m_hostname
Definition: storagegroup.h:62
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.
Definition: mythdbcon.cpp:602
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
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)
QString m_groupname
Definition: storagegroup.h:61
QStringList m_dirlist
Definition: storagegroup.h:64