annotate data/fileio/CachedFile.cpp @ 466:f35bfa88f0b5

* flesh out CachedFile a bit more
author Chris Cannam
date Mon, 27 Oct 2008 15:07:35 +0000
parents 63b8ba45d953
children c9b055f84326
rev   line source
Chris@465 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@465 2
Chris@465 3 /*
Chris@465 4 Sonic Visualiser
Chris@465 5 An audio file viewer and annotation editor.
Chris@465 6 Centre for Digital Music, Queen Mary, University of London.
Chris@465 7 This file copyright 2008 QMUL.
Chris@465 8
Chris@465 9 This program is free software; you can redistribute it and/or
Chris@465 10 modify it under the terms of the GNU General Public License as
Chris@465 11 published by the Free Software Foundation; either version 2 of the
Chris@465 12 License, or (at your option) any later version. See the file
Chris@465 13 COPYING included with this distribution for more information.
Chris@465 14 */
Chris@465 15
Chris@465 16 #include "CachedFile.h"
Chris@465 17
Chris@465 18 #include "base/TempDirectory.h"
Chris@465 19 #include "base/ProgressReporter.h"
Chris@465 20 #include "base/Exceptions.h"
Chris@465 21
Chris@466 22 #include "FileSource.h"
Chris@466 23
Chris@465 24 #include <QFileInfo>
Chris@465 25 #include <QSettings>
Chris@465 26 #include <QVariant>
Chris@465 27 #include <QMap>
Chris@465 28 #include <QDir>
Chris@465 29 #include <QCryptographicHash>
Chris@465 30
Chris@466 31 #include <iostream>
Chris@466 32
Chris@465 33 QString
Chris@465 34 CachedFile::getLocalFilenameFor(QUrl url)
Chris@465 35 {
Chris@465 36 QDir dir(getCacheDirectory());
Chris@465 37
Chris@465 38 QString filename =
Chris@465 39 QString::fromLocal8Bit
Chris@465 40 (QCryptographicHash::hash(url.toString().toLocal8Bit(),
Chris@465 41 QCryptographicHash::Sha1).toHex());
Chris@465 42
Chris@465 43 return dir.filePath(filename);
Chris@465 44 }
Chris@465 45
Chris@465 46 QString
Chris@465 47 CachedFile::getCacheDirectory()
Chris@465 48 {
Chris@465 49 QDir dir = TempDirectory::getInstance()->getContainingPath();
Chris@465 50
Chris@465 51 QString cacheDirName("cache");
Chris@465 52
Chris@465 53 QFileInfo fi(dir.filePath(cacheDirName));
Chris@465 54
Chris@465 55 if ((fi.exists() && !fi.isDir()) ||
Chris@465 56 (!fi.exists() && !dir.mkdir(cacheDirName))) {
Chris@465 57
Chris@465 58 throw DirectoryCreationFailed(fi.filePath());
Chris@465 59 }
Chris@465 60
Chris@465 61 return fi.filePath();
Chris@465 62 }
Chris@465 63
Chris@465 64 CachedFile::CachedFile(QUrl url, ProgressReporter *reporter) :
Chris@465 65 m_url(url),
Chris@465 66 m_localFilename(getLocalFilenameFor(url)),
Chris@466 67 m_reporter(reporter),
Chris@465 68 m_ok(false)
Chris@465 69 {
Chris@465 70 refresh();
Chris@465 71 }
Chris@465 72
Chris@465 73 bool
Chris@465 74 CachedFile::isOK() const
Chris@465 75 {
Chris@465 76 return m_ok;
Chris@465 77 }
Chris@465 78
Chris@465 79 QString
Chris@465 80 CachedFile::getLocalFilename() const
Chris@465 81 {
Chris@465 82 return m_localFilename;
Chris@465 83 }
Chris@465 84
Chris@465 85 void
Chris@465 86 CachedFile::refresh()
Chris@465 87 {
Chris@465 88 //!!! n.b. obvious race condition here if different CachedFile
Chris@465 89 // objects for same url used in more than one thread -- need to
Chris@465 90 // lock appropriately. also consider race condition between
Chris@465 91 // separate instances of the program
Chris@465 92
Chris@465 93 if (!QFileInfo(m_localFilename).exists()) {
Chris@465 94 updateLastRetrieval(false); // empirically!
Chris@465 95 }
Chris@465 96
Chris@465 97 QDateTime lastRetrieval = getLastRetrieval();
Chris@465 98
Chris@465 99 if (lastRetrieval.isValid()) {
Chris@465 100 // this will not be the case if the file is missing, after
Chris@465 101 // updateLastRetrieval(false) was called above
Chris@465 102 m_ok = true;
Chris@465 103 if (lastRetrieval.addDays(2) < QDateTime::currentDateTime()) { //!!!
Chris@465 104 // doesn't matter if retrieval fails -- we just don't
Chris@465 105 // update the last retrieval time
Chris@465 106
Chris@465 107 //!!! but we do want an additional last-attempted
Chris@465 108 // timestamp so as to ensure we aren't retrying the
Chris@465 109 // retrieval every single time if it isn't working
Chris@465 110
Chris@465 111 if (retrieve()) {
Chris@465 112 updateLastRetrieval(true);
Chris@465 113 }
Chris@465 114 }
Chris@465 115 } else {
Chris@465 116 // there is no acceptable file
Chris@465 117 if (retrieve()) {
Chris@465 118 m_ok = true;
Chris@465 119 updateLastRetrieval(true);
Chris@465 120 } else {
Chris@465 121 // again, we don't need to do anything here -- the last
Chris@465 122 // retrieval timestamp is already invalid
Chris@465 123 }
Chris@465 124 }
Chris@465 125 }
Chris@465 126
Chris@465 127 bool
Chris@465 128 CachedFile::retrieve()
Chris@465 129 {
Chris@465 130 //!!! need to work by retrieving the file to another name, and
Chris@465 131 //!!! then "atomically" moving it to its proper place (I'm not
Chris@465 132 //!!! sure we can do an atomic move to replace an existing file
Chris@465 133 //!!! using Qt classes, but a plain delete then copy is probably
Chris@465 134 //!!! good enough)
Chris@465 135
Chris@466 136 FileSource fs(m_url, m_reporter);
Chris@465 137
Chris@466 138 if (!fs.isOK() || !fs.isAvailable()) {
Chris@466 139 return false;
Chris@466 140 }
Chris@465 141
Chris@466 142 fs.waitForData();
Chris@465 143
Chris@466 144 if (!fs.isOK()) {
Chris@466 145 return false;
Chris@466 146 }
Chris@466 147
Chris@466 148 QString tempName = fs.getLocalFilename();
Chris@466 149 QFile tempFile(tempName);
Chris@466 150 if (!tempFile.exists()) {
Chris@466 151 std::cerr << "CachedFile::retrieve: ERROR: FileSource reported success, but local temporary file \"" << tempName.toStdString() << "\" does not exist" << std::endl;
Chris@466 152 return false;
Chris@466 153 }
Chris@466 154
Chris@466 155 QFile previous(m_localFilename);
Chris@466 156 if (previous.exists()) {
Chris@466 157 if (!previous.remove()) {
Chris@466 158 std::cerr << "CachedFile::retrieve: ERROR: Failed to remove previous copy of cached file at \"" << m_localFilename.toStdString() << "\"" << std::endl;
Chris@466 159 return false;
Chris@466 160 }
Chris@466 161 }
Chris@466 162
Chris@466 163 //!!! This is not ideal, could leave us with nothing (old file
Chris@466 164 //!!! removed, new file not able to be copied in because e.g. no
Chris@466 165 //!!! disk space left)
Chris@466 166
Chris@466 167 if (!tempFile.copy(m_localFilename)) {
Chris@466 168 std::cerr << "CachedFile::retrieve: ERROR: Failed to copy newly retrieved file from \"" << tempName.toStdString() << "\" to \"" << m_localFilename.toStdString() << "\"" << std::endl;
Chris@466 169 return false;
Chris@466 170 }
Chris@466 171
Chris@466 172 return true;
Chris@465 173 }
Chris@465 174
Chris@465 175 QDateTime
Chris@465 176 CachedFile::getLastRetrieval()
Chris@465 177 {
Chris@465 178 QSettings settings;
Chris@465 179 settings.beginGroup("FileCache");
Chris@465 180
Chris@465 181 QString key("last-retrieval-times");
Chris@465 182
Chris@465 183 QMap<QString, QVariant> timeMap = settings.value(key).toMap();
Chris@465 184 QDateTime lastTime = timeMap[m_localFilename].toDateTime();
Chris@465 185
Chris@465 186 settings.endGroup();
Chris@465 187 return lastTime;
Chris@465 188 }
Chris@465 189
Chris@465 190 void
Chris@465 191 CachedFile::updateLastRetrieval(bool successful)
Chris@465 192 {
Chris@465 193 //!!! note !successful does not mean "we failed to update the
Chris@465 194 //!!! file" (and so it remains the same as before); it means "the
Chris@465 195 //!!! file is not there at all"
Chris@465 196
Chris@465 197 QSettings settings;
Chris@465 198 settings.beginGroup("FileCache");
Chris@465 199
Chris@465 200 QString key("last-retrieval-times");
Chris@465 201
Chris@465 202 QMap<QString, QVariant> timeMap = settings.value(key).toMap();
Chris@465 203
Chris@465 204 QDateTime dt;
Chris@465 205 if (successful) dt = QDateTime::currentDateTime();
Chris@465 206
Chris@465 207 timeMap[m_localFilename] = dt;
Chris@465 208 settings.setValue(key, timeMap);
Chris@465 209
Chris@465 210 settings.endGroup();
Chris@465 211 }
Chris@465 212
Chris@465 213