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