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  foreach (auto & p, 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  foreach (auto & p, 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  foreach (auto & dir, 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  foreach (auto & dir, 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  foreach (auto & dir, m_dirlist)
299  files << QString("sgdir::%1").arg(dir);
300 
301  return files;
302  }
303 
304  foreach (auto & dir, 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  foreach (auto & entry, 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  tmp = QString("file::%1::%2::%3%4").arg(entry.fileName())
342  .arg(entry.size())
343  .arg(relPath).arg(entry.fileName());
344 
345  LOG(VB_FILE, LOG_DEBUG, LOC +
346  QString("GetFileInfoList: (%1)").arg(tmp));
347  files.append(tmp);
348  }
349 
350  return files;
351 }
352 
353 bool StorageGroup::FileExists(const QString &filename)
354 {
355  LOG(VB_FILE, LOG_DEBUG, LOC +
356  QString("FileExist: Testing for '%1'").arg(filename));
357  bool badPath = true;
358 
359  if (filename.isEmpty())
360  return false;
361 
362  foreach (auto & dir, m_dirlist)
363  {
364  if (filename.startsWith(dir))
365  {
366  badPath = false;
367  }
368  }
369 
370  if (badPath)
371  return false;
372 
373  return QFile::exists(filename);
374 }
375 
376 
377 // Returns a string list of details about the file
378 // in the order FILENAME, DATE, SIZE
379 QStringList StorageGroup::GetFileInfo(const QString &lfilename)
380 {
381  QString filename = lfilename;
382  LOG(VB_FILE, LOG_DEBUG, LOC +
383  QString("GetFileInfo: For '%1'") .arg(filename));
384 
385  QStringList details;
386  bool searched = false;
387 
388  if (!FileExists(filename))
389  {
390  searched = true;
392  }
393 
394  if ((searched && !filename.isEmpty()) ||
395  (FileExists(filename)))
396  {
397  QFileInfo fInfo(filename);
398 
399  details << filename;
400 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
401  details << QString("%1").arg(fInfo.lastModified().toTime_t());
402 #else
403  if (fInfo.lastModified().isValid()) {
404  details << QString("%1").arg(fInfo.lastModified().toSecsSinceEpoch());
405  } else {
406  details << QString(UINT_MAX);
407  }
408 #endif
409  details << QString("%1").arg(fInfo.size());
410  }
411 
412  return details;
413 }
414 
424 {
425  QString result = filename;
426  MSqlQuery query(MSqlQuery::InitCon());
427 
428  LOG(VB_FILE, LOG_DEBUG,
429  QString("StorageGroup::GetRelativePathname(%1)").arg(filename));
430 
431  StaticInit();
432 
433  if (filename.startsWith("myth://"))
434  {
435  QUrl qurl(filename);
436 
437  if (qurl.hasFragment())
438  result = qurl.path() + "#" + qurl.fragment();
439  else
440  result = qurl.path();
441 
442  if (result.startsWith("/"))
443  result.replace(0, 1, "");
444 
445  return result;
446  }
447 
448  query.prepare("SELECT DISTINCT dirname FROM storagegroup "
449  "ORDER BY dirname DESC;");
450  if (query.exec())
451  {
452  QString dirname;
453  while (query.next())
454  {
455  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
456  * uses QString::fromLatin1() for toString(). Explicitly convert the
457  * value using QString::fromUtf8() to prevent corruption. */
458  dirname = QString::fromUtf8(query.value(0)
459  .toByteArray().constData());
460  if (filename.startsWith(dirname))
461  {
462  result = filename;
463  result.replace(0, dirname.length(), "");
464  if (result.startsWith("/"))
465  result.replace(0, 1, "");
466 
467  LOG(VB_FILE, LOG_DEBUG,
468  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
469  .arg(filename).arg(result));
470  return result;
471  }
472  }
473  }
474 
475  query.prepare("SELECT DISTINCT data FROM settings WHERE "
476  "value = 'VideoStartupDir';");
477  if (query.exec())
478  {
479  while (query.next())
480  {
481  QString videostartupdir = query.value(0).toString();
482  QStringList videodirs = videostartupdir.split(':',
483  QString::SkipEmptyParts);
484  foreach (auto & directory, videodirs)
485  {
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  foreach (auto group, m_builtinGroups)
503  {
504  QDir qdir(group);
505  if (!qdir.exists())
506  qdir.mkpath(group);
507 
508  const QString& directory = group;
509  if (filename.startsWith(directory))
510  {
511  result = filename;
512  result.replace(0, directory.length(), "");
513  if (result.startsWith("/"))
514  result.replace(0, 1, "");
515 
516  LOG(VB_FILE, LOG_DEBUG,
517  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
518  .arg(filename).arg(result));
519  return result;
520  }
521  }
522 
523  return result;
524 }
525 
535 bool StorageGroup::FindDirs(const QString &group, const QString &hostname,
536  QStringList *dirlist)
537 {
538  bool found = false;
539  QString dirname;
540  MSqlQuery query(MSqlQuery::InitCon());
541 
542  StaticInit();
543 
544  QString sql = "SELECT DISTINCT dirname "
545  "FROM storagegroup ";
546 
547  if (!group.isEmpty())
548  {
549  sql.append("WHERE groupname = :GROUP");
550  if (!hostname.isEmpty())
551  sql.append(" AND hostname = :HOSTNAME");
552  }
553 
554  query.prepare(sql);
555  if (!group.isEmpty())
556  {
557  query.bindValue(":GROUP", group);
558  if (!hostname.isEmpty())
559  query.bindValue(":HOSTNAME", hostname);
560  }
561 
562  if (!query.exec() || !query.isActive())
563  MythDB::DBError("StorageGroup::StorageGroup()", query);
564  else if (query.next())
565  {
566  do
567  {
568  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
569  * uses QString::fromLatin1() for toString(). Explicitly convert the
570  * value using QString::fromUtf8() to prevent corruption. */
571  dirname = QString::fromUtf8(query.value(0)
572  .toByteArray().constData());
573  dirname.replace(QRegExp("^\\s*"), "");
574  dirname.replace(QRegExp("\\s*$"), "");
575  if (dirname.endsWith("/"))
576  dirname.remove(dirname.length() - 1, 1);
577 
578  if (dirlist)
579  (*dirlist) << dirname;
580  else
581  return true;
582  }
583  while (query.next());
584  found = true;
585  }
586 
587  if (m_builtinGroups.contains(group))
588  {
589  QDir testdir(m_builtinGroups[group]);
590  if (testdir.exists())
591  {
592  if (dirlist && !dirlist->contains(testdir.absolutePath()))
593  (*dirlist) << testdir.absolutePath();
594  found = true;
595  }
596  }
597 
598  return found;
599 }
600 
601 QString StorageGroup::FindFile(const QString &filename)
602 {
603  LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindFile: Searching for '%1'")
604  .arg(filename));
605 
606  QString recDir = FindFileDir(filename);
607  QString result = "";
608 
609  if (!recDir.isEmpty())
610  {
611  result = recDir + "/" + filename;
612  LOG(VB_FILE, LOG_INFO, LOC +
613  QString("FindFile: Found '%1'") .arg(result));
614  }
615  else
616  {
617  LOG(VB_FILE, LOG_ERR, LOC +
618  QString("FindFile: Unable to find '%1'!") .arg(filename));
619  }
620 
621  return result;
622 }
623 
624 QString StorageGroup::FindFileDir(const QString &filename)
625 {
626  QString result = "";
627  QFileInfo checkFile("");
628 
629  int curDir = 0;
630  while (curDir < m_dirlist.size())
631  {
632  QString testFile = m_dirlist[curDir] + "/" + filename;
633  LOG(VB_FILE, LOG_DEBUG, LOC +
634  QString("FindFileDir: Checking '%1' for '%2'")
635  .arg(m_dirlist[curDir]).arg(testFile));
636  checkFile.setFile(testFile);
637  if (checkFile.exists() || checkFile.isSymLink())
638  return m_dirlist[curDir];
639 
640  curDir++;
641  }
642 
643  if (m_groupname.isEmpty() || !m_allowFallback)
644  {
645  // Not found in any dir, so try RecordFilePrefix if it exists
646  QString tmpFile =
647  gCoreContext->GetSetting("RecordFilePrefix") + "/" + filename;
648  checkFile.setFile(tmpFile);
649  if (checkFile.exists() || checkFile.isSymLink())
650  result = tmpFile;
651  }
652  else if (m_groupname != "Default")
653  {
654  // Not found in current group so try Default
655  StorageGroup sgroup("Default");
656  QString tmpFile = sgroup.FindFileDir(filename);
657  result = (tmpFile.isEmpty()) ? result : tmpFile;
658  }
659  else
660  {
661  // Not found in Default so try any dir
662  StorageGroup sgroup;
663  QString tmpFile = sgroup.FindFileDir(filename);
664  result = (tmpFile.isEmpty()) ? result : tmpFile;
665  }
666 
667  return result;
668 }
669 
671 {
672  QString nextDir;
673  int64_t nextDirFree = 0;
674  int64_t thisDirTotal = 0;
675  int64_t thisDirUsed = 0;
676  int64_t thisDirFree = 0;
677 
678  LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindNextDirMostFree: Starting"));
679 
680  if (m_allowFallback)
681  nextDir = kDefaultStorageDir;
682 
683  if (!m_dirlist.empty())
684  nextDir = m_dirlist[0];
685 
686  QDir checkDir("");
687  int curDir = 0;
688  while (curDir < m_dirlist.size())
689  {
690  checkDir.setPath(m_dirlist[curDir]);
691  if (!checkDir.exists())
692  {
693  LOG(VB_GENERAL, LOG_ERR, LOC +
694  QString("FindNextDirMostFree: '%1' does not exist!")
695  .arg(m_dirlist[curDir]));
696  curDir++;
697  continue;
698  }
699 
700  thisDirFree = getDiskSpace(m_dirlist[curDir], thisDirTotal,
701  thisDirUsed);
702  LOG(VB_FILE, LOG_DEBUG, LOC +
703  QString("FindNextDirMostFree: '%1' has %2 KiB free")
704  .arg(m_dirlist[curDir])
705  .arg(QString::number(thisDirFree)));
706 
707  if (thisDirFree > nextDirFree)
708  {
709  nextDir = m_dirlist[curDir];
710  nextDirFree = thisDirFree;
711  }
712  curDir++;
713  }
714 
715  if (nextDir.isEmpty())
716  {
717  LOG(VB_FILE, LOG_ERR, LOC +
718  "FindNextDirMostFree: Unable to find any directories to use.");
719  }
720  else
721  {
722  LOG(VB_FILE, LOG_DEBUG, LOC +
723  QString("FindNextDirMostFree: Using '%1'").arg(nextDir));
724  }
725 
726  return nextDir;
727 }
728 
730 {
731  QString m_groupname;
732  QString dirname;
733  MSqlQuery query(MSqlQuery::InitCon());
734 
735  query.prepare("SELECT groupname, dirname "
736  "FROM storagegroup "
737  "WHERE hostname = :HOSTNAME;");
738  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
739  if (!query.exec() || !query.isActive())
740  {
741  MythDB::DBError("StorageGroup::CheckAllStorageGroupDirs()", query);
742  return;
743  }
744 
745  LOG(VB_FILE, LOG_DEBUG, LOC +
746  "CheckAllStorageGroupDirs(): Checking All Storage Group directories");
747 
748  QFile testFile("");
749  QDir testDir("");
750  while (query.next())
751  {
752  m_groupname = query.value(0).toString();
753  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
754  * uses QString::fromLatin1() for toString(). Explicitly convert the
755  * value using QString::fromUtf8() to prevent corruption. */
756  dirname = QString::fromUtf8(query.value(1)
757  .toByteArray().constData());
758 
759  dirname.replace(QRegExp("^\\s*"), "");
760  dirname.replace(QRegExp("\\s*$"), "");
761 
762  LOG(VB_FILE, LOG_DEBUG, LOC +
763  QString("Checking directory '%1' in group '%2'.")
764  .arg(dirname).arg(m_groupname));
765 
766  testDir.setPath(dirname);
767  if (!testDir.exists())
768  {
769  LOG(VB_FILE, LOG_WARNING, LOC +
770  QString("Group '%1' references directory '%2' but this "
771  "directory does not exist. This directory "
772  "will not be used on this server.")
773  .arg(m_groupname).arg(dirname));
774  }
775  else
776  {
777  testFile.setFileName(dirname + "/.test");
778  if (testFile.open(QIODevice::WriteOnly))
779  testFile.remove();
780  else
781  {
782  LOG(VB_GENERAL, LOG_ERR, LOC +
783  QString("Group '%1' wants to use directory '%2', but "
784  "this directory is not writeable.")
785  .arg(m_groupname).arg(dirname));
786  }
787  }
788  }
789 }
790 
792 {
793  QStringList groups;
794 
795  MSqlQuery query(MSqlQuery::InitCon());
796 
797  QString sql = "SELECT DISTINCT groupname "
798  "FROM storagegroup "
799  "WHERE groupname NOT IN (";
800  foreach (const auto & group, StorageGroup::kSpecialGroups)
801  sql.append(QString(" '%1',").arg(group));
802  sql = sql.left(sql.length() - 1);
803  sql.append(" );");
804 
805  query.prepare(sql);
806  if (query.exec())
807  {
808  while (query.next())
809  {
810  groups += query.value(0).toString();
811  }
812  }
813 
814  groups.sort();
815  return groups;
816 }
817 
818 QStringList StorageGroup::getGroupDirs(const QString &groupname,
819  const QString &host)
820 {
821  QStringList groups;
822  QString addHost;
823 
824  MSqlQuery query(MSqlQuery::InitCon());
825 
826  if (!host.isEmpty())
827  addHost = " AND hostname = :HOSTNAME";
828  else
829  addHost = "";
830 
831  QString sql = QString("SELECT dirname,hostname "
832  "FROM storagegroup "
833  "WHERE groupname = :GROUPNAME %1").arg(addHost);
834 
835  query.prepare(sql);
836  query.bindValue(":GROUPNAME", groupname);
837 
838  if (!host.isEmpty())
839  query.bindValue(":HOSTNAME", host);
840 
841  if (query.exec())
842  {
843  QString dirname;
844  while (query.next())
845  {
846  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
847  * uses QString::fromLatin1() for toString(). Explicitly convert the
848  * value using QString::fromUtf8() to prevent corruption. */
849  dirname = QString::fromUtf8(query.value(0)
850  .toByteArray().constData());
851  groups += MythCoreContext::GenMythURL(query.value(1).toString(),
852  0,
853  dirname,
854  groupname);
855  }
856  }
857 
858  groups.sort();
859  return groups;
860 }
861 
863 {
864  QMutexLocker locker(&s_groupToUseLock);
865  s_groupToUseCache.clear();
866 }
867 
869  const QString &host, const QString &sgroup)
870 {
871  QString tmpGroup = sgroup;
872  QString groupKey = QString("%1:%2").arg(sgroup).arg(host);
873 
874  QMutexLocker locker(&s_groupToUseLock);
875 
876  if (s_groupToUseCache.contains(groupKey))
877  {
878  tmpGroup = s_groupToUseCache[groupKey];
879  }
880  else
881  {
882  if (StorageGroup::FindDirs(sgroup, host))
883  {
884  s_groupToUseCache[groupKey] = sgroup;
885  }
886  else
887  {
888  LOG(VB_FILE, LOG_DEBUG,
889  QString("GetGroupToUse(): "
890  "falling back to Videos Storage Group for host %1 "
891  "since it does not have a %2 Storage Group.")
892  .arg(host).arg(sgroup));
893 
894  tmpGroup = "Videos";
895  s_groupToUseCache[groupKey] = tmpGroup;
896  }
897  }
898 
899  return tmpGroup;
900 }
901 
902 /* 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.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)
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
#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)
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