]> git.sur5r.net Git - minitube/blob - src/downloaditem.cpp
05995370968b5fa1b07aed5298e0c6a2971f5342
[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->disconnect();
87         m_reply->abort();
88         m_reply->deleteLater();
89         m_reply = 0;
90     }
91     m_status = Idle;
92     emit statusChanged();
93 }
94
95 void DownloadItem::open() {
96     QFileInfo info(m_file);
97     QUrl url = QUrl::fromLocalFile(info.absoluteFilePath());
98     QDesktopServices::openUrl(url);
99 }
100
101 void DownloadItem::openFolder() {
102     QFileInfo info(m_file);
103 #ifdef APP_MAC
104     mac::showInFinder(info.absoluteFilePath());
105 #else
106     QUrl url = QUrl::fromLocalFile(info.absolutePath());
107     QDesktopServices::openUrl(url);
108 #endif
109 }
110
111 void DownloadItem::tryAgain() {
112     stop();
113     start();
114 }
115
116 void DownloadItem::downloadReadyRead() {
117     if (!m_reply) return;
118
119     if (!m_file.isOpen()) {
120         if (!m_file.open(QIODevice::ReadWrite)) {
121             qWarning() << QString("Error opening output file: %1").arg(m_file.errorString());
122             stop();
123             emit statusChanged();
124             return;
125         }
126         emit statusChanged();
127     }
128
129     if (-1 == m_file.write(m_reply->readAll())) {
130         qWarning() << "Error saving." << m_file.errorString();
131     } else {
132
133         m_startedSaving = true;
134
135         // if (m_finishedDownloading) requestFinished();
136     }
137 }
138
139 void DownloadItem::error(QNetworkReply::NetworkError) {
140
141     if (m_reply) {
142         qWarning() << m_reply->errorString() << m_reply->url().toEncoded();
143         m_errorMessage = m_reply->errorString();
144     }
145
146     m_reply = 0;
147     m_status = Failed;
148
149     emit finished();
150 }
151
152 QString DownloadItem::errorMessage() const {
153     return m_errorMessage;
154 }
155
156 void DownloadItem::metaDataChanged() {
157     if (!m_reply) return;
158     QVariant locationHeader = m_reply->header(QNetworkRequest::LocationHeader);
159     if (locationHeader.isValid()) {
160         m_url = locationHeader.toUrl();
161         qDebug() << "Redirecting to" << m_url;
162         tryAgain();
163         return;
164     }
165
166 #ifdef DOWNLOADMANAGER_DEBUG
167     qDebug() << "DownloadItem::" << __FUNCTION__ << "not handled.";
168 #endif
169 }
170
171 int DownloadItem::initialBufferSize() {
172     // qDebug() << video->getDefinitionCode();
173     switch (video->getDefinitionCode()) {
174     case 18:
175         return 1024*512;
176     case 22:
177         return 1024*1024;
178     case 37:
179         return 1024*1024*2;
180     }
181     return 1024*128;
182 }
183
184 void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
185
186     // qDebug() << bytesReceived << bytesTotal << m_downloadTime.elapsed();
187
188     if (m_lastProgressTime.elapsed() < 150) return;
189     m_lastProgressTime.start();
190
191     m_bytesReceived = bytesReceived;
192
193     if (m_status != Downloading) {
194
195         int neededBytes = (int) (bytesTotal * .005);
196         int bufferSize = initialBufferSize();
197         if (bufferSize > bytesTotal) bufferSize = bytesTotal;
198         // qDebug() << bytesReceived << bytesTotal << neededBytes << bufferSize << m_downloadTime.elapsed();
199         if (bytesReceived > bufferSize
200             && bytesReceived > neededBytes
201             && m_downloadTime.elapsed() > 2000) {
202             emit bufferProgress(100);
203             m_status = Downloading;
204             emit statusChanged();
205         } else {
206             int bufferPercent = bytesReceived * 100 / qMax(bufferSize, neededBytes);
207             emit bufferProgress(bufferPercent);
208         }
209
210     } else {
211
212         if (bytesTotal > 0) {
213             int percent = bytesReceived * 100 / bytesTotal;
214             if (percent != this->percent) {
215                 this->percent = percent;
216                 emit progress(percent);
217             }
218         }
219
220     }
221 }
222
223 void DownloadItem::speedCheck() {
224     if (!m_reply) return;
225     int bytesTotal = m_reply->size();
226     int bufferSize = initialBufferSize();
227     if (bufferSize > bytesTotal) bufferSize = 0;
228     if (m_bytesReceived < bufferSize / 3) {
229         stop();
230
231         // too slow! retry
232         qDebug() << "Retrying...";
233         connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)), Qt::UniqueConnection);
234         video->loadStreamUrl();
235     }
236 }
237
238 void DownloadItem::gotStreamUrl(QUrl /*streamUrl*/) {
239
240     Video *video = static_cast<Video *>(sender());
241     if (!video) {
242         qDebug() << "Cannot get sender";
243         return;
244     }
245     video->disconnect(this);
246
247     m_url = video->getStreamUrl();
248     start();
249 }
250
251 qint64 DownloadItem::bytesTotal() const {
252     if (!m_reply) return 0;
253     return m_reply->header(QNetworkRequest::ContentLengthHeader).toULongLong();
254 }
255
256 qint64 DownloadItem::bytesReceived() const {
257     return m_bytesReceived;
258 }
259
260 double DownloadItem::remainingTime() const {
261     if (m_finishedDownloading)
262         return -1.0;
263
264     double timeRemaining = ((double)(bytesTotal() - bytesReceived())) / currentSpeed();
265
266     // When downloading the eta should never be 0
267     if (timeRemaining == 0)
268         timeRemaining = 1;
269
270     return timeRemaining;
271 }
272
273 double DownloadItem::currentSpeed() const {
274     if (m_finishedDownloading)
275         return -1.0;
276
277     return m_bytesReceived * 1000.0 / m_downloadTime.elapsed();
278 }
279
280 void DownloadItem::requestFinished() {
281     if (!m_startedSaving) {
282         qDebug() << "Request finished but never started saving";
283         tryAgain();
284         return;
285     }
286
287     if (m_bytesReceived <= 0) {
288         qDebug() << "Request finished but saved 0 bytes";
289         tryAgain();
290         return;
291     }
292
293     m_finishedDownloading = true;
294
295     if (m_status == Starting) {
296         m_status = Downloading;
297         emit statusChanged();
298     }
299     m_file.close();
300     m_status = Finished;
301     emit statusChanged();
302     emit finished();
303     m_reply->deleteLater();
304     m_reply = 0;
305 }
306
307 QString DownloadItem::formattedFilesize(qint64 size) {
308     QString unit;
309     if (size < 1024) {
310         unit = tr("bytes");
311     } else if (size < 1024*1024) {
312         size /= 1024;
313         unit = tr("KB");
314     } else {
315         size /= 1024*1024;
316         unit = tr("MB");
317     }
318     return QString(QLatin1String("%1 %2")).arg(size).arg(unit);
319 }
320
321 QString DownloadItem::formattedSpeed(double speed) {
322     int speedInt = (int) speed;
323     QString unit;
324     if (speedInt < 1024) {
325         unit = tr("bytes/sec");
326     } else if (speedInt < 1024*1024) {
327         speedInt /= 1024;
328         unit = tr("KB/sec");
329     } else {
330         speedInt /= 1024*1024;
331         unit = tr("MB/sec");
332     }
333     return QString(QLatin1String("%1 %2")).arg(speedInt).arg(unit);
334 }
335
336 QString DownloadItem::formattedTime(double timeRemaining) {
337     QString timeRemainingString = tr("seconds");
338     if (timeRemaining > 60) {
339         timeRemaining = timeRemaining / 60;
340         timeRemainingString = tr("minutes");
341     }
342     timeRemaining = floor(timeRemaining);
343     return tr("%4 %5 remaining")
344             .arg(timeRemaining)
345             .arg(timeRemainingString);
346 }