MythTV  0.26-pre
storagegroup.cpp
Go to the documentation of this file.
00001 #include <QDir>
00002 #include <QFile>
00003 #include <QRegExp>
00004 #include <QUrl>
00005 
00006 #include "storagegroup.h"
00007 #include "mythcorecontext.h"
00008 #include "mythdb.h"
00009 #include "mythlogging.h"
00010 #include "mythcoreutil.h"
00011 #include "mythdirs.h"
00012 
00013 #define LOC QString("SG(%1): ").arg(m_groupname)
00014 
00015 const char *StorageGroup::kDefaultStorageDir = "/mnt/store";
00016 
00017 QMutex                 StorageGroup::m_staticInitLock;
00018 bool                   StorageGroup::m_staticInitDone = false;
00019 QMap<QString, QString> StorageGroup::m_builtinGroups;
00020 QMutex                 StorageGroup::s_groupToUseLock;
00021 QHash<QString,QString> StorageGroup::s_groupToUseCache;
00022 
00023 const QStringList StorageGroup::kSpecialGroups = QStringList()
00024     << "LiveTV"
00025 //    << "Thumbnails"
00026     << "DB Backups"
00027     << "Videos"
00028     << "Trailers"
00029     << "Coverart"
00030     << "Fanart"
00031     << "Screenshots"
00032     << "Banners"
00033     ;
00034 
00035 /****************************************************************************/
00036 
00045 StorageGroup::StorageGroup(const QString group, const QString hostname,
00046                            bool allowFallback) :
00047     m_groupname(group), m_hostname(hostname), m_allowFallback(allowFallback)
00048 {
00049     m_groupname.detach();
00050     m_hostname.detach();
00051     m_dirlist.clear();
00052 
00053     if (getenv("MYTHTV_NOSGFALLBACK"))
00054         m_allowFallback = false;
00055 
00056     Init(m_groupname, m_hostname, m_allowFallback);
00057 }
00058 
00059 void StorageGroup::StaticInit(void)
00060 {
00061     QMutexLocker locker(&m_staticInitLock);
00062 
00063     if (m_staticInitDone)
00064         return;
00065 
00066     m_staticInitDone = true;
00067 
00068     m_builtinGroups["ChannelIcons"] = GetConfDir() + "/channels";
00069     m_builtinGroups["Themes"] = GetConfDir() + "/themes";
00070     m_builtinGroups["Temp"] = GetConfDir() + "/tmp";
00071     m_builtinGroups["Streaming"] = GetConfDir() + "/tmp/hls";
00072     m_builtinGroups["3rdParty"] = GetConfDir() + "/3rdParty";
00073 
00074     QMap<QString, QString>::iterator it = m_builtinGroups.begin();
00075     for (; it != m_builtinGroups.end(); ++it)
00076     {
00077         QDir qdir(it.value());
00078         if (!qdir.exists())
00079             qdir.mkpath(it.value());
00080 
00081         if (!qdir.exists())
00082             LOG(VB_GENERAL, LOG_ERR,
00083                 QString("SG() Error: Could not create builtin"
00084                         "Storage Group directory '%1' for '%2'").arg(it.value())
00085                     .arg(it.key()));
00086     }
00087 }
00088 
00101 void StorageGroup::Init(const QString group, const QString hostname,
00102                         const bool allowFallback)
00103 {
00104     bool found = false;
00105     m_groupname = group;    m_groupname.detach();
00106     m_hostname  = hostname; m_hostname.detach();
00107     m_allowFallback = allowFallback;
00108     m_dirlist.clear();
00109 
00110     StaticInit();
00111 
00112     found = FindDirs(m_groupname, m_hostname, &m_dirlist);
00113 
00114     if (!found && m_builtinGroups.contains(group))
00115     {
00116         QDir testdir(m_builtinGroups[group]);
00117         if (!testdir.exists())
00118             testdir.mkpath(m_builtinGroups[group]);
00119 
00120         if (testdir.exists())
00121         {
00122             m_dirlist.prepend(testdir.absolutePath());
00123             found = true;
00124         }
00125     }
00126 
00127     if ((!found) && m_allowFallback && (m_groupname != "LiveTV") &&
00128         (!hostname.isEmpty()))
00129     {
00130         LOG(VB_FILE, LOG_NOTICE, LOC +
00131             QString("Unable to find any directories for the local "
00132                     "storage group '%1' on '%2', trying directories on "
00133                     "all hosts!").arg(group).arg(hostname));
00134         found = FindDirs(m_groupname, "", &m_dirlist);
00135         if (found)
00136         {
00137             m_hostname = "";
00138             m_hostname.detach();
00139         }
00140     }
00141     if ((!found) && m_allowFallback && (group != "Default"))
00142     {
00143         LOG(VB_FILE, LOG_NOTICE, LOC +
00144             QString("Unable to find storage group '%1', trying "
00145                     "'Default' group!").arg(group));
00146         found = FindDirs("Default", m_hostname, &m_dirlist);
00147         if(found)
00148         {
00149             m_groupname = "Default";
00150             m_groupname.detach();
00151         }
00152         else if (!hostname.isEmpty())
00153         {
00154             LOG(VB_FILE, LOG_NOTICE, LOC +
00155                 QString("Unable to find any directories for the local "
00156                         "Default storage group on '%1', trying directories "
00157                         "in all Default groups!").arg(hostname));
00158             found = FindDirs("Default", "", &m_dirlist);
00159             if(found)
00160             {
00161                 m_groupname = "Default";
00162                 m_hostname = "";
00163                 m_groupname.detach();
00164                 m_hostname.detach();
00165             }
00166         }
00167     }
00168 
00169     if (allowFallback && !m_dirlist.size())
00170     {
00171         QString msg = "Unable to find any Storage Group Directories.  ";
00172         QString tmpDir = gCoreContext->GetSetting("RecordFilePrefix");
00173         if (tmpDir != "")
00174         {
00175             msg += QString("Using old 'RecordFilePrefix' value of '%1'")
00176                            .arg(tmpDir);
00177         }
00178         else
00179         {
00180             tmpDir = kDefaultStorageDir;
00181             msg += QString("Using hardcoded default value of '%1'")
00182                            .arg(kDefaultStorageDir);
00183         }
00184         LOG(VB_GENERAL, LOG_ERR, LOC + msg);
00185         m_dirlist << tmpDir;
00186     }
00187 }
00188 
00189 QString StorageGroup::GetFirstDir(bool appendSlash) const
00190 {
00191     if (m_dirlist.isEmpty())
00192         return QString();
00193 
00194     QString tmp = m_dirlist[0];
00195     tmp.detach();
00196 
00197     if (appendSlash)
00198         tmp += "/";
00199 
00200     return tmp;
00201 }
00202 
00203 QStringList StorageGroup::GetDirFileList(QString dir, QString base,
00204                                          bool recursive)
00205 {
00206     QStringList files;
00207     QDir d(dir);
00208 
00209     if (!d.exists())
00210         return files;
00211 
00212     if (base.split("/").size() > 20)
00213     {
00214         LOG(VB_GENERAL, LOG_ERR, LOC + "GetDirFileList(), 20 levels deep, "
00215                                        "possible directory loop detected.");
00216         return files;
00217     }
00218 
00219     if (!base.isEmpty())
00220         base += "/";
00221 
00222     if (recursive)
00223     {
00224         QStringList list =
00225             d.entryList(QDir::Dirs|QDir::NoDotAndDotDot|QDir::Readable);
00226 
00227         for (QStringList::iterator p = list.begin(); p != list.end(); ++p)
00228         {
00229             LOG(VB_FILE, LOG_DEBUG, LOC +
00230                 QString("GetDirFileList: Dir: %1/%2").arg(base).arg(*p));
00231             files << GetDirFileList(dir + "/" + *p, base + *p, true);
00232         }
00233     }
00234 
00235     QStringList list = d.entryList(QDir::Files|QDir::Readable);
00236     for (QStringList::iterator p = list.begin(); p != list.end(); ++p)
00237     {
00238         LOG(VB_FILE, LOG_DEBUG, LOC +
00239             QString("GetDirFileList: File: %1%2").arg(base).arg(*p));
00240         if (recursive)
00241             files.append(base + *p);
00242         else
00243             files.append(*p);
00244     }
00245 
00246     return files;
00247 }
00248 
00249 QStringList StorageGroup::GetFileList(QString Path, bool recursive)
00250 {
00251     QStringList files;
00252     QString tmpDir;
00253     QDir d;
00254 
00255     for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
00256     {
00257         tmpDir = *it + Path;
00258 
00259         d.setPath(tmpDir);
00260         if (d.exists())
00261             files << GetDirFileList(tmpDir, Path, recursive);
00262     }
00263 
00264     return files;
00265 }
00266 
00267 QStringList StorageGroup::GetFileInfoList(QString Path)
00268 {
00269     QStringList files;
00270     QString relPath;
00271     bool badPath = true;
00272 
00273     if (Path.isEmpty() || Path == "/")
00274     {
00275         for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
00276             files << QString("sgdir::%1").arg(*it);
00277 
00278         return files;
00279     }
00280 
00281     for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
00282     {
00283         if (Path.startsWith(*it))
00284         {
00285             relPath = Path;
00286             relPath.replace(*it,"");
00287             if (relPath.startsWith("/"))
00288                 relPath.replace(0,1,"");
00289             badPath = false;
00290         }
00291     }
00292 
00293     LOG(VB_FILE, LOG_INFO, LOC +
00294         QString("GetFileInfoList: Reading '%1'").arg(Path));
00295 
00296     if (badPath)
00297         return files;
00298 
00299     QDir d(Path);
00300     if (!d.exists())
00301         return files;
00302 
00303     QFileInfoList list = d.entryInfoList();
00304     if (!list.size())
00305         return files;
00306 
00307     for (QFileInfoList::iterator p = list.begin(); p != list.end(); ++p)
00308     {
00309         if (p->fileName() == "." ||
00310             p->fileName() == ".." ||
00311             p->fileName() == "Thumbs.db")
00312         {
00313             continue;
00314         }
00315 
00316         QString tmp;
00317 
00318         if (p->isDir())
00319             tmp = QString("dir::%1::0").arg(p->fileName());
00320         else
00321             tmp = QString("file::%1::%2::%3%4").arg(p->fileName()).arg(p->size())
00322                           .arg(relPath).arg(p->fileName());
00323 
00324         LOG(VB_FILE, LOG_DEBUG, LOC +
00325             QString("GetFileInfoList: (%1)").arg(tmp));
00326         files.append(tmp);
00327     }
00328 
00329     return files;
00330 }
00331 
00332 bool StorageGroup::FileExists(QString filename)
00333 {
00334     LOG(VB_FILE, LOG_DEBUG, LOC +
00335         QString("FileExist: Testing for '%1'").arg(filename));
00336     bool badPath = true;
00337 
00338     if (filename.isEmpty())
00339         return false;
00340 
00341     for (QStringList::Iterator it = m_dirlist.begin(); it != m_dirlist.end(); ++it)
00342     {
00343         if (filename.startsWith(*it))
00344         {
00345             badPath = false;
00346         }
00347     }
00348 
00349     if (badPath)
00350         return false;
00351 
00352     bool result = false;
00353 
00354     QFile checkFile(filename);
00355     if (checkFile.exists(filename))
00356         result = true;
00357 
00358     return result;
00359 }
00360 
00361 
00362 // Returns a string list of details about the file
00363 // in the order EXISTS, DATE, SIZE
00364 QStringList StorageGroup::GetFileInfo(QString filename)
00365 {
00366     LOG(VB_FILE, LOG_DEBUG, LOC +
00367         QString("GetFileInfo: For '%1'") .arg(filename));
00368 
00369     QStringList details;
00370     bool searched = false;
00371 
00372     if (!FileExists(filename))
00373     {
00374         searched = true;
00375         filename = FindFile(filename);
00376     }
00377 
00378     if ((searched && !filename.isEmpty()) ||
00379         (FileExists(filename)))
00380     {
00381         QFileInfo fInfo(filename);
00382 
00383         details << filename;
00384         details << QString("%1").arg(fInfo.lastModified().toTime_t());
00385         details << QString("%1").arg(fInfo.size());
00386     }
00387 
00388     return details;
00389 }
00390 
00399 QString StorageGroup::GetRelativePathname(const QString &filename)
00400 {
00401     QString result = filename;
00402     MSqlQuery query(MSqlQuery::InitCon());
00403 
00404     LOG(VB_FILE, LOG_DEBUG,
00405         QString("StorageGroup::GetRelativePathname(%1)").arg(filename));
00406 
00407     StaticInit();
00408 
00409     if (filename.startsWith("myth://"))
00410     {
00411         QUrl qurl(filename);
00412 
00413         if (qurl.hasFragment())
00414             result = qurl.path() + "#" + qurl.fragment();
00415         else
00416             result = qurl.path();
00417 
00418         if (result.startsWith("/"))
00419             result.replace(0, 1, "");
00420 
00421         return result;
00422     }
00423 
00424     query.prepare("SELECT DISTINCT dirname FROM storagegroup "
00425                       "ORDER BY dirname DESC;");
00426     if (query.exec())
00427     {
00428         QString dirname;
00429         while (query.next())
00430         {
00431             /* The storagegroup.dirname column uses utf8_bin collation, so Qt
00432              * uses QString::fromAscii() for toString(). Explicitly convert the
00433              * value using QString::fromUtf8() to prevent corruption. */
00434             dirname = QString::fromUtf8(query.value(0)
00435                                         .toByteArray().constData());
00436             if (filename.startsWith(dirname))
00437             {
00438                 result = filename;
00439                 result.replace(0, dirname.length(), "");
00440                 if (result.startsWith("/"))
00441                     result.replace(0, 1, "");
00442 
00443                 LOG(VB_FILE, LOG_DEBUG,
00444                     QString("StorageGroup::GetRelativePathname(%1) = '%2'")
00445                         .arg(filename).arg(result));
00446                 return result;
00447             }
00448         }
00449     }
00450 
00451     query.prepare("SELECT DISTINCT data FROM settings WHERE "
00452                   "value = 'VideoStartupDir';");
00453     if (query.exec())
00454     {
00455         while (query.next())
00456         {
00457             QString videostartupdir = query.value(0).toString();
00458             QStringList videodirs = videostartupdir.split(':',
00459                                             QString::SkipEmptyParts);
00460             QString directory;
00461             for (QStringList::Iterator it = videodirs.begin();
00462                                        it != videodirs.end(); ++it)
00463             {
00464                 directory = *it;
00465                 if (filename.startsWith(directory))
00466                 {
00467                     result = filename;
00468                     result.replace(0, directory.length(), "");
00469                     if (result.startsWith("/"))
00470                         result.replace(0, 1, "");
00471 
00472                     LOG(VB_FILE, LOG_DEBUG,
00473                         QString("StorageGroup::GetRelativePathname(%1) = '%2'")
00474                             .arg(filename).arg(result));
00475                     return result;
00476                 }
00477             }
00478         }
00479     }
00480 
00481     QMap<QString, QString>::iterator it = m_builtinGroups.begin();
00482     for (; it != m_builtinGroups.end(); ++it)
00483     {
00484         QDir qdir(it.value());
00485         if (!qdir.exists())
00486             qdir.mkpath(it.value());
00487 
00488         QString directory = it.value();
00489         if (filename.startsWith(directory))
00490         {
00491             result = filename;
00492             result.replace(0, directory.length(), "");
00493             if (result.startsWith("/"))
00494                 result.replace(0, 1, "");
00495 
00496             LOG(VB_FILE, LOG_DEBUG,
00497                 QString("StorageGroup::GetRelativePathname(%1) = '%2'")
00498                     .arg(filename).arg(result));
00499             return result;
00500         }
00501     }
00502 
00503     return result;
00504 }
00505 
00515 bool StorageGroup::FindDirs(const QString group, const QString hostname,
00516                             QStringList *dirlist)
00517 {
00518     bool found = false;
00519     QString dirname;
00520     MSqlQuery query(MSqlQuery::InitCon());
00521 
00522     StaticInit();
00523 
00524     QString sql = "SELECT DISTINCT dirname "
00525                   "FROM storagegroup ";
00526 
00527     if (!group.isEmpty())
00528     {
00529         sql.append("WHERE groupname = :GROUP");
00530         if (!hostname.isEmpty())
00531             sql.append(" AND hostname = :HOSTNAME");
00532     }
00533 
00534     query.prepare(sql);
00535     if (!group.isEmpty())
00536     {
00537         query.bindValue(":GROUP", group);
00538         if (!hostname.isEmpty())
00539             query.bindValue(":HOSTNAME", hostname);
00540     }
00541 
00542     if (!query.exec() || !query.isActive())
00543         MythDB::DBError("StorageGroup::StorageGroup()", query);
00544     else if (query.next())
00545     {
00546         do
00547         {
00548             /* The storagegroup.dirname column uses utf8_bin collation, so Qt
00549              * uses QString::fromAscii() for toString(). Explicitly convert the
00550              * value using QString::fromUtf8() to prevent corruption. */
00551             dirname = QString::fromUtf8(query.value(0)
00552                                         .toByteArray().constData());
00553             dirname.replace(QRegExp("^\\s*"), "");
00554             dirname.replace(QRegExp("\\s*$"), "");
00555             if (dirname.right(1) == "/")
00556                 dirname.remove(dirname.length() - 1, 1);
00557 
00558             if (dirlist)
00559                 (*dirlist) << dirname;
00560             else
00561                 return true;
00562         }
00563         while (query.next());
00564         found = true;
00565     }
00566 
00567     if (m_builtinGroups.contains(group))
00568     {
00569         QDir testdir(m_builtinGroups[group]);
00570         if (testdir.exists())
00571         {
00572             if (dirlist && !dirlist->contains(testdir.absolutePath()))
00573                 (*dirlist) << testdir.absolutePath();
00574             found = true;
00575         }
00576     }
00577 
00578     return found;
00579 }
00580 
00581 QString StorageGroup::FindFile(QString filename)
00582 {
00583     LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindFile: Searching for '%1'")
00584                                       .arg(filename));
00585 
00586     QString recDir = FindFileDir(filename);
00587     QString result = "";
00588 
00589     if (!recDir.isEmpty())
00590     {
00591         result = recDir + "/" + filename;
00592         LOG(VB_FILE, LOG_DEBUG, LOC +
00593             QString("FindFile: Found '%1'") .arg(result));
00594     }
00595     else
00596     {
00597         LOG(VB_FILE, LOG_ERR, LOC +
00598             QString("FindFile: Unable to find '%1'!") .arg(filename));
00599     }
00600 
00601     return result;
00602 }
00603 
00604 QString StorageGroup::FindFileDir(QString filename)
00605 {
00606     QString result = "";
00607     QFileInfo checkFile("");
00608 
00609     int curDir = 0;
00610     while (curDir < m_dirlist.size())
00611     {
00612         QString testFile = m_dirlist[curDir] + "/" + filename;
00613         LOG(VB_FILE, LOG_DEBUG, LOC +
00614             QString("FindFileDir: Checking '%1' for '%2'")
00615                 .arg(m_dirlist[curDir]).arg(testFile));
00616         checkFile.setFile(testFile);
00617         if (checkFile.exists() || checkFile.isSymLink())
00618         {
00619             QString tmp = m_dirlist[curDir];
00620             tmp.detach();
00621             return tmp;
00622         }
00623 
00624         curDir++;
00625     }
00626 
00627     if (m_groupname.isEmpty() || (m_allowFallback == false))
00628     {
00629         // Not found in any dir, so try RecordFilePrefix if it exists
00630         QString tmpFile =
00631             gCoreContext->GetSetting("RecordFilePrefix") + "/" + filename;
00632         checkFile.setFile(tmpFile);
00633         if (checkFile.exists() || checkFile.isSymLink())
00634             result = tmpFile;
00635     }
00636     else if (m_groupname != "Default")
00637     {
00638         // Not found in current group so try Default
00639         StorageGroup sgroup("Default");
00640         QString tmpFile = sgroup.FindFileDir(filename);
00641         result = (tmpFile.isEmpty()) ? result : tmpFile;
00642     }
00643     else
00644     {
00645         // Not found in Default so try any dir
00646         StorageGroup sgroup;
00647         QString tmpFile = sgroup.FindFileDir(filename);
00648         result = (tmpFile.isEmpty()) ? result : tmpFile;
00649     }
00650 
00651     result.detach();
00652     return result;
00653 }
00654 
00655 QString StorageGroup::FindNextDirMostFree(void)
00656 {
00657     QString nextDir;
00658     int64_t nextDirFree = 0;
00659     int64_t thisDirTotal;
00660     int64_t thisDirUsed;
00661     int64_t thisDirFree;
00662 
00663     LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindNextDirMostFree: Starting"));
00664 
00665     if (m_allowFallback)
00666         nextDir = kDefaultStorageDir;
00667 
00668     if (m_dirlist.size())
00669         nextDir = m_dirlist[0];
00670 
00671     QDir checkDir("");
00672     int curDir = 0;
00673     while (curDir < m_dirlist.size())
00674     {
00675         checkDir.setPath(m_dirlist[curDir]);
00676         if (!checkDir.exists())
00677         {
00678             LOG(VB_GENERAL, LOG_ERR, LOC +
00679                 QString("FindNextDirMostFree: '%1' does not exist!")
00680                     .arg(m_dirlist[curDir]));
00681             curDir++;
00682             continue;
00683         }
00684 
00685         thisDirFree = getDiskSpace(m_dirlist[curDir], thisDirTotal,
00686                                    thisDirUsed);
00687         LOG(VB_FILE, LOG_DEBUG, LOC +
00688             QString("FindNextDirMostFree: '%1' has %2 KiB free")
00689                 .arg(m_dirlist[curDir])
00690                 .arg(QString::number(thisDirFree)));
00691 
00692         if (thisDirFree > nextDirFree)
00693         {
00694             nextDir     = m_dirlist[curDir];
00695             nextDirFree = thisDirFree;
00696         }
00697         curDir++;
00698     }
00699 
00700     if (nextDir.isEmpty())
00701         LOG(VB_FILE, LOG_ERR, LOC +
00702             "FindNextDirMostFree: Unable to find any directories to use.");
00703     else
00704         LOG(VB_FILE, LOG_DEBUG, LOC +
00705             QString("FindNextDirMostFree: Using '%1'").arg(nextDir));
00706 
00707     nextDir.detach();
00708     return nextDir;
00709 }
00710 
00711 void StorageGroup::CheckAllStorageGroupDirs(void)
00712 {
00713     QString m_groupname;
00714     QString dirname;
00715     MSqlQuery query(MSqlQuery::InitCon());
00716 
00717     query.prepare("SELECT groupname, dirname "
00718                   "FROM storagegroup "
00719                   "WHERE hostname = :HOSTNAME;");
00720     query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
00721     if (!query.exec() || !query.isActive())
00722     {
00723         MythDB::DBError("StorageGroup::CheckAllStorageGroupDirs()", query);
00724         return;
00725     }
00726 
00727     LOG(VB_FILE, LOG_DEBUG, LOC +
00728         "CheckAllStorageGroupDirs(): Checking All Storage Group directories");
00729 
00730     QFile testFile("");
00731     QDir testDir("");
00732     while (query.next())
00733     {
00734         m_groupname = query.value(0).toString();
00735         /* The storagegroup.dirname column uses utf8_bin collation, so Qt
00736          * uses QString::fromAscii() for toString(). Explicitly convert the
00737          * value using QString::fromUtf8() to prevent corruption. */
00738         dirname = QString::fromUtf8(query.value(1)
00739                                     .toByteArray().constData());
00740 
00741         dirname.replace(QRegExp("^\\s*"), "");
00742         dirname.replace(QRegExp("\\s*$"), "");
00743 
00744         LOG(VB_FILE, LOG_DEBUG, LOC +
00745             QString("Checking directory '%1' in group '%2'.")
00746                 .arg(dirname).arg(m_groupname));
00747 
00748         testDir.setPath(dirname);
00749         if (!testDir.exists())
00750         {
00751             LOG(VB_FILE, LOG_WARNING, LOC +
00752                 QString("Group '%1' references directory '%2' but this "
00753                         "directory does not exist.  This directory "
00754                         "will not be used on this server.")
00755                     .arg(m_groupname).arg(dirname));
00756         }
00757         else
00758         {
00759             testFile.setFileName(dirname + "/.test");
00760             if (testFile.open(QIODevice::WriteOnly))
00761                 testFile.remove();
00762             else
00763                 LOG(VB_GENERAL, LOG_ERR, LOC +
00764                     QString("Group '%1' wants to use directory '%2', but "
00765                             "this directory is not writeable.")
00766                         .arg(m_groupname).arg(dirname));
00767         }
00768     }
00769 }
00770 
00771 QStringList StorageGroup::getRecordingsGroups(void)
00772 {
00773     QStringList groups;
00774 
00775     MSqlQuery query(MSqlQuery::InitCon());
00776 
00777     QString sql = "SELECT DISTINCT groupname "
00778                   "FROM storagegroup "
00779                   "WHERE groupname NOT IN (";
00780     for (QStringList::const_iterator it = StorageGroup::kSpecialGroups.begin();
00781          it != StorageGroup::kSpecialGroups.end(); ++it)
00782         sql.append(QString(" '%1',").arg(*it));
00783     sql = sql.left(sql.length() - 1);
00784     sql.append(" );");
00785 
00786     query.prepare(sql);
00787     if (query.exec() && query.isActive() && query.size() > 0)
00788         while (query.next())
00789             groups += query.value(0).toString();
00790 
00791     groups.sort();
00792     groups.detach();
00793 
00794     return groups;
00795 }
00796 
00797 QStringList StorageGroup::getGroupDirs(QString groupname, QString host)
00798 {
00799     QStringList groups;
00800     QString addHost;
00801 
00802     MSqlQuery query(MSqlQuery::InitCon());
00803 
00804     if (!host.isEmpty())
00805         addHost = " AND hostname = :HOSTNAME";
00806     else
00807         addHost = "";
00808 
00809     QString sql = QString("SELECT dirname,hostname "
00810                   "FROM storagegroup "
00811                   "WHERE groupname = :GROUPNAME %1").arg(addHost);
00812 
00813     query.prepare(sql);
00814     query.bindValue(":GROUPNAME", groupname);
00815 
00816     if (!host.isEmpty())
00817         query.bindValue(":HOSTNAME", host);
00818 
00819     if (query.exec() && query.isActive() && query.size() > 0)
00820     {
00821         QString dirname;
00822         while (query.next())
00823         {
00824             /* The storagegroup.dirname column uses utf8_bin collation, so Qt
00825              * uses QString::fromAscii() for toString(). Explicitly convert the
00826              * value using QString::fromUtf8() to prevent corruption. */
00827             dirname = QString::fromUtf8(query.value(0)
00828                                         .toByteArray().constData());
00829             groups += gCoreContext->GenMythURL(query.value(1).toString(),
00830                                                0,
00831                                                dirname,
00832                                                groupname);
00833         }
00834     }
00835 
00836     groups.sort();
00837     groups.detach();
00838 
00839     return groups;
00840 }
00841 
00842 void StorageGroup::ClearGroupToUseCache(void)
00843 {
00844     QMutexLocker locker(&s_groupToUseLock);
00845     s_groupToUseCache.clear();
00846 }
00847 
00848 QString StorageGroup::GetGroupToUse(
00849     const QString &host, const QString &sgroup)
00850 {
00851     QString tmpGroup = sgroup;
00852     QString groupKey = QString("%1:%2").arg(sgroup).arg(host);
00853 
00854     QMutexLocker locker(&s_groupToUseLock);
00855 
00856     if (s_groupToUseCache.contains(groupKey))
00857     {
00858         tmpGroup = s_groupToUseCache[groupKey];
00859     }
00860     else
00861     {
00862         if (StorageGroup::FindDirs(sgroup, host))
00863         {
00864             s_groupToUseCache[groupKey] = sgroup;
00865         }
00866         else
00867         {
00868             LOG(VB_FILE, LOG_DEBUG,
00869                     QString("GetGroupToUse(): "
00870                             "falling back to Videos Storage Group for host %1 "
00871                             "since it does not have a %2 Storage Group.")
00872                     .arg(host).arg(sgroup));
00873 
00874             tmpGroup = "Videos";
00875             s_groupToUseCache[groupKey] = tmpGroup;
00876         }
00877     }
00878 
00879     return tmpGroup;
00880 }
00881 
00882 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends