Add a Download class to encapsulate a aria2 download.

This commit is contained in:
Matthieu Gautier 2018-10-16 17:51:54 +02:00
parent f718c4c472
commit 43ff8565d1
2 changed files with 150 additions and 24 deletions

View File

@ -21,6 +21,8 @@
#define KIWIX_DOWNLOADER_H
#include <string>
#include <vector>
#include <map>
#include <pthread.h>
#include <memory>
@ -40,6 +42,41 @@ class AriaError : public std::runtime_error {
AriaError(const std::string& message) : std::runtime_error(message) {}
};
class Download {
public:
typedef enum { ACTIVE, WAITING, PAUSED, ERROR, COMPLETE, REMOVED, UNKNOWN } StatusResult;
Download() :
m_status(UNKNOWN) {}
Download(std::shared_ptr<Aria2> p_aria, std::string did)
: mp_aria(p_aria),
m_status(UNKNOWN),
m_did(did) {};
void updateStatus(bool follow=false);
StatusResult getStatus() { return m_status; }
std::string getDid() { return m_did; }
std::string getFollowedBy() { return m_followedBy; }
uint64_t getTotalLength() { return m_totalLength; }
uint64_t getCompletedLength() { return m_completedLength; }
uint64_t getDownloadSpeed() { return m_downloadSpeed; }
uint64_t getVerifiedLength() { return m_verifiedLength; }
std::string getPath() { return m_path; }
std::vector<std::string>& getUris() { return m_uris; }
protected:
std::shared_ptr<Aria2> mp_aria;
StatusResult m_status;
std::string m_did = "";
std::string m_followedBy = "";
uint64_t m_totalLength;
uint64_t m_completedLength;
uint64_t m_downloadSpeed;
uint64_t m_verifiedLength;
std::vector<std::string> m_uris;
std::string m_path;
};
/**
* A tool to download things.
*
@ -60,9 +97,15 @@ class Downloader
*/
DownloadedFile download(const std::string& url);
private:
Download* startDownload(const std::string& uri);
Download* getDownload(const std::string& did);
std::unique_ptr<Aria2> mp_aria;
size_t getNbDownload() { return m_knownDownloads.size(); }
std::vector<std::string> getDownloadIds();
private:
std::map<std::string, std::unique_ptr<Download>> m_knownDownloads;
std::shared_ptr<Aria2> mp_aria;
};
}

View File

