annotate data/fileio/RemoteFile.cpp @ 263:71dfc6ab3b54

* Threaded mp3/ogg file reading. Not activated yet, as it doesn't work in context (SV needs to know the duration of its main model at the outset)
author Chris Cannam
date Thu, 24 May 2007 16:20:22 +0000
parents ce6f65ab3327
children 9a13687c078b
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@214 66 this, SLOT(httpResponseHeaderReceived(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@214 74 connect(m_ftp, SIGNAL(commandFinished(int, bool)),
Chris@214 75 this, SLOT(ftpCommandFinished(int, bool)));
Chris@208 76 connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
Chris@208 77 this, SLOT(dataTransferProgress(qint64, qint64)));
Chris@208 78 m_ftp->connectToHost(url.host(), url.port(21));
Chris@208 79
Chris@208 80 QString username = url.userName();
Chris@208 81 if (username == "") {
Chris@208 82 username = "anonymous";
Chris@208 83 }
Chris@208 84
Chris@208 85 QString password = url.password();
Chris@208 86 if (password == "") {
Chris@208 87 password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
Chris@208 88 }
Chris@208 89
Chris@214 90 m_ftp->login(username, password);
Chris@214 91
Chris@214 92 QString dirpath = url.path().section('/', 0, -2);
Chris@214 93 QString filename = url.path().section('/', -1);
Chris@214 94
Chris@214 95 if (dirpath == "") dirpath = "/";
Chris@214 96 m_ftp->cd(dirpath);
Chris@214 97 m_ftp->get(filename, m_localFile);
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@214 121 if (m_http) {
Chris@214 122 delete m_http;
Chris@214 123 m_http = 0;
Chris@214 124 }
Chris@214 125 if (m_ftp) {
Chris@214 126 m_ftp->abort();
Chris@214 127 m_ftp->deleteLater();
Chris@214 128 m_ftp = 0;
Chris@214 129 }
Chris@211 130 delete m_progressDialog;
Chris@211 131 m_progressDialog = 0;
Chris@208 132 delete m_localFile;
Chris@211 133 m_localFile = 0;
Chris@208 134 }
Chris@208 135
Chris@208 136 bool
Chris@208 137 RemoteFile::canHandleScheme(QUrl url)
Chris@208 138 {
Chris@208 139 QString scheme = url.scheme().toLower();
Chris@208 140 return (scheme == "http" || scheme == "ftp");
Chris@208 141 }
Chris@208 142
Chris@210 143 bool
Chris@210 144 RemoteFile::isAvailable()
Chris@210 145 {
Chris@211 146 while (m_ok && (!m_done && m_lastStatus == 0)) {
Chris@210 147 QApplication::processEvents();
Chris@210 148 }
Chris@211 149 bool available = true;
Chris@211 150 if (!m_ok) available = false;
Chris@211 151 else available = (m_lastStatus / 100 == 2);
Chris@211 152 std::cerr << "RemoteFile::isAvailable: " << (available ? "yes" : "no")
Chris@211 153 << std::endl;
Chris@211 154 return available;
Chris@210 155 }
Chris@210 156
Chris@208 157 void
Chris@208 158 RemoteFile::wait()
Chris@208 159 {
Chris@211 160 while (m_ok && !m_done) {
Chris@208 161 QApplication::processEvents();
Chris@208 162 }
Chris@208 163 }
Chris@208 164
Chris@208 165 bool
Chris@208 166 RemoteFile::isOK() const
Chris@208 167 {
Chris@208 168 return m_ok;
Chris@208 169 }
Chris@208 170
Chris@208 171 bool
Chris@208 172 RemoteFile::isDone() const
Chris@208 173 {
Chris@208 174 return m_done;
Chris@208 175 }
Chris@208 176
Chris@208 177 QString
Chris@208 178 RemoteFile::getLocalFilename() const
Chris@208 179 {
Chris@208 180 return m_localFilename;
Chris@208 181 }
Chris@208 182
Chris@208 183 QString
Chris@208 184 RemoteFile::getErrorString() const
Chris@208 185 {
Chris@208 186 return m_errorString;
Chris@208 187 }
Chris@208 188
Chris@208 189 void
Chris@208 190 RemoteFile::dataReadProgress(int done, int total)
Chris@208 191 {
Chris@208 192 dataTransferProgress(done, total);
Chris@208 193 }
Chris@208 194
Chris@208 195 void
Chris@214 196 RemoteFile::httpResponseHeaderReceived(const QHttpResponseHeader &resp)
Chris@210 197 {
Chris@210 198 m_lastStatus = resp.statusCode();
Chris@210 199 if (m_lastStatus / 100 >= 4) {
Chris@210 200 m_errorString = QString("%1 %2")
Chris@210 201 .arg(resp.statusCode()).arg(resp.reasonPhrase());
Chris@211 202 std::cerr << "RemoteFile::responseHeaderReceived: "
Chris@211 203 << m_errorString.toStdString() << std::endl;
Chris@211 204 } else {
Chris@211 205 std::cerr << "RemoteFile::responseHeaderReceived: "
Chris@211 206 << m_lastStatus << std::endl;
Chris@211 207 }
Chris@210 208 }
Chris@210 209
Chris@210 210 void
Chris@214 211 RemoteFile::ftpCommandFinished(int id, bool error)
Chris@214 212 {
Chris@214 213 std::cerr << "RemoteFile::ftpCommandFinished(" << id << ", " << error << ")" << std::endl;
Chris@214 214
Chris@214 215 if (!m_ftp) return;
Chris@214 216
Chris@214 217 QFtp::Command command = m_ftp->currentCommand();
Chris@214 218
Chris@214 219 if (!error) {
Chris@214 220 std::cerr << "RemoteFile::ftpCommandFinished: success for command "
Chris@214 221 << command << std::endl;
Chris@214 222 return;
Chris@214 223 }
Chris@214 224
Chris@214 225 if (command == QFtp::ConnectToHost) {
Chris@214 226 m_errorString = tr("Failed to connect to FTP server");
Chris@214 227 } else if (command == QFtp::Login) {
Chris@214 228 m_errorString = tr("Login failed");
Chris@214 229 } else if (command == QFtp::Cd) {
Chris@214 230 m_errorString = tr("Failed to change to correct directory");
Chris@214 231 } else if (command == QFtp::Get) {
Chris@214 232 m_errorString = tr("FTP download aborted");
Chris@214 233 }
Chris@214 234
Chris@214 235 m_lastStatus = 400; // for done()
Chris@214 236 }
Chris@214 237
Chris@214 238 void
Chris@208 239 RemoteFile::dataTransferProgress(qint64 done, qint64 total)
Chris@208 240 {
Chris@211 241 if (!m_progressDialog) return;
Chris@211 242
Chris@208 243 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
Chris@208 244 emit progress(percent);
Chris@208 245
Chris@211 246 if (percent > 0) {
Chris@211 247 m_progressDialog->setValue(percent);
Chris@211 248 m_progressDialog->show();
Chris@211 249 }
Chris@210 250 }
Chris@210 251
Chris@210 252 void
Chris@210 253 RemoteFile::cancelled()
Chris@210 254 {
Chris@211 255 deleteLocalFile();
Chris@210 256 m_done = true;
Chris@210 257 m_ok = false;
Chris@210 258 m_errorString = tr("Download cancelled");
Chris@208 259 }
Chris@208 260
Chris@208 261 void
Chris@208 262 RemoteFile::done(bool error)
Chris@208 263 {
Chris@214 264 std::cerr << "RemoteFile::done(" << error << ")" << std::endl;
Chris@211 265
Chris@211 266 if (m_done) return;
Chris@211 267
Chris@208 268 emit progress(100);
Chris@210 269
Chris@208 270 if (error) {
Chris@208 271 if (m_http) {
Chris@208 272 m_errorString = m_http->errorString();
Chris@208 273 } else if (m_ftp) {
Chris@208 274 m_errorString = m_ftp->errorString();
Chris@208 275 }
Chris@208 276 }
Chris@208 277
Chris@210 278 if (m_lastStatus / 100 >= 4) {
Chris@211 279 error = true;
Chris@210 280 }
Chris@210 281
Chris@211 282 cleanup();
Chris@208 283
Chris@211 284 if (!error) {
Chris@208 285 QFileInfo fi(m_localFilename);
Chris@208 286 if (!fi.exists()) {
Chris@208 287 m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
Chris@211 288 error = true;
Chris@208 289 } else if (fi.size() == 0) {
Chris@208 290 m_errorString = tr("File contains no data!");
Chris@211 291 error = true;
Chris@208 292 }
Chris@208 293 }
Chris@211 294
Chris@211 295 if (error) {
Chris@211 296 deleteLocalFile();
Chris@211 297 }
Chris@211 298
Chris@211 299 m_ok = !error;
Chris@211 300 m_done = true;
Chris@211 301 }
Chris@211 302
Chris@211 303 void
Chris@211 304 RemoteFile::deleteLocalFile()
Chris@211 305 {
Chris@211 306 // std::cerr << "RemoteFile::deleteLocalFile" << std::endl;
Chris@211 307
Chris@211 308 cleanup();
Chris@211 309
Chris@211 310 if (m_localFilename == "") return;
Chris@211 311
Chris@211 312 m_fileCreationMutex.lock();
Chris@211 313
Chris@211 314 if (!QFile(m_localFilename).remove()) {
Chris@211 315 std::cerr << "RemoteFile::deleteLocalFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
Chris@211 316 } else {
Chris@211 317 m_localFilename = "";
Chris@211 318 }
Chris@211 319
Chris@211 320 m_fileCreationMutex.unlock();
Chris@211 321
Chris@208 322 m_done = true;
Chris@208 323 }
Chris@208 324
Chris@210 325 void
Chris@210 326 RemoteFile::showProgressDialog()
Chris@210 327 {
Chris@210 328 if (m_progressDialog) m_progressDialog->show();
Chris@210 329 }
Chris@210 330
Chris@208 331 QString
Chris@208 332 RemoteFile::createLocalFile(QUrl url)
Chris@208 333 {
Chris@208 334 QDir dir;
Chris@208 335 try {
Chris@208 336 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
Chris@208 337 } catch (DirectoryCreationFailed f) {
Chris@208 338 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
Chris@208 339 return "";
Chris@208 340 }
Chris@208 341
Chris@208 342 QString filepart = url.path().section('/', -1, -1,
Chris@208 343 QString::SectionSkipEmpty);
Chris@208 344
Chris@208 345 QString extension = filepart.section('.', -1);
Chris@208 346 QString base = filepart;
Chris@208 347 if (extension != "") {
Chris@208 348 base = base.left(base.length() - extension.length() - 1);
Chris@208 349 }
Chris@208 350 if (base == "") base = "remote";
Chris@208 351
Chris@208 352 QString filename;
Chris@208 353
Chris@208 354 if (extension == "") {
Chris@208 355 filename = base;
Chris@208 356 } else {
Chris@208 357 filename = QString("%1.%2").arg(base).arg(extension);
Chris@208 358 }
Chris@208 359
Chris@208 360 QString filepath(dir.filePath(filename));
Chris@208 361
Chris@208 362 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 363
Chris@208 364 m_fileCreationMutex.lock();
Chris@208 365 ++m_count;
Chris@208 366
Chris@208 367 if (QFileInfo(filepath).exists() ||
Chris@208 368 !QFile(filepath).open(QFile::WriteOnly)) {
Chris@208 369
Chris@208 370 std::cerr << "RemoteFile::createLocalFile: Failed to create local file \""
Chris@208 371 << filepath.toStdString() << "\" for URL \""
Chris@208 372 << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
Chris@208 373
Chris@208 374
Chris@208 375 if (extension == "") {
Chris@208 376 filename = QString("%1_%2").arg(base).arg(m_count);
Chris@208 377 } else {
Chris@208 378 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
Chris@208 379 }
Chris@208 380 filepath = dir.filePath(filename);
Chris@208 381
Chris@208 382 if (QFileInfo(filepath).exists() ||
Chris@208 383 !QFile(filepath).open(QFile::WriteOnly)) {
Chris@208 384
Chris@208 385 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \""
Chris@208 386 << filepath.toStdString() << "\" for URL \""
Chris@208 387 << url.toString().toStdString() << "\" (or file already exists)" << std::endl;
Chris@208 388
Chris@208 389 m_fileCreationMutex.unlock();
Chris@208 390 return "";
Chris@208 391 }
Chris@208 392 }
Chris@208 393
Chris@208 394 m_fileCreationMutex.unlock();
Chris@208 395
Chris@208 396 std::cerr << "RemoteFile::createLocalFile: url "
Chris@208 397 << url.toString().toStdString() << " -> local filename "
Chris@208 398 << filepath.toStdString() << std::endl;
Chris@208 399
Chris@208 400 return filepath;
Chris@208 401 }