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