MythTV  master
storagegroup.cpp
Go to the documentation of this file.
1 #include <QDir>
2 #include <QFile>
3 #include <QUrl>
4 
5 #include "storagegroup.h"
6 #include "mythcorecontext.h"
7 #include "mythdb.h"
8 #include "mythlogging.h"
9 #include "mythcoreutil.h"
10 #include "mythdirs.h"
11 
12 #define LOC QString("SG(%1): ").arg(m_groupname)
13 
14 const char *StorageGroup::kDefaultStorageDir = "/mnt/store";
15 
18 QMap<QString, QString> StorageGroup::m_builtinGroups;
20 QHash<QString,QString> StorageGroup::s_groupToUseCache;
21 
22 const QStringList StorageGroup::kSpecialGroups = QStringList()
23  << QT_TRANSLATE_NOOP("(StorageGroups)", "LiveTV")
24 // << "Thumbnails"
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")
35  ;
36 
37 /****************************************************************************/
38 
47 StorageGroup::StorageGroup(QString group, QString hostname,
48  bool allowFallback) :
49  m_groupname(std::move(group)), m_hostname(std::move(hostname)),
50  m_allowFallback(allowFallback)
51 {
52  m_dirlist.clear();
53 
54  if (qEnvironmentVariableIsSet("MYTHTV_NOSGFALLBACK"))
55  m_allowFallback = false;
56 
58 }
59 
61 {
62  QMutexLocker locker(&m_staticInitLock);
63 
64  if (m_staticInitDone)
65  return;
66 
67  m_staticInitDone = true;
68 
69  m_builtinGroups["ChannelIcons"] = GetConfDir() + "/channels";
70  m_builtinGroups["Themes"] = GetConfDir() + "/themes";
71  m_builtinGroups["Temp"] = GetConfDir() + "/tmp";
72  m_builtinGroups["Streaming"] = GetConfDir() + "/tmp/hls";
73  m_builtinGroups["3rdParty"] = GetConfDir() + "/3rdParty";
74 
75  // NOLINTNEXTLINE(modernize-loop-convert)
76  for (auto it = m_builtinGroups.begin(); it != m_builtinGroups.end(); ++it)
77  {
78  QDir qdir(it.value());
79  if (!qdir.exists())
80  qdir.mkpath(it.value());
81 
82  if (!qdir.exists())
83  {
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()));
88  }
89  }
90 }
91 
106 void StorageGroup::Init(const QString &group, const QString &hostname,
107  const bool allowFallback)
108 {
109  m_groupname = group;
111  m_allowFallback = allowFallback;
112  m_dirlist.clear();
113 
114  StaticInit();
115 
116  bool found = FindDirs(m_groupname, m_hostname, &m_dirlist);
117 
118  if (!found && m_builtinGroups.contains(group))
119  {
120  QDir testdir(m_builtinGroups[group]);
121  if (!testdir.exists())
122  testdir.mkpath(m_builtinGroups[group]);
123 
124  if (testdir.exists())
125  {
126  m_dirlist.prepend(testdir.absolutePath());
127  found = true;
128  }
129  }
130 
131  if ((!found) && m_allowFallback && (m_groupname != "LiveTV") &&
132  (!hostname.isEmpty()))
133  {
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));
138  found = FindDirs(m_groupname, "", &m_dirlist);
139  if (found)
140  {
141  m_hostname = "";
142  }
143  }
144  if ((!found) && m_allowFallback && (group != "Default"))
145  {
146  LOG(VB_FILE, LOG_NOTICE, LOC +
147  QString("Unable to find storage group '%1', trying "
148  "'Default' group!").arg(group));
149  found = FindDirs("Default", m_hostname, &m_dirlist);
150  if(found)
151  {
152  m_groupname = "Default";
153  }
154  else if (!hostname.isEmpty())
155  {
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));
160  found = FindDirs("Default", "", &m_dirlist);
161  if(found)
162  {
163  m_groupname = "Default";
164  m_hostname = "";
165  }
166  }
167  }
168 
169  if (allowFallback && m_dirlist.empty())
170  {
171  QString msg = "Unable to find any Storage Group Directories. ";
172  QString tmpDir = gCoreContext->GetSetting("RecordFilePrefix");
173  if (tmpDir != "")
174  {
175  msg += QString("Using old 'RecordFilePrefix' value of '%1'")
176  .arg(tmpDir);
177  }
178  else
179  {
180  tmpDir = kDefaultStorageDir;
181  msg += QString("Using hardcoded default value of '%1'")
182  .arg(kDefaultStorageDir);
183  }
184  LOG(VB_GENERAL, LOG_ERR, LOC + msg);
185  m_dirlist << tmpDir;
186  }
187 }
188 
189 QString StorageGroup::GetFirstDir(bool appendSlash) const
190 {
191  if (m_dirlist.isEmpty())
192  return {};
193 
194  QString tmp = m_dirlist[0];
195 
196  if (appendSlash)
197  tmp += "/";
198 
199  return tmp;
200 }
201 
202 QStringList StorageGroup::GetDirFileList(const QString &dir,
203  const QString &lbase,
204  bool recursive, bool onlyDirs)
205 {
206  QStringList files;
207  QString base = lbase;
208  QDir d(dir);
209 
210  if (!d.exists())
211  return files;
212 
213  if (base.split("/").size() > 20)
214  {
215  LOG(VB_GENERAL, LOG_ERR, LOC + "GetDirFileList(), 20 levels deep, "
216  "possible directory loop detected.");
217  return files;
218  }
219 
220  if (!base.isEmpty())
221  base += "/";
222 
223  if (recursive)
224  {
225  QStringList list =
226  d.entryList(QDir::Dirs|QDir::NoDotAndDotDot|QDir::Readable);
227 
228  for (const auto& p : qAsConst(list))
229  {
230  LOG(VB_FILE, LOG_DEBUG, LOC +
231  QString("GetDirFileList: Dir: %1/%2").arg(base, p));
232 
233  if (onlyDirs)
234  files.append(base + p);
235 
236  files << GetDirFileList(dir + "/" + p, base + p, true, onlyDirs);
237  }
238  }
239 
240  if (!onlyDirs)
241  {
242  QStringList list = d.entryList(QDir::Files|QDir::Readable);
243  for (const auto& p : qAsConst(list))
244  {
245  LOG(VB_FILE, LOG_DEBUG, LOC +
246  QString("GetDirFileList: File: %1%2").arg(base, p));
247  if (recursive)
248  files.append(base + p);
249  else
250  files.append(p);
251  }
252  }
253  return files;
254 }
255 
256 QStringList StorageGroup::GetDirList(const QString &Path, bool recursive)
257 {
258  QStringList files;
259  QString tmpDir;
260  QDir d;
261  for (const auto& dir : qAsConst(m_dirlist))
262  {
263  tmpDir = dir + Path;
264  d.setPath(tmpDir);
265  if (d.exists())
266  files << GetDirFileList(tmpDir, Path, recursive, true);
267  }
268  return files;
269 }
270 
271 QStringList StorageGroup::GetFileList(const QString &Path, bool recursive)
272 {
273  QStringList files;
274  QString tmpDir;
275  QDir d;
276 
277  for (const auto& dir : qAsConst(m_dirlist))
278  {
279  tmpDir = dir + Path;
280 
281  d.setPath(tmpDir);
282  if (d.exists())
283  files << GetDirFileList(tmpDir, Path, recursive, false);
284  }
285 
286  return files;
287 }
288 
289 QStringList StorageGroup::GetFileInfoList(const QString &Path)
290 {
291  QStringList files;
292  QString relPath;
293  bool badPath = true;
294 
295  if (Path.isEmpty() || Path == "/")
296  {
297  for (const auto& dir : qAsConst(m_dirlist))
298  files << QString("sgdir::%1").arg(dir);
299 
300  return files;
301  }
302 
303  for (const auto& dir : qAsConst(m_dirlist))
304  {
305  if (Path.startsWith(dir))
306  {
307  relPath = Path;
308  relPath.replace(dir,"");
309  if (relPath.startsWith("/"))
310  relPath.replace(0,1,"");
311  badPath = false;
312  }
313  }
314 
315  LOG(VB_FILE, LOG_INFO, LOC +
316  QString("GetFileInfoList: Reading '%1'").arg(Path));
317 
318  if (badPath)
319  return files;
320 
321  QDir d(Path);
322  if (!d.exists())
323  return files;
324 
325  d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
326  QFileInfoList list = d.entryInfoList();
327  if (list.isEmpty())
328  return files;
329 
330  for (const auto& entry : qAsConst(list))
331  {
332  if (entry.fileName() == "Thumbs.db")
333  continue;
334 
335  QString tmp;
336 
337  if (entry.isDir())
338  tmp = QString("dir::%1::0").arg(entry.fileName());
339  else
340  {
341  tmp = QString("file::%1::%2::%3%4").arg(entry.fileName())
342  .arg(entry.size())
343  .arg(relPath, entry.fileName());
344 
345  }
346  LOG(VB_FILE, LOG_DEBUG, LOC +
347  QString("GetFileInfoList: (%1)").arg(tmp));
348  files.append(tmp);
349  }
350 
351  return files;
352 }
353 
354 bool StorageGroup::FileExists(const QString &filename)
355 {
356  LOG(VB_FILE, LOG_DEBUG, LOC +
357  QString("FileExist: Testing for '%1'").arg(filename));
358  bool badPath = true;
359 
360  if (filename.isEmpty())
361  return false;
362 
363  for (const auto & dir : qAsConst(m_dirlist))
364  {
365  if (filename.startsWith(dir))
366  {
367  badPath = false;
368  }
369  }
370 
371  if (badPath)
372  return false;
373 
374  return QFile::exists(filename);
375 }
376 
377 
378 // Returns a string list of details about the file
379 // in the order FILENAME, DATE, SIZE
380 QStringList StorageGroup::GetFileInfo(const QString &lfilename)
381 {
382  QString filename = lfilename;
383  LOG(VB_FILE, LOG_DEBUG, LOC +
384  QString("GetFileInfo: For '%1'") .arg(filename));
385 
386  QStringList details;
387  bool searched = false;
388 
389  if (!FileExists(filename))
390  {
391  searched = true;
393  }
394 
395  if ((searched && !filename.isEmpty()) ||
396  (FileExists(filename)))
397  {
398  QFileInfo fInfo(filename);
399 
400  details << filename;
401  if (fInfo.lastModified().isValid()) {
402  details << QString("%1").arg(fInfo.lastModified().toSecsSinceEpoch());
403  } else {
404  details << QString::number(UINT_MAX);
405  }
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, 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 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
480  QStringList videodirs = videostartupdir.split(':',
481  QString::SkipEmptyParts);
482 #else
483  QStringList videodirs = videostartupdir.split(':',
484  Qt::SkipEmptyParts);
485 #endif
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, 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, 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;
542  MSqlQuery query(MSqlQuery::InitCon());
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 = dirname.trimmed();
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], 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], 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  dirname = dirname.trimmed();
759 
760  LOG(VB_FILE, LOG_DEBUG, LOC +
761  QString("Checking directory '%1' in group '%2'.")
762  .arg(dirname, m_groupname));
763 
764  testDir.setPath(dirname);
765  if (!testDir.exists())
766  {
767  LOG(VB_FILE, LOG_WARNING, LOC +
768  QString("Group '%1' references directory '%2' but this "
769  "directory does not exist. This directory "
770  "will not be used on this server.")
771  .arg(m_groupname, dirname));
772  }
773  else
774  {
775  testFile.setFileName(dirname + "/.test");
776  if (testFile.open(QIODevice::WriteOnly))
777  testFile.remove();
778  else
779  {
780  LOG(VB_GENERAL, LOG_ERR, LOC +
781  QString("Group '%1' wants to use directory '%2', but "
782  "this directory is not writeable.")
783  .arg(m_groupname, dirname));
784  }
785  }
786  }
787 }
788 
790 {
791  QStringList groups;
792 
793  MSqlQuery query(MSqlQuery::InitCon());
794 
795  QString sql = "SELECT DISTINCT groupname "
796  "FROM storagegroup "
797  "WHERE groupname NOT IN (";
798  for (const auto& group : qAsConst(StorageGroup::kSpecialGroups))
799  sql.append(QString(" '%1',").arg(group));
800  sql = sql.left(sql.length() - 1);
801  sql.append(" );");
802 
803  query.prepare(sql);
804  if (query.exec())
805  {
806  while (query.next())
807  {
808  groups += query.value(0).toString();
809  }
810  }
811 
812  groups.sort();
813  return groups;
814 }
815 
816 QStringList StorageGroup::getGroupDirs(const QString &groupname,
817  const QString &host)
818 {
819  QStringList groups;
820  QString addHost;
821 
822  MSqlQuery query(MSqlQuery::InitCon());
823 
824  if (!host.isEmpty())
825  addHost = " AND hostname = :HOSTNAME";
826  else
827  addHost = "";
828 
829  QString sql = QString("SELECT dirname,hostname "
830  "FROM storagegroup "
831  "WHERE groupname = :GROUPNAME %1").arg(addHost);
832 
833  query.prepare(sql);
834  query.bindValue(":GROUPNAME", groupname);
835 
836  if (!host.isEmpty())
837  query.bindValue(":HOSTNAME", host);
838 
839  if (query.exec())
840  {
841  QString dirname;
842  while (query.next())
843  {
844  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
845  * uses QString::fromLatin1() for toString(). Explicitly convert the
846  * value using QString::fromUtf8() to prevent corruption. */
847  dirname = QString::fromUtf8(query.value(0)
848  .toByteArray().constData());
849  groups += MythCoreContext::GenMythURL(query.value(1).toString(),
850  0,
851  dirname,
852  groupname);
853  }
854  }
855 
856  groups.sort();
857  return groups;
858 }
859 
861 {
862  QMutexLocker locker(&s_groupToUseLock);
863  s_groupToUseCache.clear();
864 }
865 
867  const QString &host, const QString &sgroup)
868 {
869  QString tmpGroup = sgroup;
870  QString groupKey = QString("%1:%2").arg(sgroup, host);
871 
872  QMutexLocker locker(&s_groupToUseLock);
873 
874  if (s_groupToUseCache.contains(groupKey))
875  {
876  tmpGroup = s_groupToUseCache[groupKey];
877  }
878  else
879  {
880  if (StorageGroup::FindDirs(sgroup, host))
881  {
882  s_groupToUseCache[groupKey] = sgroup;
883  }
884  else
885  {
886  LOG(VB_FILE, LOG_DEBUG,
887  QString("GetGroupToUse(): "
888  "falling back to Videos Storage Group for host %1 "
889  "since it does not have a %2 Storage Group.")
890  .arg(host, sgroup));
891 
892  tmpGroup = "Videos";
893  s_groupToUseCache[groupKey] = tmpGroup;
894  }
895  }
896 
897  return tmpGroup;
898 }
899 
900 /* vim: set expandtab tabstop=4 shiftwidth=4: */
StorageGroup::getRecordingsGroups
static QStringList getRecordingsGroups(void)
Definition: storagegroup.cpp:789
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:216
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:811
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
StorageGroup::s_groupToUseLock
static QMutex s_groupToUseLock
Definition: storagegroup.h:68
mythdb.h
getDiskSpace
int64_t getDiskSpace(const QString &file_on_disk, int64_t &total, int64_t &used)
Definition: mythcoreutil.cpp:33
StorageGroup::GetDirFileList
QStringList GetDirFileList(const QString &dir, const QString &base, bool recursive=false, bool onlyDirs=false)
Definition: storagegroup.cpp:202
StorageGroup::FindFile
QString FindFile(const QString &filename)
Definition: storagegroup.cpp:602
mythcoreutil.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
StorageGroup::m_hostname
QString m_hostname
Definition: storagegroup.h:62
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:617
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
mythdirs.h
StorageGroup::m_allowFallback
bool m_allowFallback
Definition: storagegroup.h:63
StorageGroup::FileExists
bool FileExists(const QString &filename)
Definition: storagegroup.cpp:354
StorageGroup::ClearGroupToUseCache
static void ClearGroupToUseCache(void)
Definition: storagegroup.cpp:860
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
StorageGroup::m_staticInitLock
static QMutex m_staticInitLock
Definition: storagegroup.h:59
StorageGroup::FindFileDir
QString FindFileDir(const QString &filename)
Definition: storagegroup.cpp:625
StorageGroup::StorageGroup
StorageGroup(QString group="", QString hostname="", bool allowFallback=true)
StorageGroup constructor.
Definition: storagegroup.cpp:47
StorageGroup::GetFileList
QStringList GetFileList(const QString &Path, bool recursive=false)
Definition: storagegroup.cpp:271
mythlogging.h
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:758
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
StorageGroup::StaticInit
static void StaticInit(void)
Definition: storagegroup.cpp:60
StorageGroup::m_staticInitDone
static bool m_staticInitDone
Definition: storagegroup.h:58
StorageGroup::FindDirs
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.
Definition: storagegroup.cpp:537
hardwareprofile.config.p
p
Definition: config.py:33
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:549
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:227
StorageGroup::GetFileInfo
QStringList GetFileInfo(const QString &filename)
Definition: storagegroup.cpp:380
StorageGroup::getGroupDirs
static QStringList getGroupDirs(const QString &groupname, const QString &host)
Definition: storagegroup.cpp:816
StorageGroup::GetFirstDir
QString GetFirstDir(bool appendSlash=false) const
Definition: storagegroup.cpp:189
storagegroup.h
StorageGroup::GetGroupToUse
static QString GetGroupToUse(const QString &host, const QString &sgroup)
Definition: storagegroup.cpp:866
StorageGroup::GetDirList
QStringList GetDirList(void) const
Definition: storagegroup.h:23
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
StorageGroup::GetRelativePathname
static QString GetRelativePathname(const QString &filename)
Returns the relative pathname of a file by comparing the filename against all Storage Group directori...
Definition: storagegroup.cpp:420
StorageGroup::kDefaultStorageDir
static const char * kDefaultStorageDir
Definition: storagegroup.h:45
LOC
#define LOC
Definition: storagegroup.cpp:12
mythcorecontext.h
StorageGroup::FindNextDirMostFree
QString FindNextDirMostFree(void)
Definition: storagegroup.cpp:671
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:887
std
Definition: mythchrono.h:23
StorageGroup::m_dirlist
QStringList m_dirlist
Definition: storagegroup.h:64
StorageGroup::Init
void Init(const QString &group="Default", const QString &hostname="", bool allowFallback=true)
Initilizes the groupname, hostname, and dirlist.
Definition: storagegroup.cpp:106
StorageGroup::GetFileInfoList
QStringList GetFileInfoList(const QString &Path)
Definition: storagegroup.cpp:289
StorageGroup::m_groupname
QString m_groupname
Definition: storagegroup.h:61
StorageGroup::kSpecialGroups
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
StorageGroup
Definition: storagegroup.h:11
StorageGroup::m_builtinGroups
static QMap< QString, QString > m_builtinGroups
Definition: storagegroup.h:66
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:836
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
d
static const iso6937table * d
Definition: iso6937tables.cpp:1025
build_compdb.filename
filename
Definition: build_compdb.py:21
StorageGroup::CheckAllStorageGroupDirs
static void CheckAllStorageGroupDirs(void)
Definition: storagegroup.cpp:729
StorageGroup::s_groupToUseCache
static QHash< QString, QString > s_groupToUseCache
Definition: storagegroup.h:69
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:896
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:836