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(const QString &group, const QString &hostname,
49  bool allowFallback) :
50  m_groupname(group), m_hostname(hostname), m_allowFallback(allowFallback)
51 {
52  m_dirlist.clear();
53 
54  if (getenv("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  QMap<QString, QString>::iterator it = m_builtinGroups.begin();
76  for (; 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  LOG(VB_GENERAL, LOG_ERR,
84  QString("SG() Error: Could not create builtin"
85  "Storage Group directory '%1' for '%2'").arg(it.value())
86  .arg(it.key()));
87  }
88 }
89 
104 void StorageGroup::Init(const QString &group, const QString &hostname,
105  const bool allowFallback)
106 {
107  m_groupname = group;
109  m_allowFallback = allowFallback;
110  m_dirlist.clear();
111 
112  StaticInit();
113 
114  bool found = FindDirs(m_groupname, m_hostname, &m_dirlist);
115 
116  if (!found && m_builtinGroups.contains(group))
117  {
118  QDir testdir(m_builtinGroups[group]);
119  if (!testdir.exists())
120  testdir.mkpath(m_builtinGroups[group]);
121 
122  if (testdir.exists())
123  {
124  m_dirlist.prepend(testdir.absolutePath());
125  found = true;
126  }
127  }
128 
129  if ((!found) && m_allowFallback && (m_groupname != "LiveTV") &&
130  (!hostname.isEmpty()))
131  {
132  LOG(VB_FILE, LOG_NOTICE, LOC +
133  QString("Unable to find any directories for the local "
134  "storage group '%1' on '%2', trying directories on "
135  "all hosts!").arg(group).arg(hostname));
136  found = FindDirs(m_groupname, "", &m_dirlist);
137  if (found)
138  {
139  m_hostname = "";
140  }
141  }
142  if ((!found) && m_allowFallback && (group != "Default"))
143  {
144  LOG(VB_FILE, LOG_NOTICE, LOC +
145  QString("Unable to find storage group '%1', trying "
146  "'Default' group!").arg(group));
147  found = FindDirs("Default", m_hostname, &m_dirlist);
148  if(found)
149  {
150  m_groupname = "Default";
151  }
152  else if (!hostname.isEmpty())
153  {
154  LOG(VB_FILE, LOG_NOTICE, LOC +
155  QString("Unable to find any directories for the local "
156  "Default storage group on '%1', trying directories "
157  "in all Default groups!").arg(hostname));
158  found = FindDirs("Default", "", &m_dirlist);
159  if(found)
160  {
161  m_groupname = "Default";
162  m_hostname = "";
163  }
164  }
165  }
166 
167  if (allowFallback && !m_dirlist.size())
168  {
169  QString msg = "Unable to find any Storage Group Directories. ";
170  QString tmpDir = gCoreContext->GetSetting("RecordFilePrefix");
171  if (tmpDir != "")
172  {
173  msg += QString("Using old 'RecordFilePrefix' value of '%1'")
174  .arg(tmpDir);
175  }
176  else
177  {
178  tmpDir = kDefaultStorageDir;
179  msg += QString("Using hardcoded default value of '%1'")
180  .arg(kDefaultStorageDir);
181  }
182  LOG(VB_GENERAL, LOG_ERR, LOC + msg);
183  m_dirlist << tmpDir;
184  }
185 }
186 
187 QString StorageGroup::GetFirstDir(bool appendSlash) const
188 {
189  if (m_dirlist.isEmpty())
190  return QString();
191 
192  QString tmp = m_dirlist[0];
193 
194  if (appendSlash)
195  tmp += "/";
196 
197  return tmp;
198 }
199 
200 QStringList StorageGroup::GetDirFileList(const QString &dir,
201  const QString &lbase,
202  bool recursive, bool onlyDirs)
203 {
204  QStringList files;
205  QString base = lbase;
206  QDir d(dir);
207 
208  if (!d.exists())
209  return files;
210 
211  if (base.split("/").size() > 20)
212  {
213  LOG(VB_GENERAL, LOG_ERR, LOC + "GetDirFileList(), 20 levels deep, "
214  "possible directory loop detected.");
215  return files;
216  }
217 
218  if (!base.isEmpty())
219  base += "/";
220 
221  if (recursive)
222  {
223  QStringList list =
224  d.entryList(QDir::Dirs|QDir::NoDotAndDotDot|QDir::Readable);
225 
226  for (QStringList::iterator p = list.begin(); p != list.end(); ++p)
227  {
228  LOG(VB_FILE, LOG_DEBUG, LOC +
229  QString("GetDirFileList: Dir: %1/%2").arg(base).arg(*p));
230 
231  if (onlyDirs)
232  files.append(base + *p);
233 
234  files << GetDirFileList(dir + "/" + *p, base + *p, true, onlyDirs);
235  }
236  }
237 
238  if (!onlyDirs)
239  {
240  QStringList list = d.entryList(QDir::Files|QDir::Readable);
241  for (QStringList::iterator p = list.begin(); p != list.end(); ++p)
242  {
243  LOG(VB_FILE, LOG_DEBUG, LOC +
244  QString("GetDirFileList: File: %1%2").arg(base).arg(*p));
245  if (recursive)
246  files.append(base + *p);
247  else
248  files.append(*p);
249  }
250  }
251  return files;
252 }
253 
254 QStringList StorageGroup::GetDirList(const QString &Path, bool recursive)
255 {
256  QStringList files;
257  QString tmpDir;
258  QDir d;
259  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
260  {
261  tmpDir = *it + Path;
262  d.setPath(tmpDir);
263  if (d.exists())
264  files << GetDirFileList(tmpDir, Path, recursive, true);
265  }
266  return files;
267 }
268 
269 QStringList StorageGroup::GetFileList(const QString &Path, bool recursive)
270 {
271  QStringList files;
272  QString tmpDir;
273  QDir d;
274 
275  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
276  {
277  tmpDir = *it + Path;
278 
279  d.setPath(tmpDir);
280  if (d.exists())
281  files << GetDirFileList(tmpDir, Path, recursive, false);
282  }
283 
284  return files;
285 }
286 
287 QStringList StorageGroup::GetFileInfoList(const QString &Path)
288 {
289  QStringList files;
290  QString relPath;
291  bool badPath = true;
292 
293  if (Path.isEmpty() || Path == "/")
294  {
295  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
296  files << QString("sgdir::%1").arg(*it);
297 
298  return files;
299  }
300 
301  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
302  {
303  if (Path.startsWith(*it))
304  {
305  relPath = Path;
306  relPath.replace(*it,"");
307  if (relPath.startsWith("/"))
308  relPath.replace(0,1,"");
309  badPath = false;
310  }
311  }
312 
313  LOG(VB_FILE, LOG_INFO, LOC +
314  QString("GetFileInfoList: Reading '%1'").arg(Path));
315 
316  if (badPath)
317  return files;
318 
319  QDir d(Path);
320  if (!d.exists())
321  return files;
322 
323  d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
324  QFileInfoList list = d.entryInfoList();
325  if (list.isEmpty())
326  return files;
327 
328  for (QFileInfoList::iterator p = list.begin(); p != list.end(); ++p)
329  {
330  if (p->fileName() == "Thumbs.db")
331  continue;
332 
333  QString tmp;
334 
335  if (p->isDir())
336  tmp = QString("dir::%1::0").arg(p->fileName());
337  else
338  tmp = QString("file::%1::%2::%3%4").arg(p->fileName()).arg(p->size())
339  .arg(relPath).arg(p->fileName());
340 
341  LOG(VB_FILE, LOG_DEBUG, LOC +
342  QString("GetFileInfoList: (%1)").arg(tmp));
343  files.append(tmp);
344  }
345 
346  return files;
347 }
348 
349 bool StorageGroup::FileExists(const QString &filename)
350 {
351  LOG(VB_FILE, LOG_DEBUG, LOC +
352  QString("FileExist: Testing for '%1'").arg(filename));
353  bool badPath = true;
354 
355  if (filename.isEmpty())
356  return false;
357 
358  for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
359  {
360  if (filename.startsWith(*it))
361  {
362  badPath = false;
363  }
364  }
365 
366  if (badPath)
367  return false;
368 
369  bool result = false;
370 
371  QFile checkFile(filename);
372  if (checkFile.exists(filename))
373  result = true;
374 
375  return result;
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;
393  filename = FindFile(filename);
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)-1);
409  }
410 #endif
411  details << QString("%1").arg(fInfo.size());
412  }
413 
414  return details;
415 }
416 
425 QString StorageGroup::GetRelativePathname(const QString &filename)
426 {
427  QString result = filename;
428  MSqlQuery query(MSqlQuery::InitCon());
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  QString directory;
487  for (QStringList::Iterator it = videodirs.begin();
488  it != videodirs.end(); ++it)
489  {
490  directory = *it;
491  if (filename.startsWith(directory))
492  {
493  result = filename;
494  result.replace(0, directory.length(), "");
495  if (result.startsWith("/"))
496  result.replace(0, 1, "");
497 
498  LOG(VB_FILE, LOG_DEBUG,
499  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
500  .arg(filename).arg(result));
501  return result;
502  }
503  }
504  }
505  }
506 
507  QMap<QString, QString>::iterator it = m_builtinGroups.begin();
508  for (; it != m_builtinGroups.end(); ++it)
509  {
510  QDir qdir(it.value());
511  if (!qdir.exists())
512  qdir.mkpath(it.value());
513 
514  QString directory = it.value();
515  if (filename.startsWith(directory))
516  {
517  result = filename;
518  result.replace(0, directory.length(), "");
519  if (result.startsWith("/"))
520  result.replace(0, 1, "");
521 
522  LOG(VB_FILE, LOG_DEBUG,
523  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
524  .arg(filename).arg(result));
525  return result;
526  }
527  }
528 
529  return result;
530 }
531 
541 bool StorageGroup::FindDirs(const QString &group, const QString &hostname,
542  QStringList *dirlist)
543 {
544  bool found = false;
545  QString dirname;
546  MSqlQuery query(MSqlQuery::InitCon());
547 
548  StaticInit();
549 
550  QString sql = "SELECT DISTINCT dirname "
551  "FROM storagegroup ";
552 
553  if (!group.isEmpty())
554  {
555  sql.append("WHERE groupname = :GROUP");
556  if (!hostname.isEmpty())
557  sql.append(" AND hostname = :HOSTNAME");
558  }
559 
560  query.prepare(sql);
561  if (!group.isEmpty())
562  {
563  query.bindValue(":GROUP", group);
564  if (!hostname.isEmpty())
565  query.bindValue(":HOSTNAME", hostname);
566  }
567 
568  if (!query.exec() || !query.isActive())
569  MythDB::DBError("StorageGroup::StorageGroup()", query);
570  else if (query.next())
571  {
572  do
573  {
574  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
575  * uses QString::fromLatin1() for toString(). Explicitly convert the
576  * value using QString::fromUtf8() to prevent corruption. */
577  dirname = QString::fromUtf8(query.value(0)
578  .toByteArray().constData());
579  dirname.replace(QRegExp("^\\s*"), "");
580  dirname.replace(QRegExp("\\s*$"), "");
581  if (dirname.endsWith("/"))
582  dirname.remove(dirname.length() - 1, 1);
583 
584  if (dirlist)
585  (*dirlist) << dirname;
586  else
587  return true;
588  }
589  while (query.next());
590  found = true;
591  }
592 
593  if (m_builtinGroups.contains(group))
594  {
595  QDir testdir(m_builtinGroups[group]);
596  if (testdir.exists())
597  {
598  if (dirlist && !dirlist->contains(testdir.absolutePath()))
599  (*dirlist) << testdir.absolutePath();
600  found = true;
601  }
602  }
603 
604  return found;
605 }
606 
607 QString StorageGroup::FindFile(const QString &filename)
608 {
609  LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindFile: Searching for '%1'")
610  .arg(filename));
611 
612  QString recDir = FindFileDir(filename);
613  QString result = "";
614 
615  if (!recDir.isEmpty())
616  {
617  result = recDir + "/" + filename;
618  LOG(VB_FILE, LOG_INFO, LOC +
619  QString("FindFile: Found '%1'") .arg(result));
620  }
621  else
622  {
623  LOG(VB_FILE, LOG_ERR, LOC +
624  QString("FindFile: Unable to find '%1'!") .arg(filename));
625  }
626 
627  return result;
628 }
629 
630 QString StorageGroup::FindFileDir(const QString &filename)
631 {
632  QString result = "";
633  QFileInfo checkFile("");
634 
635  int curDir = 0;
636  while (curDir < m_dirlist.size())
637  {
638  QString testFile = m_dirlist[curDir] + "/" + filename;
639  LOG(VB_FILE, LOG_DEBUG, LOC +
640  QString("FindFileDir: Checking '%1' for '%2'")
641  .arg(m_dirlist[curDir]).arg(testFile));
642  checkFile.setFile(testFile);
643  if (checkFile.exists() || checkFile.isSymLink())
644  return m_dirlist[curDir];
645 
646  curDir++;
647  }
648 
649  if (m_groupname.isEmpty() || (m_allowFallback == false))
650  {
651  // Not found in any dir, so try RecordFilePrefix if it exists
652  QString tmpFile =
653  gCoreContext->GetSetting("RecordFilePrefix") + "/" + filename;
654  checkFile.setFile(tmpFile);
655  if (checkFile.exists() || checkFile.isSymLink())
656  result = tmpFile;
657  }
658  else if (m_groupname != "Default")
659  {
660  // Not found in current group so try Default
661  StorageGroup sgroup("Default");
662  QString tmpFile = sgroup.FindFileDir(filename);
663  result = (tmpFile.isEmpty()) ? result : tmpFile;
664  }
665  else
666  {
667  // Not found in Default so try any dir
668  StorageGroup sgroup;
669  QString tmpFile = sgroup.FindFileDir(filename);
670  result = (tmpFile.isEmpty()) ? result : tmpFile;
671  }
672 
673  return result;
674 }
675 
677 {
678  QString nextDir;
679  int64_t nextDirFree = 0;
680  int64_t thisDirTotal;
681  int64_t thisDirUsed;
682  int64_t thisDirFree;
683 
684  LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindNextDirMostFree: Starting"));
685 
686  if (m_allowFallback)
687  nextDir = kDefaultStorageDir;
688 
689  if (m_dirlist.size())
690  nextDir = m_dirlist[0];
691 
692  QDir checkDir("");
693  int curDir = 0;
694  while (curDir < m_dirlist.size())
695  {
696  checkDir.setPath(m_dirlist[curDir]);
697  if (!checkDir.exists())
698  {
699  LOG(VB_GENERAL, LOG_ERR, LOC +
700  QString("FindNextDirMostFree: '%1' does not exist!")
701  .arg(m_dirlist[curDir]));
702  curDir++;
703  continue;
704  }
705 
706  thisDirFree = getDiskSpace(m_dirlist[curDir], thisDirTotal,
707  thisDirUsed);
708  LOG(VB_FILE, LOG_DEBUG, LOC +
709  QString("FindNextDirMostFree: '%1' has %2 KiB free")
710  .arg(m_dirlist[curDir])
711  .arg(QString::number(thisDirFree)));
712 
713  if (thisDirFree > nextDirFree)
714  {
715  nextDir = m_dirlist[curDir];
716  nextDirFree = thisDirFree;
717  }
718  curDir++;
719  }
720 
721  if (nextDir.isEmpty())
722  LOG(VB_FILE, LOG_ERR, LOC +
723  "FindNextDirMostFree: Unable to find any directories to use.");
724  else
725  LOG(VB_FILE, LOG_DEBUG, LOC +
726  QString("FindNextDirMostFree: Using '%1'").arg(nextDir));
727 
728  return nextDir;
729 }
730 
732 {
733  QString m_groupname;
734  QString dirname;
735  MSqlQuery query(MSqlQuery::InitCon());
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  LOG(VB_GENERAL, LOG_ERR, LOC +
784  QString("Group '%1' wants to use directory '%2', but "
785  "this directory is not writeable.")
786  .arg(m_groupname).arg(dirname));
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  for (QStringList::const_iterator it = StorageGroup::kSpecialGroups.begin();
801  it != StorageGroup::kSpecialGroups.end(); ++it)
802  sql.append(QString(" '%1',").arg(*it));
803  sql = sql.left(sql.length() - 1);
804  sql.append(" );");
805 
806  query.prepare(sql);
807  if (query.exec())
808  {
809  while (query.next())
810  {
811  groups += query.value(0).toString();
812  }
813  }
814 
815  groups.sort();
816  return groups;
817 }
818 
819 QStringList StorageGroup::getGroupDirs(const QString &groupname,
820  const QString &host)
821 {
822  QStringList groups;
823  QString addHost;
824 
825  MSqlQuery query(MSqlQuery::InitCon());
826 
827  if (!host.isEmpty())
828  addHost = " AND hostname = :HOSTNAME";
829  else
830  addHost = "";
831 
832  QString sql = QString("SELECT dirname,hostname "
833  "FROM storagegroup "
834  "WHERE groupname = :GROUPNAME %1").arg(addHost);
835 
836  query.prepare(sql);
837  query.bindValue(":GROUPNAME", groupname);
838 
839  if (!host.isEmpty())
840  query.bindValue(":HOSTNAME", host);
841 
842  if (query.exec())
843  {
844  QString dirname;
845  while (query.next())
846  {
847  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
848  * uses QString::fromLatin1() for toString(). Explicitly convert the
849  * value using QString::fromUtf8() to prevent corruption. */
850  dirname = QString::fromUtf8(query.value(0)
851  .toByteArray().constData());
852  groups += gCoreContext->GenMythURL(query.value(1).toString(),
853  0,
854  dirname,
855  groupname);
856  }
857  }
858 
859  groups.sort();
860  return groups;
861 }
862 
864 {
865  QMutexLocker locker(&s_groupToUseLock);
866  s_groupToUseCache.clear();
867 }
868 
870  const QString &host, const QString &sgroup)
871 {
872  QString tmpGroup = sgroup;
873  QString groupKey = QString("%1:%2").arg(sgroup).arg(host);
874 
875  QMutexLocker locker(&s_groupToUseLock);
876 
877  if (s_groupToUseCache.contains(groupKey))
878  {
879  tmpGroup = s_groupToUseCache[groupKey];
880  }
881  else
882  {
883  if (StorageGroup::FindDirs(sgroup, host))
884  {
885  s_groupToUseCache[groupKey] = sgroup;
886  }
887  else
888  {
889  LOG(VB_FILE, LOG_DEBUG,
890  QString("GetGroupToUse(): "
891  "falling back to Videos Storage Group for host %1 "
892  "since it does not have a %2 Storage Group.")
893  .arg(host).arg(sgroup));
894 
895  tmpGroup = "Videos";
896  s_groupToUseCache[groupKey] = tmpGroup;
897  }
898  }
899 
900  return tmpGroup;
901 }
902 
903 /* vim: set expandtab tabstop=4 shiftwidth=4: */
StorageGroup(const QString &group="", const QString &hostname="", const bool allowFallback=true)
StorageGroup constructor.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:794
void bindValue(const QString &placeholder, const QVariant &val)
Definition: mythdbcon.cpp:875
QStringList GetDirList(void) const
Definition: storagegroup.h:23
QString GenMythURL(QString host=QString(), QString port=QString(), QString path=QString(), QString storageGroup=QString())
void Init(const QString &group="Default", const QString &hostname="", const bool allowFallback=true)
Initilizes the groupname, hostname, and dirlist.
QString GetFirstDir(bool appendSlash=false) const
static QHash< QString, QString > s_groupToUseCache
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)
unsigned int uint
Definition: compat.h:140
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:182
static void CheckAllStorageGroupDirs(void)
static const uint16_t * d
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:188
string hostname
Definition: caa.py:17
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:547
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:819
#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:615
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