@ -20,6 +20,7 @@
#include "downloader.h"
#include "common/pathTools.h"
#include <algorithm>
#include <thread>
#include <chrono>
@ -33,10 +34,73 @@
namespace kiwix
{
void Download::updateStatus(bool follow)
{
static std::vector<std::string> statusKey = {"status", "files", "totalLength",
"completedLength", "followedBy",
"downloadSpeed", "verifiedLength"};
std::string strStatus;
if(follow && !m_followedBy.empty()) {
strStatus = mp_aria->tellStatus(m_followedBy, statusKey);
} else {
strStatus = mp_aria->tellStatus(m_did, statusKey);
}
// std::cout << strStatus << std::endl;
MethodResponse response(strStatus);
if (response.isFault()) {
m_status = Download::UNKNOWN;
return;
}
auto structNode = response.getParams().getParam(0).getValue().getStruct();
auto _status = structNode.getMember("status").getValue().getAsS();
auto status = _status == "active" ? Download::ACTIVE
: _status == "waiting" ? Download::WAITING
: _status == "paused" ? Download::PAUSED
: _status == "error" ? Download::ERROR
: _status == "complete" ? Download::COMPLETE
: _status == "removed" ? Download::REMOVED
: Download::UNKNOWN;
if (status == COMPLETE) {
try {
auto followedByMember = structNode.getMember("followedBy");
m_followedBy = followedByMember.getValue().getArray().getValue(0).getAsS();
if (follow) {
status = ACTIVE;
updateStatus(true);
return;
}
} catch (InvalidRPCNode& e) { }
}
m_status = status;
m_totalLength = std::stoull(structNode.getMember("totalLength").getValue().getAsS());
m_completedLength = std::stoull(structNode.getMember("completedLength").getValue().getAsS());
m_downloadSpeed = std::stoull(structNode.getMember("downloadSpeed").getValue().getAsS());
try {
auto verifiedLengthValue = structNode.getMember("verifiedLength").getValue();
m_verifiedLength = std::stoull(verifiedLengthValue.getAsS());
} catch (InvalidRPCNode& e) { m_verifiedLength = 0; }
auto filesMember = structNode.getMember("files");
auto fileStruct = filesMember.getValue().getArray().getValue(0).getStruct();
m_path = fileStruct.getMember("path").getValue().getAsS();
auto urisArray = fileStruct.getMember("uris").getValue().getArray();
int index = 0;
m_uris.clear();
while(true) {
try {
auto uriNode = urisArray.getValue(index++).getStruct().getMember("uri");
m_uris.push_back(uriNode.getValue().getAsS());
} catch(InvalidRPCNode& e) { break; }
}
}
/* Constructor */
Downloader::Downloader() :
mp_aria(new Aria2())
{
for (auto gid : mp_aria->tellActive()) {
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
m_knownDownloads[gid]->updateStatus();
}
}
@ -50,39 +114,29 @@ void Downloader::close()
mp_aria->close();
}
pugi::xml_node find_member_in_struct(pugi::xml_node struct_node, std::string member_name) {
for(auto member=struct_node.first_child(); member; member=member.next_sibling()) {
std::string _member_name = member.child("name").text().get();
if (_member_name == member_name) {
return member.child("value");
}
std::vector<std::string> Downloader::getDownloadIds() {
std::vector<std::string> ret;
for(auto& p:m_knownDownloads) {
ret.push_back(p.first);
}
return pugi::xml_node();
return ret;
}
DownloadedFile Downloader::download(const std::string& url) {
DownloadedFile fileHandle;
try {
std::vector<std::string> uris = {url};
std::vector<std::string> status_key = {"status", "files"};
std::string gid;
gid = mp_aria->addUri(uris);
std::cerr << "gid is : " << gid << std::endl;
auto download = startDownload(url);
std::cerr << "gid is : " << download->getDid() << std::endl;
pugi::xml_document ret;
while(true) {
auto strStatus = mp_aria->tellStatus(gid, status_key);
MethodResponse response(strStatus);
auto structNode = response.getParams().getParam(0).getValue().getStruct();
auto status = structNode.getMember("status").getValue().getAsS();
std::cerr << "Status is " << status << std::endl;
if (status == "complete") {
download->updateStatus();
std::cerr << "Status is " << download->getStatus() << std::endl;
if (download->getStatus() == Download::COMPLETE) {
fileHandle.success = true;
auto filesMember = structNode.getMember("files");
auto fileStruct = filesMember.getValue().getArray().getValue(0).getStruct();
fileHandle.path = fileStruct.getMember("path").getValue().getAsS();
fileHandle.path = download->getPath();
std::cerr << "FilePath is " << fileHandle.path << std::endl;
} else if (status == "error") {
} else if (download->getStatus() == Download::ERROR) {
fileHandle.success = false;
} else {
// [TODO] Be wise here.
@ -95,4 +149,33 @@ DownloadedFile Downloader::download(const std::string& url) {
return fileHandle;
}
Download* Downloader::startDownload(const std::string& uri)
{
for (auto& p: m_knownDownloads) {
auto& d = p.second;
auto& uris = d->getUris();
if (std::find(uris.begin(), uris.end(), uri) != uris.end())
return d.get();
}
std::vector<std::string> uris = {uri};
auto gid = mp_aria->addUri(uris);
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
return m_knownDownloads[gid].get();
}
Download* Downloader::getDownload(const std::string& did)
{
try {
return m_knownDownloads.at(did).get();
} catch(exception& e) {
for (auto gid : mp_aria->tellActive()) {
if (gid == did) {
m_knownDownloads[gid] = std::unique_ptr<Download>(new Download(mp_aria, gid));
return m_knownDownloads[gid].get();
}
}
throw e;
}
}
}