# HG changeset patch # User Chris Cannam # Date 1168275875 0 # Node ID 8a3d68910b372e43f1bc958b41fb6d8a1b812ff3 # Parent 8ee6cf529c4e6b4f2a660cff95021e232badb744 * Framework for retrieving files from remote locations diff -r 8ee6cf529c4e -r 8a3d68910b37 data/data.pro --- a/data/data.pro Fri Jan 05 15:49:10 2007 +0000 +++ b/data/data.pro Mon Jan 08 17:04:35 2007 +0000 @@ -4,6 +4,7 @@ load(../sv.prf) CONFIG += sv staticlib qt thread warn_on stl rtti exceptions +QT += network TARGET = svdata @@ -30,6 +31,7 @@ fileio/MIDIFileReader.h \ fileio/MP3FileReader.h \ fileio/OggVorbisFileReader.h \ + fileio/RemoteFile.h \ fileio/WavFileReader.h \ fileio/WavFileWriter.h \ model/DenseThreeDimensionalModel.h \ @@ -63,6 +65,7 @@ fileio/MIDIFileReader.cpp \ fileio/MP3FileReader.cpp \ fileio/OggVorbisFileReader.cpp \ + fileio/RemoteFile.cpp \ fileio/WavFileReader.cpp \ fileio/WavFileWriter.cpp \ model/DenseTimeValueModel.cpp \ diff -r 8ee6cf529c4e -r 8a3d68910b37 data/fileio/RemoteFile.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/RemoteFile.cpp Mon Jan 08 17:04:35 2007 +0000 @@ -0,0 +1,270 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "RemoteFile.h" +#include "base/TempDirectory.h" +#include "base/Exceptions.h" + +#include +#include +#include +#include +#include +#include + +#include + +int +RemoteFile::m_count = 0; + +QMutex +RemoteFile::m_fileCreationMutex; + +RemoteFile::RemoteFile(QUrl url) : + m_ftp(0), + m_http(0), + m_localFile(0), + m_ok(false), + m_done(false), + m_progressDialog(0) +{ + if (!canHandleScheme(url)) { + std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << url.toString().toStdString() << "\"" << std::endl; + return; + } + + m_localFilename = createLocalFile(url); + if (m_localFilename == "") return; + m_localFile = new QFile(m_localFilename); + m_localFile->open(QFile::WriteOnly); + + QString scheme = url.scheme().toLower(); + + if (scheme == "http") { + + m_http = new QHttp(url.host(), url.port(80)); + connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool))); + connect(m_http, SIGNAL(dataReadProgress(int, int)), + this, SLOT(dataReadProgress(int, int))); + m_http->get(url.path(), m_localFile); + m_ok = true; + + } else if (scheme == "ftp") { + + m_ftp = new QFtp; + connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool))); + connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)), + this, SLOT(dataTransferProgress(qint64, qint64))); + m_ftp->connectToHost(url.host(), url.port(21)); + + QString username = url.userName(); + if (username == "") { + username = "anonymous"; + } + + QString password = url.password(); + if (password == "") { + password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST")); + } + + QStringList path = url.path().split('/'); + for (QStringList::iterator i = path.begin(); i != path.end(); ) { + QString bit = *i; + ++i; + if (i != path.end()) { + m_ftp->cd(*i); + } else { + m_ftp->get(*i, m_localFile); + } + } + + m_ok = true; + } + + if (m_ok) { + m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100); + m_progressDialog->show(); + } +} + +RemoteFile::~RemoteFile() +{ + delete m_ftp; + delete m_http; + delete m_localFile; + delete m_progressDialog; +} + +bool +RemoteFile::canHandleScheme(QUrl url) +{ + QString scheme = url.scheme().toLower(); + return (scheme == "http" || scheme == "ftp"); +} + +void +RemoteFile::wait() +{ + while (!m_done) { + QApplication::processEvents(); + } +} + +bool +RemoteFile::isOK() const +{ + return m_ok; +} + +bool +RemoteFile::isDone() const +{ + return m_done; +} + +QString +RemoteFile::getLocalFilename() const +{ + return m_localFilename; +} + +QString +RemoteFile::getErrorString() const +{ + return m_errorString; +} + +void +RemoteFile::dataReadProgress(int done, int total) +{ + dataTransferProgress(done, total); +} + +void +RemoteFile::dataTransferProgress(qint64 done, qint64 total) +{ + int percent = int((double(done) / double(total)) * 100.0 - 0.1); + emit progress(percent); + + m_progressDialog->setValue(percent); +} + +void +RemoteFile::done(bool error) +{ + //!!! need to identify 404s etc in the return headers + + emit progress(100); + m_ok = !error; + if (error) { + if (m_http) { + m_errorString = m_http->errorString(); + } else if (m_ftp) { + m_errorString = m_ftp->errorString(); + } + } + + delete m_localFile; + m_localFile = 0; + + delete m_progressDialog; + m_progressDialog = 0; + + if (m_ok) { + QFileInfo fi(m_localFilename); + if (!fi.exists()) { + m_errorString = tr("Failed to create local file %1").arg(m_localFilename); + m_ok = false; + } else if (fi.size() == 0) { + m_errorString = tr("File contains no data!"); + m_ok = false; + } + } + m_done = true; +} + +QString +RemoteFile::createLocalFile(QUrl url) +{ + //!!! should we actually put up dialogs for these errors? or propagate an exception? + + QDir dir; + try { + dir = TempDirectory::getInstance()->getSubDirectoryPath("download"); + } catch (DirectoryCreationFailed f) { + std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl; + return ""; + } + + QString filepart = url.path().section('/', -1, -1, + QString::SectionSkipEmpty); + + QString extension = filepart.section('.', -1); + QString base = filepart; + if (extension != "") { + base = base.left(base.length() - extension.length() - 1); + } + if (base == "") base = "remote"; + + QString filename; + + if (extension == "") { + filename = base; + } else { + filename = QString("%1.%2").arg(base).arg(extension); + } + + QString filepath(dir.filePath(filename)); + + 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; + + m_fileCreationMutex.lock(); + ++m_count; + + if (QFileInfo(filepath).exists() || + !QFile(filepath).open(QFile::WriteOnly)) { + + std::cerr << "RemoteFile::createLocalFile: Failed to create local file \"" + << filepath.toStdString() << "\" for URL \"" + << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl; + + + if (extension == "") { + filename = QString("%1_%2").arg(base).arg(m_count); + } else { + filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension); + } + filepath = dir.filePath(filename); + + if (QFileInfo(filepath).exists() || + !QFile(filepath).open(QFile::WriteOnly)) { + + std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \"" + << filepath.toStdString() << "\" for URL \"" + << url.toString().toStdString() << "\" (or file already exists)" << std::endl; + + m_fileCreationMutex.unlock(); + return ""; + } + } + + m_fileCreationMutex.unlock(); + + std::cerr << "RemoteFile::createLocalFile: url " + << url.toString().toStdString() << " -> local filename " + << filepath.toStdString() << std::endl; + + return filepath; +} diff -r 8ee6cf529c4e -r 8a3d68910b37 data/fileio/RemoteFile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/RemoteFile.h Mon Jan 08 17:04:35 2007 +0000 @@ -0,0 +1,70 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _REMOTE_FILE_H_ +#define _REMOTE_FILE_H_ + +#include +#include +#include + +class QFtp; +class QHttp; +class QFile; +class QProgressDialog; + +class RemoteFile : public QObject +{ + Q_OBJECT + +public: + RemoteFile(QUrl url); + virtual ~RemoteFile(); + + void wait(); + + bool isOK() const; + bool isDone() const; + QString getLocalFilename() const; + QString getErrorString() const; + + static bool canHandleScheme(QUrl url); + +signals: + void progress(int percent); + void ready(); + +protected slots: + void dataReadProgress(int done, int total); + void dataTransferProgress(qint64 done, qint64 total); + void done(bool error); + +protected: + QFtp *m_ftp; + QHttp *m_http; + QFile *m_localFile; + QString m_localFilename; + QString m_errorString; + bool m_ok; + bool m_done; + QProgressDialog *m_progressDialog; + + QString createLocalFile(QUrl url); + + static QMutex m_fileCreationMutex; + static int m_count; +}; + +#endif