Chris@465: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@465: Chris@465: /* Chris@465: Sonic Visualiser Chris@465: An audio file viewer and annotation editor. Chris@465: Centre for Digital Music, Queen Mary, University of London. Chris@465: This file copyright 2008 QMUL. Chris@465: Chris@465: This program is free software; you can redistribute it and/or Chris@465: modify it under the terms of the GNU General Public License as Chris@465: published by the Free Software Foundation; either version 2 of the Chris@465: License, or (at your option) any later version. See the file Chris@465: COPYING included with this distribution for more information. Chris@465: */ Chris@465: Chris@465: #include "CachedFile.h" Chris@465: Chris@465: #include "base/TempDirectory.h" Chris@465: #include "base/ProgressReporter.h" Chris@465: #include "base/Exceptions.h" Chris@465: Chris@465: #include Chris@465: #include Chris@465: #include Chris@465: #include Chris@465: #include Chris@465: #include Chris@465: Chris@465: QString Chris@465: CachedFile::getLocalFilenameFor(QUrl url) Chris@465: { Chris@465: QDir dir(getCacheDirectory()); Chris@465: Chris@465: QString filename = Chris@465: QString::fromLocal8Bit Chris@465: (QCryptographicHash::hash(url.toString().toLocal8Bit(), Chris@465: QCryptographicHash::Sha1).toHex()); Chris@465: Chris@465: return dir.filePath(filename); Chris@465: } Chris@465: Chris@465: QString Chris@465: CachedFile::getCacheDirectory() Chris@465: { Chris@465: QDir dir = TempDirectory::getInstance()->getContainingPath(); Chris@465: Chris@465: QString cacheDirName("cache"); Chris@465: Chris@465: QFileInfo fi(dir.filePath(cacheDirName)); Chris@465: Chris@465: if ((fi.exists() && !fi.isDir()) || Chris@465: (!fi.exists() && !dir.mkdir(cacheDirName))) { Chris@465: Chris@465: throw DirectoryCreationFailed(fi.filePath()); Chris@465: } Chris@465: Chris@465: return fi.filePath(); Chris@465: } Chris@465: Chris@465: CachedFile::CachedFile(QUrl url, ProgressReporter *reporter) : Chris@465: m_url(url), Chris@465: m_localFilename(getLocalFilenameFor(url)), Chris@465: m_ok(false) Chris@465: { Chris@465: refresh(); Chris@465: } Chris@465: Chris@465: bool Chris@465: CachedFile::isOK() const Chris@465: { Chris@465: return m_ok; Chris@465: } Chris@465: Chris@465: QString Chris@465: CachedFile::getLocalFilename() const Chris@465: { Chris@465: return m_localFilename; Chris@465: } Chris@465: Chris@465: void Chris@465: CachedFile::refresh() Chris@465: { Chris@465: //!!! n.b. obvious race condition here if different CachedFile Chris@465: // objects for same url used in more than one thread -- need to Chris@465: // lock appropriately. also consider race condition between Chris@465: // separate instances of the program Chris@465: Chris@465: if (!QFileInfo(m_localFilename).exists()) { Chris@465: updateLastRetrieval(false); // empirically! Chris@465: } Chris@465: Chris@465: QDateTime lastRetrieval = getLastRetrieval(); Chris@465: Chris@465: if (lastRetrieval.isValid()) { Chris@465: // this will not be the case if the file is missing, after Chris@465: // updateLastRetrieval(false) was called above Chris@465: m_ok = true; Chris@465: if (lastRetrieval.addDays(2) < QDateTime::currentDateTime()) { //!!! Chris@465: // doesn't matter if retrieval fails -- we just don't Chris@465: // update the last retrieval time Chris@465: Chris@465: //!!! but we do want an additional last-attempted Chris@465: // timestamp so as to ensure we aren't retrying the Chris@465: // retrieval every single time if it isn't working Chris@465: Chris@465: if (retrieve()) { Chris@465: updateLastRetrieval(true); Chris@465: } Chris@465: } Chris@465: } else { Chris@465: // there is no acceptable file Chris@465: if (retrieve()) { Chris@465: m_ok = true; Chris@465: updateLastRetrieval(true); Chris@465: } else { Chris@465: // again, we don't need to do anything here -- the last Chris@465: // retrieval timestamp is already invalid Chris@465: } Chris@465: } Chris@465: } Chris@465: Chris@465: bool Chris@465: CachedFile::retrieve() Chris@465: { Chris@465: //!!! need to work by retrieving the file to another name, and Chris@465: //!!! then "atomically" moving it to its proper place (I'm not Chris@465: //!!! sure we can do an atomic move to replace an existing file Chris@465: //!!! using Qt classes, but a plain delete then copy is probably Chris@465: //!!! good enough) Chris@465: Chris@465: Chris@465: Chris@465: Chris@465: } Chris@465: Chris@465: QDateTime Chris@465: CachedFile::getLastRetrieval() Chris@465: { Chris@465: QSettings settings; Chris@465: settings.beginGroup("FileCache"); Chris@465: Chris@465: QString key("last-retrieval-times"); Chris@465: Chris@465: QMap timeMap = settings.value(key).toMap(); Chris@465: QDateTime lastTime = timeMap[m_localFilename].toDateTime(); Chris@465: Chris@465: settings.endGroup(); Chris@465: return lastTime; Chris@465: } Chris@465: Chris@465: void Chris@465: CachedFile::updateLastRetrieval(bool successful) Chris@465: { Chris@465: //!!! note !successful does not mean "we failed to update the Chris@465: //!!! file" (and so it remains the same as before); it means "the Chris@465: //!!! file is not there at all" Chris@465: Chris@465: QSettings settings; Chris@465: settings.beginGroup("FileCache"); Chris@465: Chris@465: QString key("last-retrieval-times"); Chris@465: Chris@465: QMap timeMap = settings.value(key).toMap(); Chris@465: Chris@465: QDateTime dt; Chris@465: if (successful) dt = QDateTime::currentDateTime(); Chris@465: Chris@465: timeMap[m_localFilename] = dt; Chris@465: settings.setValue(key, timeMap); Chris@465: Chris@465: settings.endGroup(); Chris@465: } Chris@465: Chris@465: