svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2008 QMUL. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "CachedFile.h" 00017 00018 #include "base/TempDirectory.h" 00019 #include "base/ProgressReporter.h" 00020 #include "base/Exceptions.h" 00021 00022 #include "FileSource.h" 00023 00024 #include <QFileInfo> 00025 #include <QSettings> 00026 #include <QVariant> 00027 #include <QMap> 00028 #include <QDir> 00029 #include <QCryptographicHash> 00030 00031 #include "base/Profiler.h" 00032 00033 #include <iostream> 00034 00035 CachedFile::OriginLocalFilenameMap 00036 CachedFile::m_knownGoodCaches; 00037 00038 QString 00039 CachedFile::getLocalFilenameFor(QUrl url) 00040 { 00041 Profiler p("CachedFile::getLocalFilenameFor"); 00042 00043 QDir dir(getCacheDirectory()); 00044 00045 QString filename = 00046 QString::fromLocal8Bit 00047 (QCryptographicHash::hash(url.toString().toLocal8Bit(), 00048 QCryptographicHash::Sha1).toHex()); 00049 00050 return dir.filePath(filename); 00051 } 00052 00053 QString 00054 CachedFile::getCacheDirectory() 00055 { 00056 QDir dir = TempDirectory::getInstance()->getContainingPath(); 00057 00058 QString cacheDirName("cache"); 00059 00060 QFileInfo fi(dir.filePath(cacheDirName)); 00061 00062 if ((fi.exists() && !fi.isDir()) || 00063 (!fi.exists() && !dir.mkdir(cacheDirName))) { 00064 00065 throw DirectoryCreationFailed(fi.filePath()); 00066 } 00067 00068 return fi.filePath(); 00069 } 00070 00071 CachedFile::CachedFile(QString origin, 00072 ProgressReporter *reporter, 00073 QString preferredContentType) : 00074 m_origin(origin), 00075 m_preferredContentType(preferredContentType), 00076 m_reporter(reporter), 00077 m_ok(false) 00078 { 00079 Profiler p("CachedFile::CachedFile[1]"); 00080 00081 SVDEBUG << "CachedFile::CachedFile: origin is \"" 00082 << origin << "\"" << endl; 00083 checkFile(); 00084 } 00085 00086 CachedFile::CachedFile(QUrl url, 00087 ProgressReporter *reporter, 00088 QString preferredContentType) : 00089 m_origin(url.toString()), 00090 m_preferredContentType(preferredContentType), 00091 m_reporter(reporter), 00092 m_ok(false) 00093 { 00094 Profiler p("CachedFile::CachedFile[2]"); 00095 00096 SVDEBUG << "CachedFile::CachedFile: url is \"" 00097 << url.toString() << "\"" << endl; 00098 checkFile(); 00099 } 00100 00101 CachedFile::~CachedFile() 00102 { 00103 } 00104 00105 bool 00106 CachedFile::isOK() const 00107 { 00108 return m_ok; 00109 } 00110 00111 QString 00112 CachedFile::getLocalFilename() const 00113 { 00114 return m_localFilename; 00115 } 00116 00117 void 00118 CachedFile::checkFile() 00119 { 00121 // objects for same url used in more than one thread -- need to 00122 // lock appropriately. also consider race condition between 00123 // separate instances of the program! 00124 00125 OriginLocalFilenameMap::const_iterator i = m_knownGoodCaches.find(m_origin); 00126 if (i != m_knownGoodCaches.end()) { 00127 m_ok = true; 00128 m_localFilename = i->second; 00129 return; 00130 } 00131 00132 m_localFilename = getLocalFilenameFor(m_origin); 00133 00134 if (!QFileInfo(m_localFilename).exists()) { 00135 SVDEBUG << "CachedFile::check: Local file does not exist, making a note that it hasn't been retrieved" << endl; 00136 updateLastRetrieval(false); // empirically! 00137 } 00138 00139 QDateTime lastRetrieval = getLastRetrieval(); 00140 00141 if (lastRetrieval.isValid()) { 00142 SVDEBUG << "CachedFile::check: Valid last retrieval at " 00143 << lastRetrieval.toString() << endl; 00144 // this will not be the case if the file is missing, after 00145 // updateLastRetrieval(false) was called above 00146 m_ok = true; 00147 if (lastRetrieval.addDays(2) < QDateTime::currentDateTime()) { 00148 SVDEBUG << "CachedFile::check: Out of date; trying to retrieve again" << endl; 00149 // doesn't matter if retrieval fails -- we just don't 00150 // update the last retrieval time 00151 00153 // timestamp so as to ensure we aren't retrying the 00154 // retrieval every single time if it isn't working 00155 00156 if (retrieve()) { 00157 SVDEBUG << "CachedFile::check: Retrieval succeeded" << endl; 00158 updateLastRetrieval(true); 00159 } else { 00160 cerr << "CachedFile::check: Retrieval failed, will try again later (using existing file for now)" << endl; 00161 } 00162 } 00163 } else { 00164 SVDEBUG << "CachedFile::check: No valid last retrieval" << endl; 00165 // there is no acceptable file 00166 if (retrieve()) { 00167 SVDEBUG << "CachedFile::check: Retrieval succeeded" << endl; 00168 m_ok = true; 00169 updateLastRetrieval(true); 00170 } else { 00171 cerr << "CachedFile::check: Retrieval failed, remaining in invalid state" << endl; 00172 // again, we don't need to do anything here -- the last 00173 // retrieval timestamp is already invalid 00174 } 00175 } 00176 00177 if (m_ok) { 00178 m_knownGoodCaches[m_origin] = m_localFilename; 00179 } 00180 } 00181 00182 bool 00183 CachedFile::retrieve() 00184 { 00190 00191 FileSource fs(m_origin, m_reporter, m_preferredContentType); 00192 00193 if (!fs.isOK() || !fs.isAvailable()) { 00194 SVDEBUG << "CachedFile::retrieve: ERROR: FileSource reported unavailable or failure" << endl; 00195 return false; 00196 } 00197 00198 fs.waitForData(); 00199 00200 if (!fs.isOK()) { 00201 SVDEBUG << "CachedFile::retrieve: ERROR: FileSource reported failure during receive" << endl; 00202 return false; 00203 } 00204 00205 QString tempName = fs.getLocalFilename(); 00206 QFile tempFile(tempName); 00207 if (!tempFile.exists()) { 00208 SVDEBUG << "CachedFile::retrieve: ERROR: FileSource reported success, but local temporary file \"" << tempName << "\" does not exist" << endl; 00209 return false; 00210 } 00211 00212 QFile previous(m_localFilename); 00213 if (previous.exists()) { 00214 if (!previous.remove()) { 00215 cerr << "CachedFile::retrieve: ERROR: Failed to remove previous copy of cached file at \"" << m_localFilename << "\"" << endl; 00216 return false; 00217 } 00218 } 00219 00223 00224 if (!tempFile.copy(m_localFilename)) { 00225 cerr << "CachedFile::retrieve: ERROR: Failed to copy newly retrieved file from \"" << tempName << "\" to \"" << m_localFilename << "\"" << endl; 00226 return false; 00227 } 00228 00229 SVDEBUG << "CachedFile::retrieve: Successfully copied newly retrieved file \"" << tempName << "\" to its home at \"" << m_localFilename << "\"" << endl; 00230 00231 return true; 00232 } 00233 00234 QDateTime 00235 CachedFile::getLastRetrieval() 00236 { 00237 QSettings settings; 00238 settings.beginGroup("FileCache"); 00239 00240 QString key("last-retrieval-times"); 00241 00242 QMap<QString, QVariant> timeMap = settings.value(key).toMap(); 00243 QDateTime lastTime = timeMap[m_localFilename].toDateTime(); 00244 00245 settings.endGroup(); 00246 return lastTime; 00247 } 00248 00249 void 00250 CachedFile::updateLastRetrieval(bool successful) 00251 { 00255 00256 QSettings settings; 00257 settings.beginGroup("FileCache"); 00258 00259 QString key("last-retrieval-times"); 00260 00261 QMap<QString, QVariant> timeMap = settings.value(key).toMap(); 00262 00263 QDateTime dt; 00264 if (successful) dt = QDateTime::currentDateTime(); 00265 00266 timeMap[m_localFilename] = dt; 00267 settings.setValue(key, timeMap); 00268 00269 settings.endGroup(); 00270 } 00271 00272