+/* $BEGIN_LICENSE
+
+This file is part of Minitube.
+Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
+
+Minitube is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Minitube is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Minitube. If not, see <http://www.gnu.org/licenses/>.
+
+$END_LICENSE */
+
#include "downloaditem.h"
-#include "networkaccess.h"
+#include "http.h"
+#include "httputils.h"
#include "video.h"
#include <QDesktopServices>
+#include <QDebug>
-namespace The {
- NetworkAccess* http();
-}
+#ifdef APP_MAC
+#include "macutils.h"
+#endif
DownloadItem::DownloadItem(Video *video, QUrl url, QString filename, QObject *parent)
: QObject(parent)
, m_startedSaving(false)
, m_finishedDownloading(false)
, m_url(url)
+ , m_offset(0)
+ , sendStatusChanges(true)
, m_file(filename)
, m_reply(0)
, video(video)
speedCheckTimer->setInterval(2000);
speedCheckTimer->setSingleShot(true);
connect(speedCheckTimer, SIGNAL(timeout()), SLOT(speedCheck()));
+
+ if (m_file.exists())
+ m_file.remove();
}
DownloadItem::~DownloadItem() {
- if (m_reply) delete m_reply;
- if (video) delete video;
+ if (m_reply) {
+ delete m_reply;
+ m_reply = 0;
+ }
+ if (video) {
+ delete video;
+ video = 0;
+ }
+}
+
+bool DownloadItem::needsDownload(qint64 offset) {
+ return offset < m_offset || offset > m_offset + m_bytesReceived;
+}
+
+bool DownloadItem::isBuffered(qint64 offset) {
+ QMap<qint64, qint64>::iterator i;
+ for (i = buffers.begin(); i != buffers.end(); ++i) {
+ if (offset >= i.key() && offset <= i.value()) {
+ // qDebug() << "Buffered! " << i.key() << ":" << i.value();
+ return true;
+ }
+ }
+ // qDebug() << offset << "is not buffered";
+ return false;
+}
+
+qint64 DownloadItem::blankAtOffset(qint64 offset) {
+ // qDebug() << buffers;
+ QMap<qint64, qint64>::iterator i;
+ for (i = buffers.begin(); i != buffers.end(); ++i) {
+ if (offset >= i.key() && offset <= i.value()) {
+ // qDebug() << "Offset was" << offset << "now" << i.value();
+ return i.value() + 1;
+ }
+ }
+ return offset;
+}
+
+void DownloadItem::seekTo(qint64 offset, bool sendStatusChanges) {
+ // qDebug() << __PRETTY_FUNCTION__ << offset << sendStatusChanges;
+ stop();
+ if (m_bytesReceived > 0) {
+ bool bufferModified = false;
+ QMap<qint64, qint64>::iterator i;
+ for (i = buffers.begin(); i != buffers.end(); ++i) {
+ if (m_offset - 1 <= i.value()) {
+ /*
+ qDebug() << "Extending existing buffer "
+ << i.key() << i.value() << "now" << m_offset + m_bytesReceived;
+ */
+ bufferModified = true;
+ i.value() = m_offset + m_bytesReceived;
+ break;
+ }
+ }
+ if (!bufferModified)
+ buffers.insert(m_offset, m_offset + m_bytesReceived);
+ }
+ m_offset = offset;
+ this->sendStatusChanges = sendStatusChanges;
+ bool seekSuccess = m_file.seek(offset);
+ if (!seekSuccess) {
+ qWarning() << "Cannot seek to offset" << offset << "in file" << m_file.fileName();
+ return;
+ }
+ start();
}
void DownloadItem::start() {
- m_reply = The::http()->simpleGet(m_url);
+ // qDebug() << "Starting download at" << m_offset;
+ HttpRequest req;
+ req.url = m_url;
+ if (m_offset > 0) req.offset = m_offset;
+ m_reply = HttpUtils::yt().networkReply(req);
+
init();
}
if (!m_reply)
return;
- if (m_file.exists())
- m_file.remove();
-
m_status = Starting;
-
+ m_bytesReceived = 0;
m_startedSaving = false;
m_finishedDownloading = false;
this, SLOT(requestFinished()));
// start timer for the download estimation
+ m_totalTime = 0;
m_downloadTime.start();
speedCheckTimer->start();
void DownloadItem::stop() {
- if (m_reply)
+ if (m_reply) {
+ m_reply->disconnect();
m_reply->abort();
+ m_reply->deleteLater();
+ m_reply = 0;
+ }
m_status = Idle;
emit statusChanged();
}
void DownloadItem::openFolder() {
QFileInfo info(m_file);
+#ifdef APP_MAC
+ mac::showInFinder(info.absoluteFilePath());
+#else
QUrl url = QUrl::fromLocalFile(info.absolutePath());
QDesktopServices::openUrl(url);
+#endif
}
void DownloadItem::tryAgain() {
- if (m_reply)
- m_reply->abort();
-
- if (m_file.exists())
- m_file.remove();
-
- m_reply = The::http()->simpleGet(m_url);
- init();
- emit statusChanged();
+ stop();
+ start();
}
void DownloadItem::downloadReadyRead() {
+ if (!m_reply) return;
if (!m_file.isOpen()) {
if (!m_file.open(QIODevice::ReadWrite)) {
- qDebug() << QString("Error opening output file: %1").arg(m_file.errorString());
+ qWarning() << QString("Error opening output file: %1").arg(m_file.errorString());
stop();
emit statusChanged();
return;
emit statusChanged();
}
+ // qWarning() << __PRETTY_FUNCTION__ << m_file.pos();
if (-1 == m_file.write(m_reply->readAll())) {
- /*
- downloadInfoLabel->setText(tr("Error saving: %1")
- .arg(m_output.errorString()));
- stopButton->click();
- */
+ qWarning() << "Error saving." << m_file.errorString();
} else {
+
m_startedSaving = true;
- if (m_status != Downloading) {
- // m_status = Downloading;
- // emit statusChanged();
- } else if (m_finishedDownloading)
- requestFinished();
+
+ // if (m_finishedDownloading) requestFinished();
}
}
void DownloadItem::error(QNetworkReply::NetworkError) {
-#ifdef DOWNLOADMANAGER_DEBUG
- qDebug() << "DownloadItem::" << __FUNCTION__ << m_reply->errorString() << m_url;
-#endif
-
- qDebug() << m_reply->errorString();
+ if (m_reply) {
+ qWarning() << m_reply->errorString() << m_reply->url().toEncoded();
+ m_errorMessage = m_reply->errorString();
+ }
- m_errorMessage = m_reply->errorString();
m_reply = 0;
m_status = Failed;
}
void DownloadItem::metaDataChanged() {
+ if (!m_reply) return;
QVariant locationHeader = m_reply->header(QNetworkRequest::LocationHeader);
if (locationHeader.isValid()) {
m_url = locationHeader.toUrl();
// qDebug() << "Redirecting to" << m_url;
- m_reply->deleteLater();
- m_reply = The::http()->simpleGet(m_url);
- init();
+ tryAgain();
return;
}
-
-#ifdef DOWNLOADMANAGER_DEBUG
- qDebug() << "DownloadItem::" << __FUNCTION__ << "not handled.";
-#endif
+ // qDebug() << m_reply->rawHeaderList();
}
int DownloadItem::initialBufferSize() {
// qDebug() << video->getDefinitionCode();
switch (video->getDefinitionCode()) {
case 18:
- return 1024*192;
- case 22:
return 1024*512;
+ case 22:
+ return 1024*1024;
case 37:
- return 1024*768;
+ return 1024*1024*2;
}
return 1024*128;
}
void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
- if (m_lastProgressTime.elapsed() < 150) return;
- m_lastProgressTime.start();
+ // qDebug() << __PRETTY_FUNCTION__ << bytesReceived << bytesTotal << m_downloadTime.elapsed();
m_bytesReceived = bytesReceived;
+ if (!sendStatusChanges) return;
+
+ if (m_lastProgressTime.elapsed() < 150) return;
+ m_lastProgressTime.start();
+
if (m_status != Downloading) {
- int neededBytes = (int) (bytesTotal * .001);
- // qDebug() << bytesReceived << bytesTotal << neededBytes << m_downloadTime.elapsed();
+ int neededBytes = (int) (bytesTotal * .005);
int bufferSize = initialBufferSize();
+ if (bufferSize > bytesTotal) bufferSize = bytesTotal;
+ // qDebug() << bytesReceived << bytesTotal << neededBytes << bufferSize << m_downloadTime.elapsed();
if (bytesReceived > bufferSize
- && bytesReceived > neededBytes
- && m_downloadTime.elapsed() > 1000 ) {
+ && bytesReceived > neededBytes
+ && m_downloadTime.elapsed() > 2000) {
emit bufferProgress(100);
m_status = Downloading;
emit statusChanged();
} else {
- int bufferPercent = bytesReceived * 100 / qMax(bufferSize, neededBytes);
+ int bytes = qMax(bufferSize, neededBytes);
+ int bufferPercent = 0;
+ if (bytes > 0)
+ bufferPercent = bytesReceived * 100 / bytes;
emit bufferProgress(bufferPercent);
}
void DownloadItem::speedCheck() {
if (!m_reply) return;
- if (m_bytesReceived < initialBufferSize() / 3) {
- m_reply->disconnect();
- m_reply->abort();
- m_reply->deleteLater();
- m_reply = 0;
+ int bytesTotal = m_reply->size();
+ int bufferSize = initialBufferSize();
+ if (bufferSize > bytesTotal) bufferSize = 0;
+ if (m_bytesReceived < bufferSize / 3) {
+ stop();
// too slow! retry
qDebug() << "Retrying...";
- connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)));
+ connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)), Qt::UniqueConnection);
video->loadStreamUrl();
}
}
-void DownloadItem::gotStreamUrl(QUrl streamUrl) {
+void DownloadItem::gotStreamUrl(QUrl /*streamUrl*/) {
Video *video = static_cast<Video *>(sender());
if (!video) {
}
video->disconnect(this);
- m_reply = The::http()->simpleGet(video->getStreamUrl());
- init();
+ m_url = video->getStreamUrl();
+ start();
}
qint64 DownloadItem::bytesTotal() const {
if (m_finishedDownloading)
return -1.0;
- double timeRemaining = ((double)(bytesTotal() - bytesReceived())) / currentSpeed();
+ double speed = currentSpeed();
+ double timeRemaining = 0.0;
+ if (speed > 0.0)
+ timeRemaining = ((double)(bytesTotal() - bytesReceived())) / speed;
// When downloading the eta should never be 0
if (timeRemaining == 0)
if (m_finishedDownloading)
return -1.0;
- return m_bytesReceived * 1000.0 / m_downloadTime.elapsed();
+ int elapsed = m_downloadTime.elapsed();
+ double speed = -1.0;
+ if (elapsed > 0)
+ speed = m_bytesReceived * 1000.0 / elapsed;
+ return speed;
}
void DownloadItem::requestFinished() {
- m_reply = 0;
- m_finishedDownloading = true;
if (!m_startedSaving) {
qDebug() << "Request finished but never started saving";
+ tryAgain();
return;
}
- m_file.close();
+
+ if (m_bytesReceived <= 0) {
+ qDebug() << "Request finished but saved 0 bytes";
+ tryAgain();
+ return;
+ }
+
+ m_finishedDownloading = true;
+
+ if (m_status == Starting) {
+ m_status = Downloading;
+ emit statusChanged();
+ }
+ if (m_offset == 0) m_file.close();
m_status = Finished;
+ m_totalTime = m_downloadTime.elapsed() / 1000.0;
emit statusChanged();
emit finished();
+ m_reply->deleteLater();
+ m_reply = 0;
}
QString DownloadItem::formattedFilesize(qint64 size) {
- /*
- if (size < 1024) return tr("%1 bytes").arg(size);
- else if (size < 1024*1024) return tr("%1 KB").arg(size/1024);
- else if (size < 1024*1024*1024) return tr("%1 MB").arg(size/1024/1024);
- else return tr("%1 GB").arg(size/1024/1024/1024);
- */
QString unit;
if (size < 1024) {
unit = tr("bytes");
}
QString DownloadItem::formattedSpeed(double speed) {
- /*
- static const int K = 1024;
- if (speed < K) return tr("%1 bytes/s").arg(speed);
- else if (speed < K*K) return tr("%1 KB/s").arg(speed/K);
- else if (speed < K*K*K) return tr("%1 MB/s").arg(speed/K/K);
- else return tr("%1 GB/s").arg(speed/K/K/K);
- */
int speedInt = (int) speed;
QString unit;
if (speedInt < 1024) {
return QString(QLatin1String("%1 %2")).arg(speedInt).arg(unit);
}
-QString DownloadItem::formattedTime(double timeRemaining) {
+QString DownloadItem::formattedTime(double timeRemaining, bool remaining) {
QString timeRemainingString = tr("seconds");
if (timeRemaining > 60) {
timeRemaining = timeRemaining / 60;
timeRemainingString = tr("minutes");
}
timeRemaining = floor(timeRemaining);
- return tr("%4 %5 remaining")
+ QString msg = remaining ? tr("%4 %5 remaining") : "%4 %5";
+ return msg
.arg(timeRemaining)
.arg(timeRemainingString);
}