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@1581
|
16 #ifndef SV_FILE_SOURCE_H
|
Chris@1581
|
17 #define SV_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@762
|
23 #include <QNetworkReply>
|
Chris@208
|
24
|
Chris@304
|
25 #include <map>
|
Chris@304
|
26
|
Chris@686
|
27 #include "base/Debug.h"
|
Chris@686
|
28
|
Chris@208
|
29 class QFile;
|
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@469
|
54 * Cached files share a lifetime with their "owning" FileSource
|
Chris@469
|
55 * objects; when the last FileSource referring to a given URL is
|
Chris@469
|
56 * deleted or goes out of scope, its cached file (if any) is also
|
Chris@469
|
57 * removed.
|
Chris@325
|
58 */
|
Chris@317
|
59 class FileSource : public QObject
|
Chris@208
|
60 {
|
Chris@208
|
61 Q_OBJECT
|
Chris@208
|
62
|
Chris@208
|
63 public:
|
Chris@325
|
64 /**
|
Chris@325
|
65 * Construct a FileSource using the given local file path or URL.
|
Chris@357
|
66 * The URL may be raw or encoded.
|
Chris@357
|
67 *
|
Chris@392
|
68 * If a ProgressReporter is provided, it will be updated with
|
Chris@392
|
69 * progress status. Note that the progress() signal will also be
|
Chris@392
|
70 * emitted regularly during retrieval, even if no reporter is
|
Chris@392
|
71 * supplied here. Caller retains ownership of the reporter object.
|
Chris@325
|
72 */
|
Chris@520
|
73 FileSource(QString fileOrUrl,
|
Chris@520
|
74 ProgressReporter *reporter = 0,
|
Chris@520
|
75 QString preferredContentType = "");
|
Chris@325
|
76
|
Chris@325
|
77 /**
|
Chris@357
|
78 * Construct a FileSource using the given remote URL.
|
Chris@357
|
79 *
|
Chris@392
|
80 * If a ProgressReporter is provided, it will be updated with
|
Chris@392
|
81 * progress status. Note that the progress() signal will also be
|
Chris@392
|
82 * emitted regularly during retrieval, even if no reporter is
|
Chris@392
|
83 * supplied here. Caller retains ownership of the reporter object.
|
Chris@325
|
84 */
|
Chris@469
|
85 FileSource(QUrl url, ProgressReporter *reporter = 0);
|
Chris@325
|
86
|
Chris@317
|
87 FileSource(const FileSource &);
|
Chris@316
|
88
|
Chris@317
|
89 virtual ~FileSource();
|
Chris@208
|
90
|
Chris@325
|
91 /**
|
Chris@325
|
92 * Block on a sub-event-loop until the availability of the file or
|
Chris@325
|
93 * remote URL is known.
|
Chris@325
|
94 */
|
Chris@325
|
95 void waitForStatus();
|
Chris@325
|
96
|
Chris@325
|
97 /**
|
Chris@325
|
98 * Block on a sub-event-loop until the whole of the data has been
|
Chris@325
|
99 * retrieved (if it is remote).
|
Chris@325
|
100 */
|
Chris@325
|
101 void waitForData();
|
Chris@325
|
102
|
Chris@325
|
103 /**
|
Chris@981
|
104 * Return true if the FileSource object is valid and neither error
|
Chris@981
|
105 * nor cancellation occurred while retrieving the file or remote
|
Chris@981
|
106 * URL. Non-existence of the file or URL is not an error -- call
|
Chris@981
|
107 * isAvailable() to test for that.
|
Chris@325
|
108 */
|
Chris@325
|
109 bool isOK() const;
|
Chris@325
|
110
|
Chris@325
|
111 /**
|
Chris@325
|
112 * Return true if the file or remote URL exists. This may block
|
Chris@325
|
113 * on a sub-event-loop (calling waitForStatus) if the status is
|
Chris@325
|
114 * not yet available.
|
Chris@325
|
115 */
|
Chris@210
|
116 bool isAvailable();
|
Chris@210
|
117
|
Chris@325
|
118 /**
|
Chris@325
|
119 * Return true if the entire file has been retrieved and is
|
Chris@325
|
120 * available.
|
Chris@325
|
121 */
|
Chris@325
|
122 bool isDone() const;
|
Chris@316
|
123
|
Chris@325
|
124 /**
|
Chris@981
|
125 * Return true if the operation was cancelled by the user through
|
Chris@981
|
126 * the ProgressReporter interface. Note that the cancelled()
|
Chris@981
|
127 * signal will have been emitted, and isOK() will also return
|
Chris@981
|
128 * false in this case.
|
Chris@981
|
129 */
|
Chris@981
|
130 bool wasCancelled() const;
|
Chris@981
|
131
|
Chris@981
|
132 /**
|
Chris@706
|
133 * Return true if this FileSource is referring to a QRC resource.
|
Chris@706
|
134 */
|
Chris@706
|
135 bool isResource() const;
|
Chris@706
|
136
|
Chris@706
|
137 /**
|
Chris@325
|
138 * Return true if this FileSource is referring to a remote URL.
|
Chris@325
|
139 */
|
Chris@325
|
140 bool isRemote() const;
|
Chris@325
|
141
|
Chris@325
|
142 /**
|
Chris@325
|
143 * Return the location filename or URL as passed to the
|
Chris@325
|
144 * constructor.
|
Chris@325
|
145 */
|
Chris@325
|
146 QString getLocation() const;
|
Chris@325
|
147
|
Chris@325
|
148 /**
|
Chris@325
|
149 * Return the name of the local file this FileSource refers to.
|
Chris@325
|
150 * This is the filename that should be used when reading normally
|
Chris@325
|
151 * from the FileSource. If the filename passed to the constructor
|
Chris@325
|
152 * was a local file, this will return the same filename; otherwise
|
Chris@325
|
153 * this will be the name of the temporary cache file owned by the
|
Chris@325
|
154 * FileSource.
|
Chris@325
|
155 */
|
Chris@325
|
156 QString getLocalFilename() const;
|
Chris@325
|
157
|
Chris@325
|
158 /**
|
Chris@678
|
159 * Return the base name, i.e. the final path element (including
|
Chris@678
|
160 * extension, if any) of the location.
|
Chris@678
|
161 */
|
Chris@678
|
162 QString getBasename() const;
|
Chris@678
|
163
|
Chris@678
|
164 /**
|
Chris@325
|
165 * Return the MIME content type of this file, if known.
|
Chris@325
|
166 */
|
Chris@325
|
167 QString getContentType() const;
|
Chris@325
|
168
|
Chris@325
|
169 /**
|
Chris@1098
|
170 * Return the file extension for this file, if any. The returned
|
Chris@1098
|
171 * extension is always lower-case.
|
Chris@325
|
172 */
|
Chris@325
|
173 QString getExtension() const;
|
Chris@325
|
174
|
Chris@325
|
175 /**
|
Chris@325
|
176 * Return an error message, if isOK() is false.
|
Chris@325
|
177 */
|
Chris@325
|
178 QString getErrorString() const;
|
Chris@325
|
179
|
Chris@325
|
180 /**
|
Chris@325
|
181 * Specify whether any local, cached file should remain on disc
|
Chris@325
|
182 * after this FileSource has been destroyed. The default is false
|
Chris@469
|
183 * (cached files share their FileSource owners' lifespans).
|
Chris@325
|
184 */
|
Chris@316
|
185 void setLeaveLocalFile(bool leave);
|
Chris@208
|
186
|
Chris@325
|
187 /**
|
Chris@325
|
188 * Return true if the given filename or URL refers to a remote
|
Chris@325
|
189 * URL.
|
Chris@325
|
190 */
|
Chris@325
|
191 static bool isRemote(QString fileOrUrl);
|
Chris@210
|
192
|
Chris@325
|
193 /**
|
Chris@325
|
194 * Return true if FileSource can handle the retrieval scheme for
|
Chris@325
|
195 * the given URL (or if the URL is for a local file).
|
Chris@325
|
196 */
|
Chris@208
|
197 static bool canHandleScheme(QUrl url);
|
Chris@208
|
198
|
Chris@1033
|
199 /**
|
Chris@1033
|
200 * Print some stats, if FileSource was compiled with debugging.
|
Chris@1033
|
201 * It's safe to leave a call to this function in release code, as
|
Chris@1033
|
202 * long as FileSource itself is compiled with release flags.
|
Chris@1033
|
203 */
|
Chris@1033
|
204 static void debugReport();
|
Chris@1033
|
205
|
Chris@208
|
206 signals:
|
Chris@325
|
207 /**
|
Chris@325
|
208 * Emitted during URL retrieval, when the retrieval progress
|
Chris@325
|
209 * notches up to a new percentage.
|
Chris@325
|
210 */
|
Chris@208
|
211 void progress(int percent);
|
Chris@325
|
212
|
Chris@325
|
213 /**
|
Chris@325
|
214 * Emitted when the file's existence has been tested and/or
|
Chris@325
|
215 * response header received. Calls to isAvailable() after this
|
Chris@325
|
216 * has been emitted will not block.
|
Chris@325
|
217 */
|
Chris@325
|
218 void statusAvailable();
|
Chris@325
|
219
|
Chris@325
|
220 /**
|
Chris@325
|
221 * Emitted when the entire file data has been retrieved and the
|
Chris@325
|
222 * local file is complete (if no error has occurred).
|
Chris@325
|
223 */
|
Chris@208
|
224 void ready();
|
Chris@208
|
225
|
Chris@208
|
226 protected slots:
|
Chris@764
|
227 void metaDataChanged();
|
Chris@762
|
228 void readyRead();
|
Chris@762
|
229 void replyFailed(QNetworkReply::NetworkError);
|
Chris@762
|
230 void replyFinished();
|
Chris@762
|
231 void downloadProgress(qint64 done, qint64 total);
|
Chris@210
|
232 void cancelled();
|
Chris@208
|
233
|
Chris@208
|
234 protected:
|
Chris@317
|
235 FileSource &operator=(const FileSource &); // not provided
|
Chris@316
|
236
|
chris@831
|
237 QString m_rawFileOrUrl;
|
Chris@304
|
238 QUrl m_url;
|
Chris@208
|
239 QFile *m_localFile;
|
Chris@762
|
240 QNetworkReply *m_reply;
|
Chris@208
|
241 QString m_localFilename;
|
Chris@208
|
242 QString m_errorString;
|
Chris@315
|
243 QString m_contentType;
|
Chris@520
|
244 QString m_preferredContentType;
|
Chris@208
|
245 bool m_ok;
|
Chris@981
|
246 bool m_cancelled;
|
Chris@210
|
247 int m_lastStatus;
|
Chris@706
|
248 bool m_resource;
|
Chris@316
|
249 bool m_remote;
|
Chris@208
|
250 bool m_done;
|
Chris@316
|
251 bool m_leaveLocalFile;
|
Chris@392
|
252 ProgressReporter *m_reporter;
|
Chris@208
|
253
|
Chris@304
|
254 typedef std::map<QUrl, int> RemoteRefCountMap;
|
Chris@304
|
255 typedef std::map<QUrl, QString> RemoteLocalMap;
|
Chris@304
|
256 static RemoteRefCountMap m_refCountMap;
|
Chris@304
|
257 static RemoteLocalMap m_remoteLocalMap;
|
Chris@304
|
258 static QMutex m_mapMutex;
|
Chris@316
|
259 bool m_refCounted;
|
Chris@316
|
260
|
Chris@357
|
261 void init();
|
Chris@762
|
262 void initRemote();
|
Chris@304
|
263
|
Chris@211
|
264 void cleanup();
|
Chris@211
|
265
|
Chris@316
|
266 // Create a local file for m_url. If it already existed, return true.
|
Chris@316
|
267 // The local filename is stored in m_localFilename.
|
Chris@316
|
268 bool createCacheFile();
|
Chris@316
|
269 void deleteCacheFile();
|
Chris@208
|
270
|
Chris@208
|
271 static QMutex m_fileCreationMutex;
|
Chris@208
|
272 static int m_count;
|
Chris@208
|
273 };
|
Chris@208
|
274
|
Chris@208
|
275 #endif
|