]> git.sur5r.net Git - minitube/blob - src/downloaditem.cpp
36652f6c338a31278c932636953013e27cb4c815
[minitube] / src / downloaditem.cpp
1 #include "downloaditem.h"
2 #include "networkaccess.h"
3 #include "video.h"
4
5 #include <QDesktopServices>
6
7 namespace The {
8     NetworkAccess* http();
9 }
10
11 DownloadItem::DownloadItem(Video *video, QUrl url, QString filename, QObject *parent)
12     : QObject(parent)
13     , m_bytesReceived(0)
14     , m_startedSaving(false)
15     , m_finishedDownloading(false)
16     , m_url(url)
17     , m_file(filename)
18     , m_reply(0)
19     , video(video)
20     , m_status(Idle)
21 {
22     speedCheckTimer = new QTimer(this);
23     speedCheckTimer->setInterval(2000);
24     speedCheckTimer->setSingleShot(true);
25     connect(speedCheckTimer, SIGNAL(timeout()), SLOT(speedCheck()));
26 }
27
28 DownloadItem::~DownloadItem() {
29     if (m_reply) delete m_reply;
30     if (video) delete video;
31 }
32
33 void DownloadItem::start() {
34     m_reply = The::http()->simpleGet(m_url);
35     init();
36 }
37
38 void DownloadItem::init() {
39     if (!m_reply)
40         return;
41
42     if (m_file.exists())
43         m_file.remove();
44
45     m_status = Starting;
46
47     m_startedSaving = false;
48     m_finishedDownloading = false;
49
50     // attach to the m_reply
51     m_url = m_reply->url();
52     connect(m_reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
53     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
54             this, SLOT(error(QNetworkReply::NetworkError)));
55     connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)),
56             this, SLOT(downloadProgress(qint64, qint64)));
57     connect(m_reply, SIGNAL(metaDataChanged()),
58             this, SLOT(metaDataChanged()));
59     connect(m_reply, SIGNAL(finished()),
60             this, SLOT(requestFinished()));
61
62     // start timer for the download estimation
63     m_downloadTime.start();
64     speedCheckTimer->start();
65
66     if (m_reply->error() != QNetworkReply::NoError) {
67         error(m_reply->error());
68         requestFinished();
69     }
70 }
71
72
73 void DownloadItem::stop() {
74     if (m_reply)
75         m_reply->abort();
76     m_status = Idle;
77     emit statusChanged();
78 }
79
80 void DownloadItem::open() {
81     QFileInfo info(m_file);
82     QUrl url = QUrl::fromLocalFile(info.absoluteFilePath());
83     QDesktopServices::openUrl(url);
84 }
85
86 void DownloadItem::openFolder() {
87     QFileInfo info(m_file);
88     QUrl url = QUrl::fromLocalFile(info.absolutePath());
89     QDesktopServices::openUrl(url);
90 }
91
92 void DownloadItem::tryAgain() {
93     if (m_reply)
94         m_reply->abort();
95
96     if (m_file.exists())
97         m_file.remove();
98
99     m_reply = The::http()->simpleGet(m_url);
100     init();
101     emit statusChanged();
102 }
103
104 void DownloadItem::downloadReadyRead() {
105
106     if (!m_file.isOpen()) {
107         if (!m_file.open(QIODevice::ReadWrite)) {
108             qDebug() << QString("Error opening output file: %1").arg(m_file.errorString());
109             stop();
110             emit statusChanged();
111             return;
112         }
113         emit statusChanged();
114     }
115
116     if (-1 == m_file.write(m_reply->readAll())) {
117         /*
118         downloadInfoLabel->setText(tr("Error saving: %1")
119                                    .arg(m_output.errorString()));
120         stopButton->click();
121         */
122     } else {
123         m_startedSaving = true;
124         if (m_status != Downloading) {
125             // m_status = Downloading;
126             // emit statusChanged();
127         } else if (m_finishedDownloading)
128             requestFinished();
129     }
130 }
131
132 void DownloadItem::error(QNetworkReply::NetworkError) {
133
134 #ifdef DOWNLOADMANAGER_DEBUG
135     qDebug() << "DownloadItem::" << __FUNCTION__ << m_reply->errorString() << m_url;
136 #endif
137
138     if (m_reply) {
139         qDebug() << m_reply->errorString();
140         m_errorMessage = m_reply->errorString();
141     }
142
143     m_reply = 0;
144     m_status = Failed;
145
146     emit finished();
147 }
148
149 QString DownloadItem::errorMessage() const {
150     return m_errorMessage;
151 }
152
153 void DownloadItem::metaDataChanged() {
154     QVariant locationHeader = m_reply->header(QNetworkRequest::LocationHeader);
155     if (locationHeader.isValid()) {
156         m_url = locationHeader.toUrl();
157         // qDebug() << "Redirecting to" << m_url;
158         m_reply->deleteLater();
159         m_reply = The::http()->simpleGet(m_url);
160         init();
161         return;
162     }
163
164 #ifdef DOWNLOADMANAGER_DEBUG
165     qDebug() << "DownloadItem::" << __FUNCTION__ << "not handled.";
166 #endif
167 }
168
169 int DownloadItem::initialBufferSize() {
170     // qDebug() << video->getDefinitionCode();
171     switch (video->getDefinitionCode()) {
172     case 18:
173         return 1024*192;
174     case 22:
175         return 1024*512;
176     case 37:
177         return 1024*768;
178     }
179     return 1024*128;
180 }
181
182 void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
183
184     if (m_lastProgressTime.elapsed() < 150) return;
185     m_lastProgressTime.start();
186
187     m_bytesReceived = bytesReceived;
188
189     if (m_status != Downloading) {
190
191         int neededBytes = (int) (bytesTotal * .001);
192         // qDebug() << bytesReceived << bytesTotal << neededBytes << m_downloadTime.elapsed();
193         int bufferSize = initialBufferSize();
194         if (bytesReceived > bufferSize
195             && bytesReceived > neededBytes
196             && m_downloadTime.elapsed() > 1000 ) {
197             emit bufferProgress(100);
198             m_status = Downloading;
199             emit statusChanged();
200         } else {
201             int bufferPercent = bytesReceived * 100 / qMax(bufferSize, neededBytes);
202             emit bufferProgress(bufferPercent);
203         }
204
205     } else {
206
207         if (bytesTotal > 0) {
208             int percent = bytesReceived * 100 / bytesTotal;
209             if (percent != this->percent) {
210                 this->percent = percent;
211                 emit progress(percent);
212             }
213         }
214
215     }
216 }
217
218 void DownloadItem::speedCheck() {
219     if (!m_reply) return;
220     if (m_bytesReceived < initialBufferSize() / 3) {
221         m_reply->disconnect();
222         m_reply->abort();
223         m_reply->deleteLater();
224         m_reply = 0;
225
226         // too slow! retry
227         qDebug() << "Retrying...";
228         connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)));
229         video->loadStreamUrl();
230     }
231 }
232
233 void DownloadItem::gotStreamUrl(QUrl streamUrl) {
234
235     Video *video = static_cast<Video *>(sender());
236     if (!video) {
237         qDebug() << "Cannot get sender";
238         return;
239     }
240     video->disconnect(this);
241
242     m_reply = The::http()->simpleGet(video->getStreamUrl());
243     init();
244 }
245
246 qint64 DownloadItem::bytesTotal() const {
247     if (!m_reply) return 0;
248     return m_reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
249 }
250
251 qint64 DownloadItem::bytesReceived() const {
252     return m_bytesReceived;
253 }
254
255 double DownloadItem::remainingTime() const {
256     if (m_finishedDownloading)
257         return -1.0;
258
259     double timeRemaining = ((double)(bytesTotal() - bytesReceived())) / currentSpeed();
260
261     // When downloading the eta should never be 0
262     if (timeRemaining == 0)
263         timeRemaining = 1;
264
265     return timeRemaining;
266 }
267
268 double DownloadItem::currentSpeed() const {
269     if (m_finishedDownloading)
270         return -1.0;
271
272     return m_bytesReceived * 1000.0 / m_downloadTime.elapsed();
273 }
274
275 void DownloadItem::requestFinished() {
276     m_reply = 0;
277     m_finishedDownloading = true;
278     if (!m_startedSaving) {
279         qDebug() << "Request finished but never started saving";
280         return;
281     }
282     m_file.close();
283     m_status = Finished;
284     emit statusChanged();
285     emit finished();
286 }
287
288 QString DownloadItem::formattedFilesize(qint64 size) {
289     /*
290     if (size < 1024) return tr("%1 bytes").arg(size);
291     else if (size < 1024*1024) return tr("%1 KB").arg(size/1024);
292     else if (size < 1024*1024*1024) return tr("%1 MB").arg(size/1024/1024);
293     else return tr("%1 GB").arg(size/1024/1024/1024);
294     */
295     QString unit;
296     if (size < 1024) {
297         unit = tr("bytes");
298     } else if (size < 1024*1024) {
299         size /= 1024;
300         unit = tr("KB");
301     } else {
302         size /= 1024*1024;
303         unit = tr("MB");
304     }
305     return QString(QLatin1String("%1 %2")).arg(size).arg(unit);
306 }
307
308 QString DownloadItem::formattedSpeed(double speed) {
309     /*
310     static const int K = 1024;
311     if (speed < K) return tr("%1 bytes/s").arg(speed);
312     else if (speed < K*K) return tr("%1 KB/s").arg(speed/K);
313     else if (speed < K*K*K) return tr("%1 MB/s").arg(speed/K/K);
314     else return tr("%1 GB/s").arg(speed/K/K/K);
315     */
316     int speedInt = (int) speed;
317     QString unit;
318     if (speedInt < 1024) {
319         unit = tr("bytes/sec");
320     } else if (speedInt < 1024*1024) {
321         speedInt /= 1024;
322         unit = tr("KB/sec");
323     } else {
324         speedInt /= 1024*1024;
325         unit = tr("MB/sec");
326     }
327     return QString(QLatin1String("%1 %2")).arg(speedInt).arg(unit);
328 }
329
330 QString DownloadItem::formattedTime(double timeRemaining) {
331     QString timeRemainingString = tr("seconds");
332     if (timeRemaining > 60) {
333         timeRemaining = timeRemaining / 60;
334         timeRemainingString = tr("minutes");
335     }
336     timeRemaining = floor(timeRemaining);
337     return tr("%4 %5 remaining")
338             .arg(timeRemaining)
339             .arg(timeRemainingString);
340 }