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