annotate data/fileio/RemoteFile.cpp @ 212:fb8ddd00f440

* better icon * support range mappers in thumbwheel * supply range mapper for vertical zoom from spectrogram * fix bug in fftmodel for scaled ffts * make the various widgets all respond to double-click for edit, middle-click for reset, ctrl-left-click for reset
author Chris Cannam
date Fri, 12 Jan 2007 14:49:18 +0000
parents e2bbb58e6df6
children ce6f65ab3327
rev   line source
Chris@208 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@208 2
Chris@208 3 /*
Chris@208 4 Sonic Visualiser
Chris@208 5 An audio file viewer and annotation editor.
Chris@208 6 Centre for Digital Music, Queen Mary, University of London.
Chris@208 7 This file copyright 2007 QMUL.
Chris@208 8
Chris@208 9 This program is free software; you can redistribute it and/or
Chris@208 10 modify it under the terms of the GNU General Public License as
Chris@208 11 published by the Free Software Foundation; either version 2 of the
Chris@208 12 License, or (at your option) any later version. See the file
Chris@208 13 COPYING included with this distribution for more information.
Chris@208 14 */
Chris@208 15
Chris@208 16 #include "RemoteFile.h"
Chris@208 17 #include "base/TempDirectory.h"
Chris@208 18 #include "base/Exceptions.h"
Chris@208 19
Chris@208 20 #include <QHttp>
Chris@208 21 #include <QFtp>
Chris@208 22 #include <QFileInfo>
Chris@208 23 #include <QDir>
Chris@208 24 #include <QApplication>
Chris@208 25 #include <QProgressDialog>
Chris@210 26 #include <QHttpResponseHeader>
Chris@208 27
Chris@208 28 #include <iostream>
Chris@208 29
Chris@208 30 int
Chris@208 31 RemoteFile::m_count = 0;
Chris@208 32
Chris@208 33 QMutex
Chris@208 34 RemoteFile::m_fileCreationMutex;
Chris@208 35
Chris@208 36 RemoteFile::RemoteFile(QUrl url) :
Chris@208 37 m_ftp(0),
Chris@208 38 m_http(0),
Chris@208 39 m_localFile(0),
Chris@208 40 m_ok(false),
Chris@210 41 m_lastStatus(0),
Chris@208 42 m_done(false),
Chris@210 43 m_progressDialog(0),
Chris@210 44 m_progressShowTimer(this)
Chris@208 45 {
Chris@208 46 if (!canHandleScheme(url)) {
Chris@208 47 std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << url.toString().toStdString() << "\"" << std::endl;
Chris@208 48 return;
Chris@208 49 }
Chris@208 50
Chris@208 51 m_localFilename = createLocalFile(url);
Chris@208 52 if (m_localFilename == "") return;
Chris@208 53 m_localFile = new QFile(m_localFilename);
Chris@208 54 m_localFile->open(QFile::WriteOnly);
Chris@208 55
Chris@208 56 QString scheme = url.scheme().toLower();
Chris@208 57
Chris@208 58 if (scheme == "http") {
Chris@208 59
Chris@211 60 m_ok = true;
Chris@208 61 m_http = new QHttp(url.host(), url.port(80));
Chris@208 62 connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
Chris@208 63 connect(m_http, SIGNAL(dataReadProgress(int, int)),
Chris@208 64 this, SLOT(dataReadProgress(int, int)));
Chris@210 65 connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
Chris@210 66 this, SLOT(responseHeaderReceived(const QHttpResponseHeader &)));
Chris@208 67 m_http->get(url.path(), m_localFile);
Chris@208 68
Chris@208 69 } else if (scheme == "ftp") {
Chris@208 70
Chris@211 71 m_ok = true;
Chris@208 72 m_ftp = new QFtp;
Chris@208 73 connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
Chris@208 74 connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
Chris@208 75 this, SLOT(dataTransferProgress(qint64, qint64)));
Chris@208 76 m_ftp->connectToHost(url.host(), url.port(21));
Chris@208 77
Chris@208 78 QString username = url.userName();
Chris@208 79 if (username == "") {
Chris@208 80 username = "anonymous";
Chris@208 81 }
Chris@208 82
Chris@208 83 QString password = url.password();
Chris@208 84 if (password == "") {
Chris@208 85 password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
Chris@208 86 }
Chris@208 87
Chris@208 88 QStringList path = url.path().split('/');
Chris@208 89 for (QStringList::iterator i = path.begin(); i != path.end(); ) {
Chris@208 90 QString bit = *i;
Chris@208 91 ++i;
Chris@208 92 if (i != path.end()) {
Chris@208 93 m_ftp->cd(*i);
Chris@208 94 } else {
Chris@208 95 m_ftp->get(*i, m_localFile);
Chris@208 96 }
Chris@208 97 }
Chris@208 98 }
Chris@208 99
Chris@208 100 if (m_ok) {
Chris@208 101 m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100);
Chris@210 102 m_progressDialog->hide();
Chris@210 103 connect(&m_progressShowTimer, SIGNAL(timeout()),
Chris@210 104 this, SLOT(showProgressDialog()));
Chris@210 105 connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled()));
Chris@210 106 m_progressShowTimer.setSingleShot(true);
Chris@210 107 m_progressShowTimer.start(2000);
Chris@208 108 }
Chris@208 109 }
Chris@208 110
Chris@208 111 RemoteFile::~RemoteFile()
Chris@208 112 {
Chris@211 113 cleanup();
Chris@211 114 }
Chris@211 115
Chris@211 116 void
Chris@211 117 RemoteFile::cleanup()
Chris@211 118 {
Chris@211 119 // std::cerr << "RemoteFile::cleanup" << std::endl;
Chris@211 120 m_done = true;
Chris@211 121 delete m_http;
Chris@211 122 m_http = 0;
Chris@208 123 delete m_ftp;
Chris@211 124 m_ftp = 0;
Chris@211 125 delete m_progressDialog;
Chris@211 126 m_progressDialog = 0;
Chris@208 127 delete m_localFile;
Chris@211 128 m_localFile = 0;
Chris@208 129 }
Chris@208 130
Chris@208 131 bool
Chris@208 132 RemoteFile::canHandleScheme(QUrl url)
Chris@208 133 {
Chris@208 134 QString scheme = url.scheme().toLower();
Chris@208 135 return (scheme == "http" || scheme == "ftp");
Chris@208 136 }
Chris@208 137
Chris@210 138 bool
Chris@210 139 RemoteFile::isAvailable()
Chris@210 140 {
Chris@211 141 while (m_ok && (!m_done && m_lastStatus == 0)) {
Chris@210 142 QApplication::processEvents();
Chris@210 143 }
Chris@211 144 bool available = true;
Chris@211 145 if (!m_ok) available = false;
Chris@211 146 else available = (m_lastStatus / 100 == 2);
Chris@211 147 std::cerr << "RemoteFile::isAvailable: " << (available ? "yes" : "no")
Chris@211 148 << std::endl;
Chris@211 149 return available;
Chris@210 150 }
Chris@210 151
Chris@208 152 void
Chris@208 153 RemoteFile::wait()
Chris@208 154 {
Chris@211 155 while (m_ok && !m_done) {
Chris@208 156 QApplication::processEvents();
Chris@208 157 }
Chris@208 158 }
Chris@208 159
Chris@208 160 bool
Chris@208 161 RemoteFile::isOK() const
Chris@208 162 {
Chris@208 163 return m_ok;
Chris@208 164 }
Chris@208 165
Chris@208 166 bool
Chris@208 167 RemoteFile::isDone() const
Chris@208 168 {
Chris@208 169 return m_done;
Chris@208 170 }
Chris@208 171
Chris@208 172 QString
Chris@208 173 RemoteFile::getLocalFilename() const
Chris@208 174 {
Chris@208 175 return m_localFilename;
Chris@208 176 }
Chris@208 177
Chris@208 178 QString
Chris@208 179 RemoteFile::getErrorString() const
Chris@208 180 {
Chris@208 181 return m_errorString;
Chris@208 182 }
Chris@208 183
Chris@208 184 void
Chris@208 185 RemoteFile::dataReadProgress(int done, int total)
Chris@208 186 {
Chris@208 187 dataTransferProgress(done, total);
Chris@208 188 }
Chris@208 189
Chris@208 190 void
Chris@210 191 RemoteFile::responseHeaderReceived(const QHttpResponseHeader &resp)
Chris@210 192 {
Chris@210 193 m_lastStatus = resp.statusCode();
Chris@210 194 if (m_lastStatus / 100 >= 4) {
Chris@210 195 m_errorString = QString("%1 %2")
Chris@210 196 .arg(resp.statusCode()).arg(resp.reasonPhrase());
Chris@211 197 std::cerr << "RemoteFile::responseHeaderReceived: "
Chris@211 198 << m_errorString.toStdString() << std::endl;
Chris@211 199 } else {
Chris@211 200 std::cerr << "RemoteFile::responseHeaderReceived: "
Chris@211 201 << m_lastStatus << std::endl;
Chris@211 202 }
Chris@210 203 }
Chris@210 204
Chris@210 205 void
Chris@208 206 RemoteFile::dataTransferProgress(qint64 done, qint64 total)
Chris@208 207 {
Chris@211 208 if (!m_progressDialog) return;
Chris@211 209
Chris@208 210 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
Chris@208 211 emit progress(percent);
Chris@208 212
Chris@211 213 if (percent > 0) {
Chris@211 214 m_progressDialog->setValue(percent);
Chris@211 215 m_progressDialog->show();
Chris@211 216 }
Chris@210 217 }
Chris@210 218
Chris@210 219 void
Chris@210 220 RemoteFile::cancelled()
Chris@210 221 {
Chris@211 222 deleteLocalFile();
Chris@210 223 m_done = true;
Chris@210 224 m_ok = false;
Chris@210 225 m_errorString = tr("Download cancelled");
Chris@208 226 }
Chris@208 227
Chris@208 228 void
Chris@208 229 RemoteFile::done(bool error)
Chris@208 230 {
Chris@211 231 // std::cerr << "RemoteFile::done(" << error << ")" << std::endl;
Chris@211 232
Chris@211 233 if (m_done) return;
Chris@211 234
Chris@208 235 emit progress(100);
Chris@210 236
Chris@208 237 if (error) {
Chris@208 238 if (m_http) {
Chris@208 239 m_errorString = m_http->errorString();
Chris@208 240 } else if (m_ftp) {
Chris@208 241 m_errorString = m_ftp->errorString();
Chris@208 242 }
Chris@208 243 }
Chris@208 244
Chris@210 245 if (m_lastStatus / 100 >= 4) {
Chris@211 246 error = true;
Chris@210 247 }
Chris@210 248
Chris@211 249 cleanup();
Chris@208 250
Chris@211 251 if (!error) {
Chris@208 252 QFileInfo fi(m_localFilename);
Chris@208 253 if (!fi.exists()) {
Chris@208 254 m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
Chris@211 255 error = true;
Chris@208 256 } else if (fi.size() == 0) {
Chris@208 257 m_errorString = tr("File contains no data!");
Chris@211 258 error = true;
Chris@208 259 }
Chris@208 260 }
Chris@211 261
Chris@211 262 if (error) {
Chris@211 263 deleteLocalFile();
Chris@211 264 }
Chris@211 265
Chris@211 266 m_ok = !error;
Chris@211 267 m_done = true;
Chris@211 268 }
Chris@211 269
Chris@211 270 void
Chris@211 271 RemoteFile::deleteLocalFile()
Chris@211 272 {
Chris@211 273 // std::cerr << "RemoteFile::deleteLocalFile" << std::endl;
Chris@211 274
Chris@211 275 cleanup();
Chris@211 276
Chris@211 277 if (m_localFilename == "") return;
Chris@211 278
Chris@211 279 m_fileCreationMutex.lock();
Chris@211 280
Chris@211 281 if (!QFile(m_localFilename).remove()) {
Chris@211 282 std::cerr << "RemoteFile::deleteLocalFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
Chris@211 283 } else {
Chris@211 284 m_localFilename = "";
Chris@211 285 }
Chris@211 286
Chris@211 287 m_fileCreationMutex.unlock();
Chris@211 288
Chris@208 289 m_done = true;
Chris@208 290 }
Chris@208 291
Chris@210 292 void
Chris@210 293 RemoteFile::showProgressDialog()
Chris@210 294 {
Chris@210 295 if (m_progressDialog) m_progressDialog->show();
Chris@210 296 }
Chris@210 297
Chris@208 298 QString
Chris@208 299 RemoteFile::createLocalFile(QUrl url)
Chris@208 300 {
Chris@208 301 QDir dir;
Chris@208 302 try {
Chris@208 303 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
Chris@208 304 } catch (DirectoryCreationFailed f) {
Chris@208 305 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
Chris@208 306 return "";
Chris@208 307 }
Chris@208 308
Chris@208 309 QString filepart = url.path().section('/', -1, -1,
Chris@208 310 QString::SectionSkipEmpty);
Chris@208 311
Chris@208 312 QString extension = filepart.section('.', -1);
Chris@208 313 QString base = filepart;
Chris@208 314 if (extension != "") {
Chris@208 315 base = base.left(base.length() - extension.length() - 1);
Chris@208 316 }
Chris@208 317 if (base == "") base = "remote";
Chris@208 318
Chris@208 319 QString filename;
Chris@208 320
Chris@208 321 if (extension == "") {
Chris@208 322 filename = base;
Chris@208 323 } else {
Chris@208 324 filename = QString("%1.%2").arg(base).arg(extension);
Chris@208 325 }
Chris@208 326
Chris@208 327 QString filepath(dir.filePath(filename));
Chris@208 328
Chris@208 329 std::cerr << "RemoteFile::createLocalFile: URL is \"" << url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl;
Chris@208 330
Chris@208 331 m_fileCreationMutex.lock();
Chris@208 332 ++m_count;
Chris@208 333
Chris@208 334 if (QFileInfo(filepath).exists() ||
Chris@208 335 !QFile(filepath).open(QFile::WriteOnly)) {
Chris@208 336
Chris@208 337 std::cerr << "RemoteFile::createLocalFile: Failed to create local file \""
Chris@208 338 << filepath.toStdString() << "\" for URL \""
Chris@208 339 << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
Chris@208 340
Chris@208 341
Chris@208 342 if (extension == "") {
Chris@208 343 filename = QString("%1_%2").arg(base).arg(m_count);
Chris@208 344 } else {
Chris@208 345 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
Chris@208 346 }
Chris@208 347 filepath = dir.filePath(filename);
Chris@208 348
Chris@208 349 if (QFileInfo(filepath).exists() ||
Chris@208 350 !QFile(filepath).open(QFile::WriteOnly)) {
Chris@208 351
Chris@208 352 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \""
Chris@208 353 << filepath.toStdString() << "\" for URL \""
Chris@208 354 << url.toString().toStdString() << "\" (or file already exists)" << std::endl;
Chris@208 355
Chris@208 356 m_fileCreationMutex.unlock();
Chris@208 357 return "";
Chris@208 358 }
Chris@208 359 }
Chris@208 360
Chris@208 361 m_fileCreationMutex.unlock();
Chris@208 362
Chris@208 363 std::cerr << "RemoteFile::createLocalFile: url "
Chris@208 364 << url.toString().toStdString() << " -> local filename "
Chris@208 365 << filepath.toStdString() << std::endl;
Chris@208 366
Chris@208 367 return filepath;
Chris@208 368 }