mirror of https://github.com/kiwix/libkiwix.git
Add a Download class to encapsulate a aria2 download.
This commit is contained in:
parent
f718c4c472
commit
43ff8565d1
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue