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@465
|
16 #ifndef _FILE_SOURCE_H_
|
Chris@465
|
17 #define _FILE_SOURCE_H_
|
Chris@208
|
18
|
Chris@208
|
19 #include <QUrl>
|
Chris@208
|
20 #include <QMutex>
|
Chris@208
|
21 #include <QString>
|
Chris@210
|
22 #include <QTimer>
|
Chris@208
|
23
|
Chris@304
|
24 #include <map>
|
Chris@304
|
25
|
Chris@208
|
26 class QFtp;
|
Chris@208
|
27 class QHttp;
|
Chris@208
|
28 class QFile;
|
Chris@210
|
29 class QHttpResponseHeader;
|
Chris@392
|
30 class ProgressReporter;
|
Chris@208
|
31
|
Chris@325
|
32 /**
|
Chris@325
|
33 * FileSource is a class used to refer to the contents of a file that
|
Chris@325
|
34 * may be either local or at a remote location such as a HTTP URL.
|
Chris@325
|
35 *
|
Chris@325
|
36 * When a FileSource object is constructed, the file or URL passed to
|
Chris@325
|
37 * its constructor is tested for validity, and if it refers to a
|
Chris@325
|
38 * remote HTTP or FTP location it is retrieved asynchronously (the Qt
|
Chris@325
|
39 * event loop must be running) and cached locally. You can then query
|
Chris@325
|
40 * a local path for the file and refer to that as a normal filename.
|
Chris@325
|
41 *
|
Chris@325
|
42 * Use isAvailable() to test whether the file or remote URL exists,
|
Chris@325
|
43 * and isOK() to test for internal validity or transmission errors.
|
Chris@325
|
44 * Call waitForStatus() to block until the availability and validity
|
Chris@325
|
45 * of the URL have been established so that isAvailable may be called,
|
Chris@325
|
46 * and waitForData() to block until the entire file has been cached.
|
Chris@325
|
47 *
|
Chris@325
|
48 * FileSource handles reference counting for cache files. You can
|
Chris@325
|
49 * construct many FileSource objects with the same URL and you will
|
Chris@325
|
50 * receive the same cached file for each; it is also reasonable to
|
Chris@325
|
51 * pass FileSource objects by value. FileSource only makes sense for
|
Chris@325
|
52 * stateless URLs that result in the same data on each request.
|
Chris@325
|
53 *
|
Chris@460
|
54 * Cached files (in their default temporary mode) share a lifetime
|
Chris@460
|
55 * with their "owning" FileSource objects. When the last FileSource
|
Chris@460
|
56 * referring to a given URL is deleted or goes out of scope, its
|
Chris@460
|
57 * cached file (if any) is also removed. You can change this
|
Chris@460
|
58 * behaviour by using persistent cache mode; \see LocalCacheMode for
|
Chris@460
|
59 * details and caveats.
|
Chris@325
|
60 */
|
Chris@317
|
61 class FileSource : public QObject
|
Chris@208
|
62 {
|
Chris@208
|
63 Q_OBJECT
|
Chris@208
|
64
|
Chris@208
|
65 public:
|
Chris@325
|
66 /**
|
Chris@460
|
67 * Type of local cache to be used when retrieving remote files.
|
Chris@460
|
68 *
|
Chris@460
|
69 * Temporary cache files are created when a FileSource object is
|
Chris@460
|
70 * first created for a given URL, and removed when the last extant
|
Chris@460
|
71 * temporary cache mode FileSource object referring to a given URL
|
Chris@460
|
72 * is deleted (i.e. when its reference count is lowered to zero).
|
Chris@460
|
73 * They are also stored in a temporary directory that will be
|
Chris@460
|
74 * deleted when the program exits.
|
Chris@460
|
75 *
|
Chris@460
|
76 * Persistent cache files are created only when first retrieving a
|
Chris@460
|
77 * URL for which no persistent cache already exists, and are never
|
Chris@460
|
78 * deleted (by FileSource anyway). They are stored in a directory
|
Chris@460
|
79 * that is not deleted when the program exits. FileSource creates
|
Chris@460
|
80 * a unique local file name for each source URL, so as long as the
|
Chris@460
|
81 * local cache file remains on disc, the remote URL will not be
|
Chris@460
|
82 * retrieved again during any further run of the program. You can
|
Chris@460
|
83 * find out what local file name will be used for the persistent
|
Chris@460
|
84 * cache of a given URL by calling getPersistentCacheFilePath, if
|
Chris@460
|
85 * you want to do something such as delete it by hand.
|
Chris@460
|
86 *
|
Chris@460
|
87 * Note that FileSource does not cache local files (i.e. does not
|
Chris@460
|
88 * make a copy of files that already appear to be stored on the
|
Chris@460
|
89 * local filesystem) in either mode.
|
Chris@460
|
90 */
|
Chris@460
|
91 enum LocalCacheMode {
|
Chris@460
|
92 TemporaryCache,
|
Chris@460
|
93 PersistentCache
|
Chris@460
|
94 };
|
Chris@460
|
95
|
Chris@460
|
96 /**
|
Chris@325
|
97 * Construct a FileSource using the given local file path or URL.
|
Chris@357
|
98 * The URL may be raw or encoded.
|
Chris@357
|
99 *
|
Chris@392
|
100 * If a ProgressReporter is provided, it will be updated with
|
Chris@392
|
101 * progress status. Note that the progress() signal will also be
|
Chris@392
|
102 * emitted regularly during retrieval, even if no reporter is
|
Chris@392
|
103 * supplied here. Caller retains ownership of the reporter object.
|
Chris@460
|
104 *
|
Chris@460
|
105 * If LocalCacheMode is PersistentCache, a persistent cache file
|
Chris@460
|
106 * will be used. See LocalCacheMode documentation for details.
|
Chris@325
|
107 */
|
Chris@460
|
108 FileSource(QString fileOrUrl,
|
Chris@460
|
109 ProgressReporter *reporter = 0,
|
Chris@460
|
110 LocalCacheMode mode = TemporaryCache);
|
Chris@325
|
111
|
Chris@325
|
112 /**
|
Chris@357
|
113 * Construct a FileSource using the given remote URL.
|
Chris@357
|
114 *
|
Chris@392
|
115 * If a ProgressReporter is provided, it will be updated with
|
Chris@392
|
116 * progress status. Note that the progress() signal will also be
|
Chris@392
|
117 * emitted regularly during retrieval, even if no reporter is
|
Chris@392
|
118 * supplied here. Caller retains ownership of the reporter object.
|
Chris@460
|
119 *
|
Chris@460
|
120 * If LocalCacheMode is PersistentCache, a persistent cache file
|
Chris@460
|
121 * will be used. See LocalCacheMode documentation for details.
|
Chris@325
|
122 */
|
Chris@460
|
123 FileSource(QUrl url,
|
Chris@460
|
124 ProgressReporter *reporter = 0,
|
Chris@460
|
125 LocalCacheMode mode = TemporaryCache);
|
Chris@325
|
126
|
Chris@317
|
127 FileSource(const FileSource &);
|
Chris@316
|
128
|
Chris@317
|
129 virtual ~FileSource();
|
Chris@208
|
130
|
Chris@325
|
131 /**
|
Chris@325
|
132 * Block on a sub-event-loop until the availability of the file or
|
Chris@325
|
133 * remote URL is known.
|
Chris@325
|
134 */
|
Chris@325
|
135 void waitForStatus();
|
Chris@325
|
136
|
Chris@325
|
137 /**
|
Chris@325
|
138 * Block on a sub-event-loop until the whole of the data has been
|
Chris@325
|
139 * retrieved (if it is remote).
|
Chris@325
|
140 */
|
Chris@325
|
141 void waitForData();
|
Chris@325
|
142
|
Chris@325
|
143 /**
|
Chris@325
|
144 * Return true if the FileSource object is valid and no error
|
Chris@325
|
145 * occurred in looking up the file or remote URL. Non-existence
|
Chris@325
|
146 * of the file or URL is not an error -- call isAvailable() to
|
Chris@325
|
147 * test for that.
|
Chris@325
|
148 */
|
Chris@325
|
149 bool isOK() const;
|
Chris@325
|
150
|
Chris@325
|
151 /**
|
Chris@325
|
152 * Return true if the file or remote URL exists. This may block
|
Chris@325
|
153 * on a sub-event-loop (calling waitForStatus) if the status is
|
Chris@325
|
154 * not yet available.
|
Chris@325
|
155 */
|
Chris@210
|
156 bool isAvailable();
|
Chris@210
|
157
|
Chris@325
|
158 /**
|
Chris@325
|
159 * Return true if the entire file has been retrieved and is
|
Chris@325
|
160 * available.
|
Chris@325
|
161 */
|
Chris@325
|
162 bool isDone() const;
|
Chris@316
|
163
|
Chris@325
|
164 /**
|
Chris@325
|
165 * Return true if this FileSource is referring to a remote URL.
|
Chris@325
|
166 */
|
Chris@325
|
167 bool isRemote() const;
|
Chris@325
|
168
|
Chris@325
|
169 /**
|
Chris@325
|
170 * Return the location filename or URL as passed to the
|
Chris@325
|
171 * constructor.
|
Chris@325
|
172 */
|
Chris@325
|
173 QString getLocation() const;
|
Chris@325
|
174
|
Chris@325
|
175 /**
|
Chris@325
|
176 * Return the name of the local file this FileSource refers to.
|
Chris@325
|
177 * This is the filename that should be used when reading normally
|
Chris@325
|
178 * from the FileSource. If the filename passed to the constructor
|
Chris@325
|
179 * was a local file, this will return the same filename; otherwise
|
Chris@325
|
180 * this will be the name of the temporary cache file owned by the
|
Chris@325
|
181 * FileSource.
|
Chris@325
|
182 */
|
Chris@325
|
183 QString getLocalFilename() const;
|
Chris@325
|
184
|
Chris@325
|
185 /**
|
Chris@325
|
186 * Return the MIME content type of this file, if known.
|
Chris@325
|
187 */
|
Chris@325
|
188 QString getContentType() const;
|
Chris@325
|
189
|
Chris@325
|
190 /**
|
Chris@325
|
191 * Return the file extension for this file, if any.
|
Chris@325
|
192 */
|
Chris@325
|
193 QString getExtension() const;
|
Chris@325
|
194
|
Chris@325
|
195 /**
|
Chris@325
|
196 * Return an error message, if isOK() is false.
|
Chris@325
|
197 */
|
Chris@325
|
198 QString getErrorString() const;
|
Chris@325
|
199
|
Chris@325
|
200 /**
|
Chris@325
|
201 * Specify whether any local, cached file should remain on disc
|
Chris@325
|
202 * after this FileSource has been destroyed. The default is false
|
Chris@460
|
203 * (cached files share their FileSource owners' lifespans). This
|
Chris@460
|
204 * is only meaningful in TemporaryCache mode; even if this setting
|
Chris@460
|
205 * is true, the temporary cache will still be deleted when the
|
Chris@460
|
206 * program exits. Use PersistentCache mode if you want the cache
|
Chris@460
|
207 * to outlast the program.
|
Chris@325
|
208 */
|
Chris@316
|
209 void setLeaveLocalFile(bool leave);
|
Chris@208
|
210
|
Chris@325
|
211 /**
|
Chris@325
|
212 * Return true if the given filename or URL refers to a remote
|
Chris@325
|
213 * URL.
|
Chris@325
|
214 */
|
Chris@325
|
215 static bool isRemote(QString fileOrUrl);
|
Chris@210
|
216
|
Chris@325
|
217 /**
|
Chris@325
|
218 * Return true if FileSource can handle the retrieval scheme for
|
Chris@325
|
219 * the given URL (or if the URL is for a local file).
|
Chris@325
|
220 */
|
Chris@208
|
221 static bool canHandleScheme(QUrl url);
|
Chris@208
|
222
|
Chris@460
|
223 /**
|
Chris@460
|
224 * Return the path that will be used for the cache file copy of
|
Chris@460
|
225 * the given remote URL by a FileSource object constructed in
|
Chris@460
|
226 * PersistentCache mode.
|
Chris@460
|
227 *
|
Chris@460
|
228 * This method also creates the containing directory for such
|
Chris@460
|
229 * cache files, if it does not already exist, and so may throw
|
Chris@460
|
230 * DirectoryCreationFailed.
|
Chris@460
|
231 */
|
Chris@460
|
232 static QString getPersistentCacheFilePath(QUrl url);
|
Chris@460
|
233
|
Chris@208
|
234 signals:
|
Chris@325
|
235 /**
|
Chris@325
|
236 * Emitted during URL retrieval, when the retrieval progress
|
Chris@325
|
237 * notches up to a new percentage.
|
Chris@325
|
238 */
|
Chris@208
|
239 void progress(int percent);
|
Chris@325
|
240
|
Chris@325
|
241 /**
|
Chris@325
|
242 * Emitted when the file's existence has been tested and/or
|
Chris@325
|
243 * response header received. Calls to isAvailable() after this
|
Chris@325
|
244 * has been emitted will not block.
|
Chris@325
|
245 */
|
Chris@325
|
246 void statusAvailable();
|
Chris@325
|
247
|
Chris@325
|
248 /**
|
Chris@325
|
249 * Emitted when the entire file data has been retrieved and the
|
Chris@325
|
250 * local file is complete (if no error has occurred).
|
Chris@325
|
251 */
|
Chris@208
|
252 void ready();
|
Chris@208
|
253
|
Chris@208
|
254 protected slots:
|
Chris@208
|
255 void dataReadProgress(int done, int total);
|
Chris@214
|
256 void httpResponseHeaderReceived(const QHttpResponseHeader &resp);
|
Chris@214
|
257 void ftpCommandFinished(int, bool);
|
Chris@208
|
258 void dataTransferProgress(qint64 done, qint64 total);
|
Chris@208
|
259 void done(bool error);
|
Chris@210
|
260 void cancelled();
|
Chris@208
|
261
|
Chris@208
|
262 protected:
|
Chris@317
|
263 FileSource &operator=(const FileSource &); // not provided
|
Chris@316
|
264
|
Chris@304
|
265 QUrl m_url;
|
Chris@460
|
266 LocalCacheMode m_cacheMode;
|
Chris@208
|
267 QFtp *m_ftp;
|
Chris@208
|
268 QHttp *m_http;
|
Chris@208
|
269 QFile *m_localFile;
|
Chris@208
|
270 QString m_localFilename;
|
Chris@208
|
271 QString m_errorString;
|
Chris@315
|
272 QString m_contentType;
|
Chris@208
|
273 bool m_ok;
|
Chris@210
|
274 int m_lastStatus;
|
Chris@316
|
275 bool m_remote;
|
Chris@208
|
276 bool m_done;
|
Chris@316
|
277 bool m_leaveLocalFile;
|
Chris@392
|
278 ProgressReporter *m_reporter;
|
Chris@208
|
279
|
Chris@304
|
280 typedef std::map<QUrl, int> RemoteRefCountMap;
|
Chris@304
|
281 typedef std::map<QUrl, QString> RemoteLocalMap;
|
Chris@304
|
282 static RemoteRefCountMap m_refCountMap;
|
Chris@304
|
283 static RemoteLocalMap m_remoteLocalMap;
|
Chris@304
|
284 static QMutex m_mapMutex;
|
Chris@316
|
285 bool m_refCounted;
|
Chris@316
|
286
|
Chris@357
|
287 void init();
|
Chris@316
|
288 void initHttp();
|
Chris@316
|
289 void initFtp();
|
Chris@304
|
290
|
Chris@211
|
291 void cleanup();
|
Chris@211
|
292
|
Chris@316
|
293 // Create a local file for m_url. If it already existed, return true.
|
Chris@316
|
294 // The local filename is stored in m_localFilename.
|
Chris@316
|
295 bool createCacheFile();
|
Chris@316
|
296 void deleteCacheFile();
|
Chris@208
|
297
|
Chris@460
|
298 static QString getPersistentCacheDirectory();
|
Chris@460
|
299
|
Chris@208
|
300 static QMutex m_fileCreationMutex;
|
Chris@208
|
301 static int m_count;
|
Chris@208
|
302 };
|
Chris@208
|
303
|
Chris@208
|
304 #endif
|