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.empty())
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  return QFile::exists(filename);
370 }
371 
372 
373 // Returns a string list of details about the file
374 // in the order FILENAME, DATE, SIZE
375 QStringList StorageGroup::GetFileInfo(const QString &lfilename)
376 {
377  QString filename = lfilename;
378  LOG(VB_FILE, LOG_DEBUG, LOC +
379  QString("GetFileInfo: For '%1'") .arg(filename));
380 
381  QStringList details;
382  bool searched = false;
383 
384  if (!FileExists(filename))
385  {
386  searched = true;
387  filename = FindFile(filename);
388  }
389 
390  if ((searched && !filename.isEmpty()) ||
391  (FileExists(filename)))
392  {
393  QFileInfo fInfo(filename);
394 
395  details << filename;
396 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
397  details << QString("%1").arg(fInfo.lastModified().toTime_t());
398 #else
399  if (fInfo.lastModified().isValid()) {
400  details << QString("%1").arg(fInfo.lastModified().toSecsSinceEpoch());
401  } else {
402  details << QString((uint)-1);
403  }
404 #endif
405  details << QString("%1").arg(fInfo.size());
406  }
407 
408  return details;
409 }
410 
419 QString StorageGroup::GetRelativePathname(const QString &filename)
420 {
421  QString result = filename;
422  MSqlQuery query(MSqlQuery::InitCon());
423 
424  LOG(VB_FILE, LOG_DEBUG,
425  QString("StorageGroup::GetRelativePathname(%1)").arg(filename));
426 
427  StaticInit();
428 
429  if (filename.startsWith("myth://"))
430  {
431  QUrl qurl(filename);
432 
433  if (qurl.hasFragment())
434  result = qurl.path() + "#" + qurl.fragment();
435  else
436  result = qurl.path();
437 
438  if (result.startsWith("/"))
439  result.replace(0, 1, "");
440 
441  return result;
442  }
443 
444  query.prepare("SELECT DISTINCT dirname FROM storagegroup "
445  "ORDER BY dirname DESC;");
446  if (query.exec())
447  {
448  QString dirname;
449  while (query.next())
450  {
451  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
452  * uses QString::fromLatin1() for toString(). Explicitly convert the
453  * value using QString::fromUtf8() to prevent corruption. */
454  dirname = QString::fromUtf8(query.value(0)
455  .toByteArray().constData());
456  if (filename.startsWith(dirname))
457  {
458  result = filename;
459  result.replace(0, dirname.length(), "");
460  if (result.startsWith("/"))
461  result.replace(0, 1, "");
462 
463  LOG(VB_FILE, LOG_DEBUG,
464  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
465  .arg(filename).arg(result));
466  return result;
467  }
468  }
469  }
470 
471  query.prepare("SELECT DISTINCT data FROM settings WHERE "
472  "value = 'VideoStartupDir';");
473  if (query.exec())
474  {
475  while (query.next())
476  {
477  QString videostartupdir = query.value(0).toString();
478  QStringList videodirs = videostartupdir.split(':',
479  QString::SkipEmptyParts);
480  QString directory;
481  for (QStringList::Iterator it = videodirs.begin();
482  it != videodirs.end(); ++it)
483  {
484  directory = *it;
485  if (filename.startsWith(directory))
486  {
487  result = filename;
488  result.replace(0, directory.length(), "");
489  if (result.startsWith("/"))
490  result.replace(0, 1, "");
491 
492  LOG(VB_FILE, LOG_DEBUG,
493  QString("StorageGroup::GetRelativePathname(%1) = '%2'")
494  .arg(filename).arg(result));
495  return result;
496  }
497  }
498  }
499  }
500 
501  QMap<QString, QString>::iterator it = m_builtinGroups.begin();
502  for (; it != m_builtinGroups.end(); ++it)
503  {
504  QDir qdir(it.value());
505  if (!qdir.exists())
506  qdir.mkpath(it.value());
507 
508  QString directory = it.value();
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;
675  int64_t thisDirUsed;
676  int64_t thisDirFree;
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  LOG(VB_FILE, LOG_ERR, LOC +
717  "FindNextDirMostFree: Unable to find any directories to use.");
718  else
719  LOG(VB_FILE, LOG_DEBUG, LOC +
720  QString("FindNextDirMostFree: Using '%1'").arg(nextDir));
721 
722  return nextDir;
723 }
724 
726 {
727  QString m_groupname;
728  QString dirname;
729  MSqlQuery query(MSqlQuery::InitCon());
730 
731  query.prepare("SELECT groupname, dirname "
732  "FROM storagegroup "
733  "WHERE hostname = :HOSTNAME;");
734  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
735  if (!query.exec() || !query.isActive())
736  {
737  MythDB::DBError("StorageGroup::CheckAllStorageGroupDirs()", query);
738  return;
739  }
740 
741  LOG(VB_FILE, LOG_DEBUG, LOC +
742  "CheckAllStorageGroupDirs(): Checking All Storage Group directories");
743 
744  QFile testFile("");
745  QDir testDir("");
746  while (query.next())
747  {
748  m_groupname = query.value(0).toString();
749  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
750  * uses QString::fromLatin1() for toString(). Explicitly convert the
751  * value using QString::fromUtf8() to prevent corruption. */
752  dirname = QString::fromUtf8(query.value(1)
753  .toByteArray().constData());
754 
755  dirname.replace(QRegExp("^\\s*"), "");
756  dirname.replace(QRegExp("\\s*$"), "");
757 
758  LOG(VB_FILE, LOG_DEBUG, LOC +
759  QString("Checking directory '%1' in group '%2'.")
760  .arg(dirname).arg(m_groupname));
761 
762  testDir.setPath(dirname);
763  if (!testDir.exists())
764  {
765  LOG(VB_FILE, LOG_WARNING, LOC +
766  QString("Group '%1' references directory '%2' but this "
767  "directory does not exist. This directory "
768  "will not be used on this server.")
769  .arg(m_groupname).arg(dirname));
770  }
771  else
772  {
773  testFile.setFileName(dirname + "/.test");
774  if (testFile.open(QIODevice::WriteOnly))
775  testFile.remove();
776  else
777  LOG(VB_GENERAL, LOG_ERR, LOC +
778  QString("Group '%1' wants to use directory '%2', but "
779  "this directory is not writeable.")
780  .arg(m_groupname).arg(dirname));
781  }
782  }
783 }
784 
786 {
787  QStringList groups;
788 
789  MSqlQuery query(MSqlQuery::InitCon());
790 
791  QString sql = "SELECT DISTINCT groupname "
792  "FROM storagegroup "
793  "WHERE groupname NOT IN (";
794  for (QStringList::const_iterator it = StorageGroup::kSpecialGroups.begin();
795  it != StorageGroup::kSpecialGroups.end(); ++it)
796  sql.append(QString(" '%1',").arg(*it));
797  sql = sql.left(sql.length() - 1);
798  sql.append(" );");
799 
800  query.prepare(sql);
801  if (query.exec())
802  {
803  while (query.next())
804  {
805  groups += query.value(0).toString();
806  }
807  }
808 
809  groups.sort();
810  return groups;
811 }
812 
813 QStringList StorageGroup::getGroupDirs(const QString &groupname,
814  const QString &host)
815 {
816  QStringList groups;
817  QString addHost;
818 
819  MSqlQuery query(MSqlQuery::InitCon());
820 
821  if (!host.isEmpty())
822  addHost = " AND hostname = :HOSTNAME";
823  else
824  addHost = "";
825 
826  QString sql = QString("SELECT dirname,hostname "
827  "FROM storagegroup "
828  "WHERE groupname = :GROUPNAME %1").arg(addHost);
829 
830  query.prepare(sql);
831  query.bindValue(":GROUPNAME", groupname);
832 
833  if (!host.isEmpty())
834  query.bindValue(":HOSTNAME", host);
835 
836  if (query.exec())
837  {
838  QString dirname;
839  while (query.next())
840  {
841  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
842  * uses QString::fromLatin1() for toString(). Explicitly convert the
843  * value using QString::fromUtf8() to prevent corruption. */
844  dirname = QString::fromUtf8(query.value(0)
845  .toByteArray().constData());
846  groups += gCoreContext->GenMythURL(query.value(1).toString(),
847  0,
848  dirname,
849  groupname);
850  }
851  }
852 
853  groups.sort();
854  return groups;
855 }
856 
858 {
859  QMutexLocker locker(&s_groupToUseLock);
860  s_groupToUseCache.clear();
861 }
862 
864  const QString &host, const QString &sgroup)
865 {
866  QString tmpGroup = sgroup;
867  QString groupKey = QString("%1:%2").arg(sgroup).arg(host);
868 
869  QMutexLocker locker(&s_groupToUseLock);
870 
871  if (s_groupToUseCache.contains(groupKey))
872  {
873  tmpGroup = s_groupToUseCache[groupKey];
874  }
875  else
876  {
877  if (StorageGroup::FindDirs(sgroup, host))
878  {
879  s_groupToUseCache[groupKey] = sgroup;
880  }
881  else
882  {
883  LOG(VB_FILE, LOG_DEBUG,
884  QString("GetGroupToUse(): "
885  "falling back to Videos Storage Group for host %1 "
886  "since it does not have a %2 Storage Group.")
887  .arg(host).arg(sgroup));
888 
889  tmpGroup = "Videos";
890  s_groupToUseCache[groupKey] = tmpGroup;
891  }
892  }
893 
894  return tmpGroup;
895 }
896 
897 /* 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:782
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
QStringList GetDirList(void) const
Definition: storagegroup.h:23
void Init(const QString &group="Default", const QString &hostname="", const bool allowFallback=true)
Initilizes the groupname, hostname, and dirlist.
QString GetFirstDir(bool appendSlash=false) const
static QHash< QString, QString > s_groupToUseCache
Definition: storagegroup.h:69
static QMutex m_staticInitLock
Definition: storagegroup.h:59
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
QStringList GetFileInfoList(const QString &Path)
static QStringList getRecordingsGroups(void)
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:198
static void CheckAllStorageGroupDirs(void)
static const uint16_t * d
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=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:807
#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: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