#include "DebugRoutines.h" #include "JFileSystem.h" #include "Downloader.h" #define RECORDS_DEFAULT_FILE "cache/records.txt" #ifdef QT_CONFIG QNetworkAccessManager DownloadRequest::networkAccessManager; #endif DownloadRequest::DownloadRequest(string localPath, string remoteResourceURL, string ETag, DownloadStatus downloadStatus, size_t totalSize, size_t currentSize): mLocalPath(localPath), mRemoteResourceURL(remoteResourceURL), mRequestedRemoteResourceURL(remoteResourceURL), mETag(ETag), mDownloadStatus(downloadStatus), mUpgradeAvailable(false), mTotalSize(totalSize), mCurrentSize(currentSize) { } DownloadRequest::~DownloadRequest() { } void DownloadRequest::startHead() { #ifdef QT_CONFIG QNetworkRequest request(QUrl(QString(mRequestedRemoteResourceURL.c_str()))); request.setRawHeader("If-None-Match", mETag.c_str()); mNetworkReply = networkAccessManager.head(request); connect(mNetworkReply, SIGNAL(finished()), SLOT(fileDownloaded())); #endif } void DownloadRequest::startGet() { #ifdef QT_CONFIG mNetworkReply = networkAccessManager.get(QNetworkRequest(QUrl(QString(mRequestedRemoteResourceURL.c_str())))); #endif mFile.close(); JFileSystem::GetInstance()->Remove(getTempLocalPath()); JFileSystem::GetInstance()->openForWrite(mFile, getTempLocalPath()); #ifdef QT_CONFIG connect(mNetworkReply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); connect(mNetworkReply, SIGNAL(finished()), SLOT(fileDownloaded())); #endif } void DownloadRequest::fileDownloaded() { do { QByteArray eTagByteArray = mNetworkReply->rawHeader("ETag"); if(!eTagByteArray.isEmpty()) { string oldETag = mETag; mETag = QString(eTagByteArray).toStdString(); if(oldETag!="" && oldETag != mETag) mUpgradeAvailable = true; } // let's check some error if(mNetworkReply->error() != QNetworkReply::NoError) { DebugTrace(mNetworkReply->errorString().toStdString()); mDownloadStatus = DownloadRequest::DOWNLOAD_ERROR; mFile.close(); JFileSystem::GetInstance()->Remove(getTempLocalPath()); break; } // check if we're getting redirected QVariant redirectionTarget = mNetworkReply->attribute(QNetworkRequest::RedirectionTargetAttribute); if (!redirectionTarget.isNull()) { QUrl newUrl = QUrl(mRequestedRemoteResourceURL.c_str()).resolved(redirectionTarget.toUrl()); DebugTrace(string("Redirect to ")+ newUrl.toString().toStdString()); mRequestedRemoteResourceURL = newUrl.toString().toStdString(); mNetworkReply->deleteLater(); if(mFile.is_open()) startGet(); else startHead(); return; } if(mFile.is_open()) { QByteArray byteArray = mNetworkReply->readAll(); mFile.write(byteArray.constData(), byteArray.size()); mFile.close(); if(!JFileSystem::GetInstance()->Rename(getTempLocalPath(), mLocalPath)) { mDownloadStatus = DownloadRequest::DOWNLOAD_ERROR; break; } } mDownloadStatus = DownloadRequest::DOWNLOADED; } while(0); Downloader::GetInstance()->Update(); mNetworkReply->deleteLater(); emit statusChanged((int)mDownloadStatus); } void DownloadRequest::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { QByteArray byteArray = mNetworkReply->readAll(); mFile.write(byteArray.constData(), byteArray.size()); mCurrentSize = bytesReceived; mTotalSize = bytesTotal; int percent = 0; if(bytesTotal) percent = (bytesReceived/bytesTotal)*100; emit percentChanged(percent); } Downloader* Downloader::mInstance = 0; Downloader::Downloader(string globalRemoteURL, string localcacheRecords): mGlobalRemoteURL(globalRemoteURL), mLocalCacheRecords(localcacheRecords) { JFileSystem::GetInstance()->MakeDir("cache"); izfstream downloadRecords; if(mLocalCacheRecords.empty()) mLocalCacheRecords = RECORDS_DEFAULT_FILE; if(JFileSystem::GetInstance()->openForRead(downloadRecords, mLocalCacheRecords)) {// File exists, let's read it. downloadRecords >> (*this); } JFileSystem::GetInstance()->CloseFile(); } Downloader::~Downloader() { map::iterator ite; for(ite = mRequestMap.begin(); ite != mRequestMap.end(); ite++) { delete (*ite).second; } mRequestMap.erase(mRequestMap.begin(), mRequestMap.end()); } Downloader* Downloader::GetInstance() { if(!mInstance) { mInstance = new Downloader(); } return mInstance; } void Downloader::Release() { if(mInstance) { delete mInstance; mInstance = 0; } } bool DownloadRequest::NetworkIsAccessible() { bool result = false; #ifdef QT_CONFIG networkAccessManager.setNetworkAccessible(QNetworkAccessManager::Accessible); result = networkAccessManager.networkAccessible(); #endif return result; } DownloadRequest* Downloader::Get(string localPath, string remoteResourceURL) { map::iterator ite = mRequestMap.find(localPath); if(ite == mRequestMap.end()) { // request does not exist, let's create it DownloadRequest* request = new DownloadRequest(localPath, remoteResourceURL); std::pair::iterator,bool> ret; ret = mRequestMap.insert ( std::pair(localPath, request) ); if (ret.second==false) { DebugTrace("Downloader::Get Error inserting request in Map"); return 0; } ite = ret.first; } // Now, we can check the server if((*ite).second->getDownloadStatus() == DownloadRequest::NOT_PRESENT || (*ite).second->upgradeAvailable()) { // File is not here or an update is available, let's get it (*ite).second->startGet(); (*ite).second->mDownloadStatus = DownloadRequest::DOWNLOADING; } else if ((*ite).second->getDownloadStatus() == DownloadRequest::DOWNLOADED) { // File is here, let's check if there is some update without blocking the playback (*ite).second->startHead(); } return (*ite).second; } void Downloader::Update() { ofstream downloadRecords; if(JFileSystem::GetInstance()->openForWrite(downloadRecords, mLocalCacheRecords)) { downloadRecords << (*this); } downloadRecords.close(); } ostream& operator<<(ostream& out, const DownloadRequest& d) { // HEAD request fails, so this line erase cache record after upgrade check :( // if(d.getDownloadStatus() == DownloadRequest::DOWNLOADED) { out << "localPath=" << d.mLocalPath << endl; out << "remoteResource=" << d.mRemoteResourceURL << endl; out << "ETag=" << d.mETag << endl; out << "upgradeAvailable=" << d.mUpgradeAvailable <>(istream& in, DownloadRequest& d) { string s; while(std::getline(in, s)) { size_t limiter = s.find("="); string areaS; if (limiter != string::npos) { areaS = s.substr(0, limiter); if (areaS.compare("localPath") == 0) { d.mLocalPath = s.substr(limiter + 1); } else if (areaS.compare("remoteResource") == 0) { d.mRemoteResourceURL = s.substr(limiter + 1); d.mRequestedRemoteResourceURL = d.mRemoteResourceURL; } else if (areaS.compare("ETag") == 0) { d.mETag = s.substr(limiter + 1); d.mDownloadStatus = DownloadRequest::DOWNLOADED; } else if (areaS.compare("upgradeAvailable") == 0) { d.mUpgradeAvailable = (bool)atoi(s.substr(limiter + 1).c_str()); break; } } } return in; } ostream& operator<<(ostream& out, const Downloader& d) { map::const_iterator ite; for(ite = d.mRequestMap.begin(); ite != d.mRequestMap.end(); ite++) { out << (*(*ite).second) << endl; } return out; } istream& operator>>(istream& in, Downloader& d) { while(!in.eof()) { DownloadRequest* downloadRequest = new DownloadRequest(); in >> (*downloadRequest); if(!downloadRequest->getLocalPath().empty() && !downloadRequest->getRemoteResource().empty() && !downloadRequest->getETag().empty()) { d.mRequestMap[downloadRequest->getLocalPath()] = downloadRequest; } else { delete downloadRequest; } } return in; }