# HG changeset patch # User Chris Cannam # Date 1192724426 0 # Node ID c324d410b096d83cb1f399b0cfbf0699c16aaadb # Parent 3a6725f285d6bb45d8aac9b2003d34784cd71fd3 * RemoteFile -> FileSource now it's used all over the place for local files as well. diff -r 3a6725f285d6 -r c324d410b096 data/data.pro --- a/data/data.pro Thu Oct 18 15:31:20 2007 +0000 +++ b/data/data.pro Thu Oct 18 16:20:26 2007 +0000 @@ -29,6 +29,7 @@ fileio/DataFileReaderFactory.h \ fileio/FileFinder.h \ fileio/FileReadThread.h \ + fileio/FileSource.h \ fileio/MatchFileReader.h \ fileio/MatrixFile.h \ fileio/MIDIEvent.h \ @@ -38,7 +39,6 @@ fileio/OggVorbisFileReader.h \ fileio/PlaylistFileReader.h \ fileio/QuickTimeFileReader.h \ - fileio/RemoteFile.h \ fileio/ResamplingWavFileReader.h \ fileio/WavFileReader.h \ fileio/WavFileWriter.h \ @@ -75,6 +75,7 @@ fileio/DataFileReaderFactory.cpp \ fileio/FileFinder.cpp \ fileio/FileReadThread.cpp \ + fileio/FileSource.cpp \ fileio/MatchFileReader.cpp \ fileio/MatrixFile.cpp \ fileio/MIDIFileReader.cpp \ @@ -83,7 +84,6 @@ fileio/OggVorbisFileReader.cpp \ fileio/PlaylistFileReader.cpp \ fileio/QuickTimeFileReader.cpp \ - fileio/RemoteFile.cpp \ fileio/ResamplingWavFileReader.cpp \ fileio/WavFileReader.cpp \ fileio/WavFileWriter.cpp \ diff -r 3a6725f285d6 -r c324d410b096 data/fileio/AudioFileReader.h --- a/data/fileio/AudioFileReader.h Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/AudioFileReader.h Thu Oct 18 16:20:26 2007 +0000 @@ -19,7 +19,7 @@ #include #include "model/Model.h" // for SampleBlock -#include "RemoteFile.h" +#include "FileSource.h" class AudioFileReader : public QObject { diff -r 3a6725f285d6 -r c324d410b096 data/fileio/AudioFileReaderFactory.cpp --- a/data/fileio/AudioFileReaderFactory.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/AudioFileReaderFactory.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -54,7 +54,7 @@ } AudioFileReader * -AudioFileReaderFactory::createReader(RemoteFile source, size_t targetRate) +AudioFileReaderFactory::createReader(FileSource source, size_t targetRate) { QString err; diff -r 3a6725f285d6 -r c324d410b096 data/fileio/AudioFileReaderFactory.h --- a/data/fileio/AudioFileReaderFactory.h Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/AudioFileReaderFactory.h Thu Oct 18 16:20:26 2007 +0000 @@ -18,7 +18,7 @@ #include -#include "RemoteFile.h" +#include "FileSource.h" class AudioFileReader; @@ -44,7 +44,7 @@ * * Caller owns the returned object and must delete it after use. */ - static AudioFileReader *createReader(RemoteFile source, size_t targetRate = 0); + static AudioFileReader *createReader(FileSource source, size_t targetRate = 0); }; #endif diff -r 3a6725f285d6 -r c324d410b096 data/fileio/FileFinder.cpp --- a/data/fileio/FileFinder.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/FileFinder.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -14,7 +14,7 @@ */ #include "FileFinder.h" -#include "RemoteFile.h" +#include "FileSource.h" #include "AudioFileReaderFactory.h" #include "DataFileReaderFactory.h" @@ -380,8 +380,8 @@ { if (QFileInfo(location).exists()) return location; - if (RemoteFile::isRemote(location)) { - if (RemoteFile(location).isAvailable()) { + if (FileSource::isRemote(location)) { + if (FileSource(location).isAvailable()) { std::cerr << "FileFinder::find: ok, it's available... returning" << std::endl; return location; } @@ -411,16 +411,16 @@ QString fileName; QString resolved; - if (RemoteFile::isRemote(location)) { + if (FileSource::isRemote(location)) { fileName = QUrl(location).path().section('/', -1, -1, QString::SectionSkipEmpty); } else { fileName = QFileInfo(location).fileName(); } - if (RemoteFile::isRemote(relativeTo)) { + if (FileSource::isRemote(relativeTo)) { resolved = QUrl(relativeTo).resolved(fileName).toString(); - if (!RemoteFile(resolved).isAvailable()) resolved = ""; + if (!FileSource(resolved).isAvailable()) resolved = ""; std::cerr << "resolved: " << resolved.toStdString() << std::endl; } else { resolved = QFileInfo(relativeTo).dir().filePath(fileName); @@ -479,7 +479,7 @@ QLineEdit::Normal, "", &ok); if (ok && path != "") { - if (RemoteFile(path).isAvailable()) { + if (FileSource(path).isAvailable()) { done = true; } else { QMessageBox::critical diff -r 3a6725f285d6 -r c324d410b096 data/fileio/FileSource.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/FileSource.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -0,0 +1,718 @@ +/* -*- 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 "FileSource.h" +#include "base/TempDirectory.h" +#include "base/Exceptions.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +int +FileSource::m_count = 0; + +QMutex +FileSource::m_fileCreationMutex; + +FileSource::RemoteRefCountMap +FileSource::m_refCountMap; + +FileSource::RemoteLocalMap +FileSource::m_remoteLocalMap; + +QMutex +FileSource::m_mapMutex; + +FileSource::FileSource(QString fileOrUrl, bool showProgress) : + m_url(fileOrUrl), + m_ftp(0), + m_http(0), + m_localFile(0), + m_ok(false), + m_lastStatus(0), + m_remote(isRemote(fileOrUrl)), + m_done(false), + m_leaveLocalFile(false), + m_progressDialog(0), + m_progressShowTimer(this), + m_refCounted(false) +{ + std::cerr << "FileSource::FileSource(" << fileOrUrl.toStdString() << ")" << std::endl; + + if (!canHandleScheme(m_url)) { + std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; + m_errorString = tr("Unsupported scheme in URL"); + return; + } + + init(showProgress); + + if (isRemote() && + (fileOrUrl.contains('%') || + fileOrUrl.contains("--"))) { // for IDNA + + waitForStatus(); + + if (!isAvailable()) { + // The URL was created on the assumption that the string + // was human-readable. Let's try again, this time + // assuming it was already encoded. + std::cerr << "FileSource::FileSource: Failed to retrieve URL \"" + << fileOrUrl.toStdString() + << "\" as human-readable URL; " + << "trying again treating it as encoded URL" + << std::endl; + m_url.setEncodedUrl(fileOrUrl.toAscii()); + init(showProgress); + } + } +} + +FileSource::FileSource(QUrl url, bool showProgress) : + m_url(url), + m_ftp(0), + m_http(0), + m_localFile(0), + m_ok(false), + m_lastStatus(0), + m_remote(isRemote(url.toString())), + m_done(false), + m_leaveLocalFile(false), + m_progressDialog(0), + m_progressShowTimer(this), + m_refCounted(false) +{ + std::cerr << "FileSource::FileSource(" << url.toString().toStdString() << ") [as url]" << std::endl; + + if (!canHandleScheme(m_url)) { + std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; + m_errorString = tr("Unsupported scheme in URL"); + return; + } + + init(showProgress); +} + +FileSource::FileSource(const FileSource &rf) : + QObject(), + m_url(rf.m_url), + m_ftp(0), + m_http(0), + m_localFile(0), + m_ok(rf.m_ok), + m_lastStatus(rf.m_lastStatus), + m_remote(rf.m_remote), + m_done(false), + m_leaveLocalFile(false), + m_progressDialog(0), + m_progressShowTimer(0), + m_refCounted(false) +{ + std::cerr << "FileSource::FileSource(" << m_url.toString().toStdString() << ") [copy ctor]" << std::endl; + + if (!canHandleScheme(m_url)) { + std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; + m_errorString = tr("Unsupported scheme in URL"); + return; + } + + if (!isRemote()) { + m_localFilename = rf.m_localFilename; + } else { + QMutexLocker locker(&m_mapMutex); + std::cerr << "FileSource::FileSource(copy ctor): ref count is " + << m_refCountMap[m_url] << std::endl; + if (m_refCountMap[m_url] > 0) { + m_refCountMap[m_url]++; + std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl; + m_localFilename = m_remoteLocalMap[m_url]; + m_refCounted = true; + } else { + m_ok = false; + m_lastStatus = 404; + } + } + + m_done = true; +} + +FileSource::~FileSource() +{ + std::cerr << "FileSource(" << m_url.toString().toStdString() << ")::~FileSource" << std::endl; + + cleanup(); + + if (isRemote() && !m_leaveLocalFile) deleteCacheFile(); +} + +void +FileSource::init(bool showProgress) +{ + if (!isRemote()) { + m_localFilename = m_url.toLocalFile(); + m_ok = true; + if (!QFileInfo(m_localFilename).exists()) { + m_lastStatus = 404; + } else { + m_lastStatus = 200; + } + m_done = true; + return; + } + + if (createCacheFile()) { + std::cerr << "FileSource::init: Already have this one" << std::endl; + m_ok = true; + if (!QFileInfo(m_localFilename).exists()) { + m_lastStatus = 404; + } else { + m_lastStatus = 200; + } + m_done = true; + return; + } + + if (m_localFilename == "") return; + m_localFile = new QFile(m_localFilename); + m_localFile->open(QFile::WriteOnly); + + QString scheme = m_url.scheme().toLower(); + + std::cerr << "FileSource::init: Don't have local copy of \"" + << m_url.toString().toStdString() << "\", retrieving" << std::endl; + + if (scheme == "http") { + initHttp(); + } else if (scheme == "ftp") { + initFtp(); + } else { + m_remote = false; + m_ok = false; + } + + if (m_ok) { + + QMutexLocker locker(&m_mapMutex); + + if (m_refCountMap[m_url] > 0) { + // someone else has been doing the same thing at the same time, + // but has got there first + cleanup(); + m_refCountMap[m_url]++; + std::cerr << "FileSource::init: Another FileSource has got there first, abandoning our download and using theirs" << std::endl; + m_localFilename = m_remoteLocalMap[m_url]; + m_refCounted = true; + m_ok = true; + if (!QFileInfo(m_localFilename).exists()) { + m_lastStatus = 404; + } + m_done = true; + return; + } + + m_remoteLocalMap[m_url] = m_localFilename; + m_refCountMap[m_url]++; + m_refCounted = true; + + if (showProgress) { + m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(m_url.toString()), tr("Cancel"), 0, 100); + m_progressDialog->hide(); + connect(&m_progressShowTimer, SIGNAL(timeout()), + this, SLOT(showProgressDialog())); + connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled())); + m_progressShowTimer.setSingleShot(true); + m_progressShowTimer.start(2000); + } + } +} + +void +FileSource::initHttp() +{ + m_ok = true; + m_http = new QHttp(m_url.host(), m_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))); + connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)), + this, SLOT(httpResponseHeaderReceived(const QHttpResponseHeader &))); + + // I don't quite understand this. url.path() returns a path + // without percent encoding; for example, spaces appear as + // literal spaces. This generally won't work if sent to the + // server directly. You can retrieve a correctly encoded URL + // from QUrl using url.toEncoded(), but that gives you the + // whole URL; there doesn't seem to be any way to retrieve + // only an encoded path. Furthermore there doesn't seem to be + // any way to convert a retrieved path into an encoded path + // without explicitly specifying that you don't want the path + // separators ("/") to be encoded. (Besides being painful to + // manage, I don't see how this can work correctly in any case + // where a percent-encoded "/" is supposed to appear within a + // path element?) There also seems to be no way to retrieve + // the path plus query string, i.e. everything that I need to + // send to the HTTP server. And no way for QHttp to take a + // QUrl argument. I'm obviously missing something. + + // So, two ways to do this: query the bits from the URL, + // encode them individually, and glue them back together + // again... +/* + QString path = QUrl::toPercentEncoding(m_url.path(), "/"); + QList > query = m_url.queryItems(); + if (!query.empty()) { + QStringList q2; + for (QList >::iterator i = query.begin(); + i != query.end(); ++i) { + q2.push_back(QString("%1=%3") + .arg(QString(QUrl::toPercentEncoding(i->first))) + .arg(QString(QUrl::toPercentEncoding(i->second)))); + } + path = QString("%1%2%3") + .arg(path).arg("?") + .arg(q2.join("&")); + } +*/ + + // ...or, much simpler but relying on knowledge about the + // scheme://host/path/path/query etc format of the URL, we can + // get the whole URL ready-encoded and then split it on "/" as + // appropriate... + + QString path = "/" + QString(m_url.toEncoded()).section('/', 3); + + std::cerr << "FileSource: path is \"" + << path.toStdString() << "\"" << std::endl; + + m_http->get(path, m_localFile); +} + +void +FileSource::initFtp() +{ + m_ok = true; + m_ftp = new QFtp; + connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool))); + connect(m_ftp, SIGNAL(commandFinished(int, bool)), + this, SLOT(ftpCommandFinished(int, bool))); + connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)), + this, SLOT(dataTransferProgress(qint64, qint64))); + m_ftp->connectToHost(m_url.host(), m_url.port(21)); + + QString username = m_url.userName(); + if (username == "") { + username = "anonymous"; + } + + QString password = m_url.password(); + if (password == "") { + password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST")); + } + + m_ftp->login(username, password); + + QString dirpath = m_url.path().section('/', 0, -2); + QString filename = m_url.path().section('/', -1); + + if (dirpath == "") dirpath = "/"; + m_ftp->cd(dirpath); + m_ftp->get(filename, m_localFile); +} + +void +FileSource::cleanup() +{ + m_done = true; + if (m_http) { + QHttp *h = m_http; + m_http = 0; + h->abort(); + h->deleteLater(); + } + if (m_ftp) { + QFtp *f = m_ftp; + m_ftp = 0; + f->abort(); + f->deleteLater(); + } + delete m_progressDialog; + m_progressDialog = 0; + delete m_localFile; // does not actually delete the file + m_localFile = 0; +} + +bool +FileSource::isRemote(QString fileOrUrl) +{ + QString scheme = QUrl(fileOrUrl).scheme().toLower(); + return (scheme == "http" || scheme == "ftp"); +} + +bool +FileSource::canHandleScheme(QUrl url) +{ + QString scheme = url.scheme().toLower(); + return (scheme == "http" || scheme == "ftp" || + scheme == "file" || scheme == ""); +} + +bool +FileSource::isAvailable() +{ + waitForStatus(); + bool available = true; + if (!m_ok) available = false; + else available = (m_lastStatus / 100 == 2); + std::cerr << "FileSource::isAvailable: " << (available ? "yes" : "no") + << std::endl; + return available; +} + +void +FileSource::waitForStatus() +{ + while (m_ok && (!m_done && m_lastStatus == 0)) { +// std::cerr << "waitForStatus: processing (last status " << m_lastStatus << ")" << std::endl; + QApplication::processEvents(); + } +} + +void +FileSource::waitForData() +{ + while (m_ok && !m_done) { + QApplication::processEvents(); + } +} + +void +FileSource::setLeaveLocalFile(bool leave) +{ + m_leaveLocalFile = leave; +} + +bool +FileSource::isOK() const +{ + return m_ok; +} + +bool +FileSource::isDone() const +{ + return m_done; +} + +bool +FileSource::isRemote() const +{ + return m_remote; +} + +QString +FileSource::getLocation() const +{ + return m_url.toString(); +} + +QString +FileSource::getLocalFilename() const +{ + return m_localFilename; +} + +QString +FileSource::getContentType() const +{ + return m_contentType; +} + +QString +FileSource::getExtension() const +{ + if (m_localFilename != "") { + return QFileInfo(m_localFilename).suffix().toLower(); + } else { + return QFileInfo(m_url.toLocalFile()).suffix().toLower(); + } +} + +QString +FileSource::getErrorString() const +{ + return m_errorString; +} + +void +FileSource::dataReadProgress(int done, int total) +{ + dataTransferProgress(done, total); +} + +void +FileSource::httpResponseHeaderReceived(const QHttpResponseHeader &resp) +{ + m_lastStatus = resp.statusCode(); + if (m_lastStatus / 100 >= 4) { + m_errorString = QString("%1 %2") + .arg(resp.statusCode()).arg(resp.reasonPhrase()); + std::cerr << "FileSource::responseHeaderReceived: " + << m_errorString.toStdString() << std::endl; + } else { + std::cerr << "FileSource::responseHeaderReceived: " + << m_lastStatus << std::endl; + if (resp.hasContentType()) m_contentType = resp.contentType(); + } +} + +void +FileSource::ftpCommandFinished(int id, bool error) +{ + std::cerr << "FileSource::ftpCommandFinished(" << id << ", " << error << ")" << std::endl; + + if (!m_ftp) return; + + QFtp::Command command = m_ftp->currentCommand(); + + if (!error) { + std::cerr << "FileSource::ftpCommandFinished: success for command " + << command << std::endl; + return; + } + + if (command == QFtp::ConnectToHost) { + m_errorString = tr("Failed to connect to FTP server"); + } else if (command == QFtp::Login) { + m_errorString = tr("Login failed"); + } else if (command == QFtp::Cd) { + m_errorString = tr("Failed to change to correct directory"); + } else if (command == QFtp::Get) { + m_errorString = tr("FTP download aborted"); + } + + m_lastStatus = 400; // for done() +} + +void +FileSource::dataTransferProgress(qint64 done, qint64 total) +{ + if (!m_progressDialog) return; + + int percent = int((double(done) / double(total)) * 100.0 - 0.1); + emit progress(percent); + + if (percent > 0) { + m_progressDialog->setValue(percent); + m_progressDialog->show(); + } +} + +void +FileSource::cancelled() +{ + m_done = true; + cleanup(); + + m_ok = false; + m_errorString = tr("Download cancelled"); +} + +void +FileSource::done(bool error) +{ + std::cerr << "FileSource::done(" << error << ")" << std::endl; + + if (m_done) return; + + emit progress(100); + + if (error) { + if (m_http) { + m_errorString = m_http->errorString(); + } else if (m_ftp) { + m_errorString = m_ftp->errorString(); + } + } + + if (m_lastStatus / 100 >= 4) { + error = true; + } + + cleanup(); + + if (!error) { + QFileInfo fi(m_localFilename); + if (!fi.exists()) { + m_errorString = tr("Failed to create local file %1").arg(m_localFilename); + error = true; + } else if (fi.size() == 0) { + m_errorString = tr("File contains no data!"); + error = true; + } + } + + if (error) { + std::cerr << "FileSource::done: error is " << error << ", deleting cache file" << std::endl; + deleteCacheFile(); + } + + m_ok = !error; + m_done = true; + emit ready(); +} + +void +FileSource::deleteCacheFile() +{ + std::cerr << "FileSource::deleteCacheFile(\"" << m_localFilename.toStdString() << "\")" << std::endl; + + cleanup(); + + if (m_localFilename == "") { + return; + } + + if (!isRemote()) { + std::cerr << "not a cache file" << std::endl; + return; + } + + if (m_refCounted) { + + QMutexLocker locker(&m_mapMutex); + m_refCounted = false; + + if (m_refCountMap[m_url] > 0) { + m_refCountMap[m_url]--; + std::cerr << "reduced ref count to " << m_refCountMap[m_url] << std::endl; + if (m_refCountMap[m_url] > 0) { + m_done = true; + return; + } + } + } + + m_fileCreationMutex.lock(); + + if (!QFile(m_localFilename).remove()) { + std::cerr << "FileSource::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl; + } else { + std::cerr << "FileSource::deleteCacheFile: Deleted cache file \"" << m_localFilename.toStdString() << "\"" << std::endl; + m_localFilename = ""; + } + + m_fileCreationMutex.unlock(); + + m_done = true; +} + +void +FileSource::showProgressDialog() +{ + if (m_progressDialog) m_progressDialog->show(); +} + +bool +FileSource::createCacheFile() +{ + { + QMutexLocker locker(&m_mapMutex); + + std::cerr << "FileSource::createCacheFile: refcount is " << m_refCountMap[m_url] << std::endl; + + if (m_refCountMap[m_url] > 0) { + m_refCountMap[m_url]++; + m_localFilename = m_remoteLocalMap[m_url]; + std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl; + m_refCounted = true; + return true; + } + } + + QDir dir; + try { + dir = TempDirectory::getInstance()->getSubDirectoryPath("download"); + } catch (DirectoryCreationFailed f) { + std::cerr << "FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl; + return ""; + } + + QString filepart = m_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 << "FileSource::createCacheFile: URL is \"" << m_url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl; + + QMutexLocker fcLocker(&m_fileCreationMutex); + + ++m_count; + + if (QFileInfo(filepath).exists() || + !QFile(filepath).open(QFile::WriteOnly)) { + + std::cerr << "FileSource::createCacheFile: Failed to create local file \"" + << filepath.toStdString() << "\" for URL \"" + << m_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 << "FileSource::createCacheFile: ERROR: Failed to create local file \"" + << filepath.toStdString() << "\" for URL \"" + << m_url.toString().toStdString() << "\" (or file already exists)" << std::endl; + + return ""; + } + } + + std::cerr << "FileSource::createCacheFile: url " + << m_url.toString().toStdString() << " -> local filename " + << filepath.toStdString() << std::endl; + + m_localFilename = filepath; + + return false; +} diff -r 3a6725f285d6 -r c324d410b096 data/fileio/FileSource.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/FileSource.h Thu Oct 18 16:20:26 2007 +0000 @@ -0,0 +1,117 @@ +/* -*- 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 +#include + +#include + +class QFtp; +class QHttp; +class QFile; +class QProgressDialog; +class QHttpResponseHeader; + +class FileSource : public QObject +{ + Q_OBJECT + +public: + FileSource(QString fileOrUrl, bool showProgress = true); + FileSource(QUrl url, bool showProgress = true); + FileSource(const FileSource &); + + virtual ~FileSource(); + + bool isAvailable(); + + void waitForStatus(); + void waitForData(); + + void setLeaveLocalFile(bool leave); + + bool isOK() const; + bool isDone() const; + bool isRemote() const; + + QString getLocation() const; + QString getLocalFilename() const; + QString getContentType() const; + QString getExtension() const; + + QString getErrorString() const; + + static bool isRemote(QString fileOrUrl); + static bool canHandleScheme(QUrl url); + +signals: + void progress(int percent); + void ready(); + +protected slots: + void dataReadProgress(int done, int total); + void httpResponseHeaderReceived(const QHttpResponseHeader &resp); + void ftpCommandFinished(int, bool); + void dataTransferProgress(qint64 done, qint64 total); + void done(bool error); + void showProgressDialog(); + void cancelled(); + +protected: + FileSource &operator=(const FileSource &); // not provided + + QUrl m_url; + QFtp *m_ftp; + QHttp *m_http; + QFile *m_localFile; + QString m_localFilename; + QString m_errorString; + QString m_contentType; + bool m_ok; + int m_lastStatus; + bool m_remote; + bool m_done; + bool m_leaveLocalFile; + QProgressDialog *m_progressDialog; + QTimer m_progressShowTimer; + + typedef std::map RemoteRefCountMap; + typedef std::map RemoteLocalMap; + static RemoteRefCountMap m_refCountMap; + static RemoteLocalMap m_remoteLocalMap; + static QMutex m_mapMutex; + bool m_refCounted; + + void init(bool showProgress); + void initHttp(); + void initFtp(); + + void cleanup(); + + // Create a local file for m_url. If it already existed, return true. + // The local filename is stored in m_localFilename. + bool createCacheFile(); + void deleteCacheFile(); + + static QMutex m_fileCreationMutex; + static int m_count; +}; + +#endif diff -r 3a6725f285d6 -r c324d410b096 data/fileio/MP3FileReader.cpp --- a/data/fileio/MP3FileReader.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/MP3FileReader.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -34,7 +34,7 @@ #include #include -MP3FileReader::MP3FileReader(RemoteFile source, DecodeMode decodeMode, +MP3FileReader::MP3FileReader(FileSource source, DecodeMode decodeMode, CacheMode mode, size_t targetRate) : CodedAudioFileReader(mode, targetRate), m_source(source), @@ -410,7 +410,7 @@ } bool -MP3FileReader::supports(RemoteFile &source) +MP3FileReader::supports(FileSource &source) { return (supportsExtension(source.getExtension()) || supportsContentType(source.getContentType())); diff -r 3a6725f285d6 -r c324d410b096 data/fileio/MP3FileReader.h --- a/data/fileio/MP3FileReader.h Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/MP3FileReader.h Thu Oct 18 16:20:26 2007 +0000 @@ -35,7 +35,7 @@ DecodeThreaded // decode in a background thread after construction }; - MP3FileReader(RemoteFile source, + MP3FileReader(FileSource source, DecodeMode decodeMode, CacheMode cacheMode, size_t targetRate = 0); @@ -48,7 +48,7 @@ static void getSupportedExtensions(std::set &extensions); static bool supportsExtension(QString ext); static bool supportsContentType(QString type); - static bool supports(RemoteFile &source); + static bool supports(FileSource &source); virtual int getDecodeCompletion() const { return m_completion; } @@ -57,7 +57,7 @@ } protected: - RemoteFile m_source; + FileSource m_source; QString m_path; QString m_error; QString m_title; diff -r 3a6725f285d6 -r c324d410b096 data/fileio/OggVorbisFileReader.cpp --- a/data/fileio/OggVorbisFileReader.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -32,7 +32,7 @@ static int instances = 0; -OggVorbisFileReader::OggVorbisFileReader(RemoteFile source, +OggVorbisFileReader::OggVorbisFileReader(FileSource source, DecodeMode decodeMode, CacheMode mode, size_t targetRate) : @@ -217,7 +217,7 @@ } bool -OggVorbisFileReader::supports(RemoteFile &source) +OggVorbisFileReader::supports(FileSource &source) { return (supportsExtension(source.getExtension()) || supportsContentType(source.getContentType())); diff -r 3a6725f285d6 -r c324d410b096 data/fileio/OggVorbisFileReader.h --- a/data/fileio/OggVorbisFileReader.h Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.h Thu Oct 18 16:20:26 2007 +0000 @@ -37,7 +37,7 @@ DecodeThreaded // decode in a background thread after construction }; - OggVorbisFileReader(RemoteFile source, + OggVorbisFileReader(FileSource source, DecodeMode decodeMode, CacheMode cacheMode, size_t targetRate = 0); @@ -50,7 +50,7 @@ static void getSupportedExtensions(std::set &extensions); static bool supportsExtension(QString ext); static bool supportsContentType(QString type); - static bool supports(RemoteFile &source); + static bool supports(FileSource &source); virtual int getDecodeCompletion() const { return m_completion; } @@ -59,7 +59,7 @@ } protected: - RemoteFile m_source; + FileSource m_source; QString m_path; QString m_error; QString m_title; diff -r 3a6725f285d6 -r c324d410b096 data/fileio/QuickTimeFileReader.cpp --- a/data/fileio/QuickTimeFileReader.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/QuickTimeFileReader.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -48,7 +48,7 @@ }; -QuickTimeFileReader::QuickTimeFileReader(RemoteFile source, +QuickTimeFileReader::QuickTimeFileReader(FileSource source, DecodeMode decodeMode, CacheMode mode, size_t targetRate) : @@ -357,7 +357,7 @@ } bool -QuickTimeFileReader::supports(RemoteFile &source) +QuickTimeFileReader::supports(FileSource &source) { return (supportsExtension(source.getExtension()) || supportsContentType(source.getContentType())); diff -r 3a6725f285d6 -r c324d410b096 data/fileio/QuickTimeFileReader.h --- a/data/fileio/QuickTimeFileReader.h Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/QuickTimeFileReader.h Thu Oct 18 16:20:26 2007 +0000 @@ -37,7 +37,7 @@ DecodeThreaded // decode in a background thread after construction }; - QuickTimeFileReader(RemoteFile source, + QuickTimeFileReader(FileSource source, DecodeMode decodeMode, CacheMode cacheMode, size_t targetRate = 0); @@ -49,7 +49,7 @@ static void getSupportedExtensions(std::set &extensions); static bool supportsExtension(QString ext); static bool supportsContentType(QString type); - static bool supports(RemoteFile &source); + static bool supports(FileSource &source); virtual int getDecodeCompletion() const { return m_completion; } @@ -58,7 +58,7 @@ } protected: - RemoteFile m_source; + FileSource m_source; QString m_path; QString m_error; QString m_title; diff -r 3a6725f285d6 -r c324d410b096 data/fileio/RemoteFile.cpp --- a/data/fileio/RemoteFile.cpp Thu Oct 18 15:31:20 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,718 +0,0 @@ -/* -*- 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 - -#include - -int -RemoteFile::m_count = 0; - -QMutex -RemoteFile::m_fileCreationMutex; - -RemoteFile::RemoteRefCountMap -RemoteFile::m_refCountMap; - -RemoteFile::RemoteLocalMap -RemoteFile::m_remoteLocalMap; - -QMutex -RemoteFile::m_mapMutex; - -RemoteFile::RemoteFile(QString fileOrUrl, bool showProgress) : - m_url(fileOrUrl), - m_ftp(0), - m_http(0), - m_localFile(0), - m_ok(false), - m_lastStatus(0), - m_remote(isRemote(fileOrUrl)), - m_done(false), - m_leaveLocalFile(false), - m_progressDialog(0), - m_progressShowTimer(this), - m_refCounted(false) -{ - std::cerr << "RemoteFile::RemoteFile(" << fileOrUrl.toStdString() << ")" << std::endl; - - if (!canHandleScheme(m_url)) { - std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; - m_errorString = tr("Unsupported scheme in URL"); - return; - } - - init(showProgress); - - if (isRemote() && - (fileOrUrl.contains('%') || - fileOrUrl.contains("--"))) { // for IDNA - - waitForStatus(); - - if (!isAvailable()) { - // The URL was created on the assumption that the string - // was human-readable. Let's try again, this time - // assuming it was already encoded. - std::cerr << "RemoteFile::RemoteFile: Failed to retrieve URL \"" - << fileOrUrl.toStdString() - << "\" as human-readable URL; " - << "trying again treating it as encoded URL" - << std::endl; - m_url.setEncodedUrl(fileOrUrl.toAscii()); - init(showProgress); - } - } -} - -RemoteFile::RemoteFile(QUrl url, bool showProgress) : - m_url(url), - m_ftp(0), - m_http(0), - m_localFile(0), - m_ok(false), - m_lastStatus(0), - m_remote(isRemote(url.toString())), - m_done(false), - m_leaveLocalFile(false), - m_progressDialog(0), - m_progressShowTimer(this), - m_refCounted(false) -{ - std::cerr << "RemoteFile::RemoteFile(" << url.toString().toStdString() << ") [as url]" << std::endl; - - if (!canHandleScheme(m_url)) { - std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; - m_errorString = tr("Unsupported scheme in URL"); - return; - } - - init(showProgress); -} - -RemoteFile::RemoteFile(const RemoteFile &rf) : - QObject(), - m_url(rf.m_url), - m_ftp(0), - m_http(0), - m_localFile(0), - m_ok(rf.m_ok), - m_lastStatus(rf.m_lastStatus), - m_remote(rf.m_remote), - m_done(false), - m_leaveLocalFile(false), - m_progressDialog(0), - m_progressShowTimer(0), - m_refCounted(false) -{ - std::cerr << "RemoteFile::RemoteFile(" << m_url.toString().toStdString() << ") [copy ctor]" << std::endl; - - if (!canHandleScheme(m_url)) { - std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; - m_errorString = tr("Unsupported scheme in URL"); - return; - } - - if (!isRemote()) { - m_localFilename = rf.m_localFilename; - } else { - QMutexLocker locker(&m_mapMutex); - std::cerr << "RemoteFile::RemoteFile(copy ctor): ref count is " - << m_refCountMap[m_url] << std::endl; - if (m_refCountMap[m_url] > 0) { - m_refCountMap[m_url]++; - std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl; - m_localFilename = m_remoteLocalMap[m_url]; - m_refCounted = true; - } else { - m_ok = false; - m_lastStatus = 404; - } - } - - m_done = true; -} - -RemoteFile::~RemoteFile() -{ - std::cerr << "RemoteFile(" << m_url.toString().toStdString() << ")::~RemoteFile" << std::endl; - - cleanup(); - - if (isRemote() && !m_leaveLocalFile) deleteCacheFile(); -} - -void -RemoteFile::init(bool showProgress) -{ - if (!isRemote()) { - m_localFilename = m_url.toLocalFile(); - m_ok = true; - if (!QFileInfo(m_localFilename).exists()) { - m_lastStatus = 404; - } else { - m_lastStatus = 200; - } - m_done = true; - return; - } - - if (createCacheFile()) { - std::cerr << "RemoteFile::init: Already have this one" << std::endl; - m_ok = true; - if (!QFileInfo(m_localFilename).exists()) { - m_lastStatus = 404; - } else { - m_lastStatus = 200; - } - m_done = true; - return; - } - - if (m_localFilename == "") return; - m_localFile = new QFile(m_localFilename); - m_localFile->open(QFile::WriteOnly); - - QString scheme = m_url.scheme().toLower(); - - std::cerr << "RemoteFile::init: Don't have local copy of \"" - << m_url.toString().toStdString() << "\", retrieving" << std::endl; - - if (scheme == "http") { - initHttp(); - } else if (scheme == "ftp") { - initFtp(); - } else { - m_remote = false; - m_ok = false; - } - - if (m_ok) { - - QMutexLocker locker(&m_mapMutex); - - if (m_refCountMap[m_url] > 0) { - // someone else has been doing the same thing at the same time, - // but has got there first - cleanup(); - m_refCountMap[m_url]++; - std::cerr << "RemoteFile::init: Another RemoteFile has got there first, abandoning our download and using theirs" << std::endl; - m_localFilename = m_remoteLocalMap[m_url]; - m_refCounted = true; - m_ok = true; - if (!QFileInfo(m_localFilename).exists()) { - m_lastStatus = 404; - } - m_done = true; - return; - } - - m_remoteLocalMap[m_url] = m_localFilename; - m_refCountMap[m_url]++; - m_refCounted = true; - - if (showProgress) { - m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(m_url.toString()), tr("Cancel"), 0, 100); - m_progressDialog->hide(); - connect(&m_progressShowTimer, SIGNAL(timeout()), - this, SLOT(showProgressDialog())); - connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled())); - m_progressShowTimer.setSingleShot(true); - m_progressShowTimer.start(2000); - } - } -} - -void -RemoteFile::initHttp() -{ - m_ok = true; - m_http = new QHttp(m_url.host(), m_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))); - connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)), - this, SLOT(httpResponseHeaderReceived(const QHttpResponseHeader &))); - - // I don't quite understand this. url.path() returns a path - // without percent encoding; for example, spaces appear as - // literal spaces. This generally won't work if sent to the - // server directly. You can retrieve a correctly encoded URL - // from QUrl using url.toEncoded(), but that gives you the - // whole URL; there doesn't seem to be any way to retrieve - // only an encoded path. Furthermore there doesn't seem to be - // any way to convert a retrieved path into an encoded path - // without explicitly specifying that you don't want the path - // separators ("/") to be encoded. (Besides being painful to - // manage, I don't see how this can work correctly in any case - // where a percent-encoded "/" is supposed to appear within a - // path element?) There also seems to be no way to retrieve - // the path plus query string, i.e. everything that I need to - // send to the HTTP server. And no way for QHttp to take a - // QUrl argument. I'm obviously missing something. - - // So, two ways to do this: query the bits from the URL, - // encode them individually, and glue them back together - // again... -/* - QString path = QUrl::toPercentEncoding(m_url.path(), "/"); - QList > query = m_url.queryItems(); - if (!query.empty()) { - QStringList q2; - for (QList >::iterator i = query.begin(); - i != query.end(); ++i) { - q2.push_back(QString("%1=%3") - .arg(QString(QUrl::toPercentEncoding(i->first))) - .arg(QString(QUrl::toPercentEncoding(i->second)))); - } - path = QString("%1%2%3") - .arg(path).arg("?") - .arg(q2.join("&")); - } -*/ - - // ...or, much simpler but relying on knowledge about the - // scheme://host/path/path/query etc format of the URL, we can - // get the whole URL ready-encoded and then split it on "/" as - // appropriate... - - QString path = "/" + QString(m_url.toEncoded()).section('/', 3); - - std::cerr << "RemoteFile: path is \"" - << path.toStdString() << "\"" << std::endl; - - m_http->get(path, m_localFile); -} - -void -RemoteFile::initFtp() -{ - m_ok = true; - m_ftp = new QFtp; - connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool))); - connect(m_ftp, SIGNAL(commandFinished(int, bool)), - this, SLOT(ftpCommandFinished(int, bool))); - connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)), - this, SLOT(dataTransferProgress(qint64, qint64))); - m_ftp->connectToHost(m_url.host(), m_url.port(21)); - - QString username = m_url.userName(); - if (username == "") { - username = "anonymous"; - } - - QString password = m_url.password(); - if (password == "") { - password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST")); - } - - m_ftp->login(username, password); - - QString dirpath = m_url.path().section('/', 0, -2); - QString filename = m_url.path().section('/', -1); - - if (dirpath == "") dirpath = "/"; - m_ftp->cd(dirpath); - m_ftp->get(filename, m_localFile); -} - -void -RemoteFile::cleanup() -{ - m_done = true; - if (m_http) { - QHttp *h = m_http; - m_http = 0; - h->abort(); - h->deleteLater(); - } - if (m_ftp) { - QFtp *f = m_ftp; - m_ftp = 0; - f->abort(); - f->deleteLater(); - } - delete m_progressDialog; - m_progressDialog = 0; - delete m_localFile; // does not actually delete the file - m_localFile = 0; -} - -bool -RemoteFile::isRemote(QString fileOrUrl) -{ - QString scheme = QUrl(fileOrUrl).scheme().toLower(); - return (scheme == "http" || scheme == "ftp"); -} - -bool -RemoteFile::canHandleScheme(QUrl url) -{ - QString scheme = url.scheme().toLower(); - return (scheme == "http" || scheme == "ftp" || - scheme == "file" || scheme == ""); -} - -bool -RemoteFile::isAvailable() -{ - waitForStatus(); - bool available = true; - if (!m_ok) available = false; - else available = (m_lastStatus / 100 == 2); - std::cerr << "RemoteFile::isAvailable: " << (available ? "yes" : "no") - << std::endl; - return available; -} - -void -RemoteFile::waitForStatus() -{ - while (m_ok && (!m_done && m_lastStatus == 0)) { -// std::cerr << "waitForStatus: processing (last status " << m_lastStatus << ")" << std::endl; - QApplication::processEvents(); - } -} - -void -RemoteFile::waitForData() -{ - while (m_ok && !m_done) { - QApplication::processEvents(); - } -} - -void -RemoteFile::setLeaveLocalFile(bool leave) -{ - m_leaveLocalFile = leave; -} - -bool -RemoteFile::isOK() const -{ - return m_ok; -} - -bool -RemoteFile::isDone() const -{ - return m_done; -} - -bool -RemoteFile::isRemote() const -{ - return m_remote; -} - -QString -RemoteFile::getLocation() const -{ - return m_url.toString(); -} - -QString -RemoteFile::getLocalFilename() const -{ - return m_localFilename; -} - -QString -RemoteFile::getContentType() const -{ - return m_contentType; -} - -QString -RemoteFile::getExtension() const -{ - if (m_localFilename != "") { - return QFileInfo(m_localFilename).suffix().toLower(); - } else { - return QFileInfo(m_url.toLocalFile()).suffix().toLower(); - } -} - -QString -RemoteFile::getErrorString() const -{ - return m_errorString; -} - -void -RemoteFile::dataReadProgress(int done, int total) -{ - dataTransferProgress(done, total); -} - -void -RemoteFile::httpResponseHeaderReceived(const QHttpResponseHeader &resp) -{ - m_lastStatus = resp.statusCode(); - if (m_lastStatus / 100 >= 4) { - m_errorString = QString("%1 %2") - .arg(resp.statusCode()).arg(resp.reasonPhrase()); - std::cerr << "RemoteFile::responseHeaderReceived: " - << m_errorString.toStdString() << std::endl; - } else { - std::cerr << "RemoteFile::responseHeaderReceived: " - << m_lastStatus << std::endl; - if (resp.hasContentType()) m_contentType = resp.contentType(); - } -} - -void -RemoteFile::ftpCommandFinished(int id, bool error) -{ - std::cerr << "RemoteFile::ftpCommandFinished(" << id << ", " << error << ")" << std::endl; - - if (!m_ftp) return; - - QFtp::Command command = m_ftp->currentCommand(); - - if (!error) { - std::cerr << "RemoteFile::ftpCommandFinished: success for command " - << command << std::endl; - return; - } - - if (command == QFtp::ConnectToHost) { - m_errorString = tr("Failed to connect to FTP server"); - } else if (command == QFtp::Login) { - m_errorString = tr("Login failed"); - } else if (command == QFtp::Cd) { - m_errorString = tr("Failed to change to correct directory"); - } else if (command == QFtp::Get) { - m_errorString = tr("FTP download aborted"); - } - - m_lastStatus = 400; // for done() -} - -void -RemoteFile::dataTransferProgress(qint64 done, qint64 total) -{ - if (!m_progressDialog) return; - - int percent = int((double(done) / double(total)) * 100.0 - 0.1); - emit progress(percent); - - if (percent > 0) { - m_progressDialog->setValue(percent); - m_progressDialog->show(); - } -} - -void -RemoteFile::cancelled() -{ - m_done = true; - cleanup(); - - m_ok = false; - m_errorString = tr("Download cancelled"); -} - -void -RemoteFile::done(bool error) -{ - std::cerr << "RemoteFile::done(" << error << ")" << std::endl; - - if (m_done) return; - - emit progress(100); - - if (error) { - if (m_http) { - m_errorString = m_http->errorString(); - } else if (m_ftp) { - m_errorString = m_ftp->errorString(); - } - } - - if (m_lastStatus / 100 >= 4) { - error = true; - } - - cleanup(); - - if (!error) { - QFileInfo fi(m_localFilename); - if (!fi.exists()) { - m_errorString = tr("Failed to create local file %1").arg(m_localFilename); - error = true; - } else if (fi.size() == 0) { - m_errorString = tr("File contains no data!"); - error = true; - } - } - - if (error) { - std::cerr << "RemoteFile::done: error is " << error << ", deleting cache file" << std::endl; - deleteCacheFile(); - } - - m_ok = !error; - m_done = true; - emit ready(); -} - -void -RemoteFile::deleteCacheFile() -{ - std::cerr << "RemoteFile::deleteCacheFile(\"" << m_localFilename.toStdString() << "\")" << std::endl; - - cleanup(); - - if (m_localFilename == "") { - return; - } - - if (!isRemote()) { - std::cerr << "not a cache file" << std::endl; - return; - } - - if (m_refCounted) { - - QMutexLocker locker(&m_mapMutex); - m_refCounted = false; - - if (m_refCountMap[m_url] > 0) { - m_refCountMap[m_url]--; - std::cerr << "reduced ref count to " << m_refCountMap[m_url] << std::endl; - if (m_refCountMap[m_url] > 0) { - m_done = true; - return; - } - } - } - - m_fileCreationMutex.lock(); - - if (!QFile(m_localFilename).remove()) { - std::cerr << "RemoteFile::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl; - } else { - std::cerr << "RemoteFile::deleteCacheFile: Deleted cache file \"" << m_localFilename.toStdString() << "\"" << std::endl; - m_localFilename = ""; - } - - m_fileCreationMutex.unlock(); - - m_done = true; -} - -void -RemoteFile::showProgressDialog() -{ - if (m_progressDialog) m_progressDialog->show(); -} - -bool -RemoteFile::createCacheFile() -{ - { - QMutexLocker locker(&m_mapMutex); - - std::cerr << "RemoteFile::createCacheFile: refcount is " << m_refCountMap[m_url] << std::endl; - - if (m_refCountMap[m_url] > 0) { - m_refCountMap[m_url]++; - m_localFilename = m_remoteLocalMap[m_url]; - std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl; - m_refCounted = true; - return true; - } - } - - QDir dir; - try { - dir = TempDirectory::getInstance()->getSubDirectoryPath("download"); - } catch (DirectoryCreationFailed f) { - std::cerr << "RemoteFile::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl; - return ""; - } - - QString filepart = m_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::createCacheFile: URL is \"" << m_url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl; - - QMutexLocker fcLocker(&m_fileCreationMutex); - - ++m_count; - - if (QFileInfo(filepath).exists() || - !QFile(filepath).open(QFile::WriteOnly)) { - - std::cerr << "RemoteFile::createCacheFile: Failed to create local file \"" - << filepath.toStdString() << "\" for URL \"" - << m_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::createCacheFile: ERROR: Failed to create local file \"" - << filepath.toStdString() << "\" for URL \"" - << m_url.toString().toStdString() << "\" (or file already exists)" << std::endl; - - return ""; - } - } - - std::cerr << "RemoteFile::createCacheFile: url " - << m_url.toString().toStdString() << " -> local filename " - << filepath.toStdString() << std::endl; - - m_localFilename = filepath; - - return false; -} diff -r 3a6725f285d6 -r c324d410b096 data/fileio/RemoteFile.h --- a/data/fileio/RemoteFile.h Thu Oct 18 15:31:20 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* -*- 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 -#include - -#include - -class QFtp; -class QHttp; -class QFile; -class QProgressDialog; -class QHttpResponseHeader; - -class RemoteFile : public QObject -{ - Q_OBJECT - -public: - RemoteFile(QString fileOrUrl, bool showProgress = true); - RemoteFile(QUrl url, bool showProgress = true); - RemoteFile(const RemoteFile &); - - virtual ~RemoteFile(); - - bool isAvailable(); - - void waitForStatus(); - void waitForData(); - - void setLeaveLocalFile(bool leave); - - bool isOK() const; - bool isDone() const; - bool isRemote() const; - - QString getLocation() const; - QString getLocalFilename() const; - QString getContentType() const; - QString getExtension() const; - - QString getErrorString() const; - - static bool isRemote(QString fileOrUrl); - static bool canHandleScheme(QUrl url); - -signals: - void progress(int percent); - void ready(); - -protected slots: - void dataReadProgress(int done, int total); - void httpResponseHeaderReceived(const QHttpResponseHeader &resp); - void ftpCommandFinished(int, bool); - void dataTransferProgress(qint64 done, qint64 total); - void done(bool error); - void showProgressDialog(); - void cancelled(); - -protected: - RemoteFile &operator=(const RemoteFile &); // not provided - - QUrl m_url; - QFtp *m_ftp; - QHttp *m_http; - QFile *m_localFile; - QString m_localFilename; - QString m_errorString; - QString m_contentType; - bool m_ok; - int m_lastStatus; - bool m_remote; - bool m_done; - bool m_leaveLocalFile; - QProgressDialog *m_progressDialog; - QTimer m_progressShowTimer; - - typedef std::map RemoteRefCountMap; - typedef std::map RemoteLocalMap; - static RemoteRefCountMap m_refCountMap; - static RemoteLocalMap m_remoteLocalMap; - static QMutex m_mapMutex; - bool m_refCounted; - - void init(bool showProgress); - void initHttp(); - void initFtp(); - - void cleanup(); - - // Create a local file for m_url. If it already existed, return true. - // The local filename is stored in m_localFilename. - bool createCacheFile(); - void deleteCacheFile(); - - static QMutex m_fileCreationMutex; - static int m_count; -}; - -#endif diff -r 3a6725f285d6 -r c324d410b096 data/fileio/ResamplingWavFileReader.cpp --- a/data/fileio/ResamplingWavFileReader.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/ResamplingWavFileReader.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -22,7 +22,7 @@ #include #include -ResamplingWavFileReader::ResamplingWavFileReader(RemoteFile source, +ResamplingWavFileReader::ResamplingWavFileReader(FileSource source, ResampleMode resampleMode, CacheMode mode, size_t targetRate) : @@ -181,7 +181,7 @@ } bool -ResamplingWavFileReader::supports(RemoteFile &source) +ResamplingWavFileReader::supports(FileSource &source) { return WavFileReader::supports(source); } diff -r 3a6725f285d6 -r c324d410b096 data/fileio/ResamplingWavFileReader.h --- a/data/fileio/ResamplingWavFileReader.h Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/ResamplingWavFileReader.h Thu Oct 18 16:20:26 2007 +0000 @@ -33,7 +33,7 @@ ResampleThreaded // resample in a background thread after construction }; - ResamplingWavFileReader(RemoteFile source, + ResamplingWavFileReader(FileSource source, ResampleMode resampleMode, CacheMode cacheMode, size_t targetRate = 0); @@ -43,7 +43,7 @@ static void getSupportedExtensions(std::set &extensions); static bool supportsExtension(QString ext); static bool supportsContentType(QString type); - static bool supports(RemoteFile &source); + static bool supports(FileSource &source); virtual int getDecodeCompletion() const { return m_completion; } @@ -52,7 +52,7 @@ } protected: - RemoteFile m_source; + FileSource m_source; QString m_path; QString m_error; bool m_cancelled; diff -r 3a6725f285d6 -r c324d410b096 data/fileio/WavFileReader.cpp --- a/data/fileio/WavFileReader.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/WavFileReader.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -20,7 +20,7 @@ #include #include -WavFileReader::WavFileReader(RemoteFile source, bool fileUpdating) : +WavFileReader::WavFileReader(FileSource source, bool fileUpdating) : m_file(0), m_source(source), m_path(source.getLocalFilename()), @@ -204,7 +204,7 @@ } bool -WavFileReader::supports(RemoteFile &source) +WavFileReader::supports(FileSource &source) { return (supportsExtension(source.getExtension()) || supportsContentType(source.getContentType())); diff -r 3a6725f285d6 -r c324d410b096 data/fileio/WavFileReader.h --- a/data/fileio/WavFileReader.h Thu Oct 18 15:31:20 2007 +0000 +++ b/data/fileio/WavFileReader.h Thu Oct 18 16:20:26 2007 +0000 @@ -26,7 +26,7 @@ class WavFileReader : public AudioFileReader { public: - WavFileReader(RemoteFile source, bool fileUpdating = false); + WavFileReader(FileSource source, bool fileUpdating = false); virtual ~WavFileReader(); virtual QString getError() const { return m_error; } @@ -41,7 +41,7 @@ static void getSupportedExtensions(std::set &extensions); static bool supportsExtension(QString ext); static bool supportsContentType(QString type); - static bool supports(RemoteFile &source); + static bool supports(FileSource &source); virtual int getDecodeCompletion() const { return 100; } @@ -54,7 +54,7 @@ SF_INFO m_fileInfo; SNDFILE *m_file; - RemoteFile m_source; + FileSource m_source; QString m_path; QString m_error; diff -r 3a6725f285d6 -r c324d410b096 data/model/WaveFileModel.cpp --- a/data/model/WaveFileModel.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/model/WaveFileModel.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -39,7 +39,7 @@ PowerOfSqrtTwoZoomConstraint WaveFileModel::m_zoomConstraint; -WaveFileModel::WaveFileModel(RemoteFile source, size_t targetRate) : +WaveFileModel::WaveFileModel(FileSource source, size_t targetRate) : m_source(source), m_path(source.getLocation()), m_myReader(true), @@ -62,7 +62,7 @@ if (isOK()) fillCache(); } -WaveFileModel::WaveFileModel(RemoteFile source, AudioFileReader *reader) : +WaveFileModel::WaveFileModel(FileSource source, AudioFileReader *reader) : m_source(source), m_path(source.getLocation()), m_myReader(false), diff -r 3a6725f285d6 -r c324d410b096 data/model/WaveFileModel.h --- a/data/model/WaveFileModel.h Thu Oct 18 15:31:20 2007 +0000 +++ b/data/model/WaveFileModel.h Thu Oct 18 16:20:26 2007 +0000 @@ -20,7 +20,7 @@ #include #include -#include "data/fileio/RemoteFile.h" +#include "data/fileio/FileSource.h" #include "RangeSummarisableTimeValueModel.h" #include "PowerOfSqrtTwoZoomConstraint.h" @@ -34,8 +34,8 @@ Q_OBJECT public: - WaveFileModel(RemoteFile source, size_t targetRate = 0); - WaveFileModel(RemoteFile source, AudioFileReader *reader); + WaveFileModel(FileSource source, size_t targetRate = 0); + WaveFileModel(FileSource source, AudioFileReader *reader); ~WaveFileModel(); bool isOK() const; @@ -104,7 +104,7 @@ void fillCache(); - RemoteFile m_source; + FileSource m_source; QString m_path; AudioFileReader *m_reader; bool m_myReader; diff -r 3a6725f285d6 -r c324d410b096 data/model/WritableWaveFileModel.cpp --- a/data/model/WritableWaveFileModel.cpp Thu Oct 18 15:31:20 2007 +0000 +++ b/data/model/WritableWaveFileModel.cpp Thu Oct 18 16:20:26 2007 +0000 @@ -60,7 +60,7 @@ return; } - RemoteFile source(m_writer->getPath()); + FileSource source(m_writer->getPath()); m_reader = new WavFileReader(source, true); if (!m_reader->getError().isEmpty()) {