Commit 0eeb8b98 authored by Paweł Wegner's avatar Paweł Wegner

Introduced GetItemUrlRequest.

parent 3ae3aa13
......@@ -29,22 +29,15 @@
namespace cloudstorage {
EitherError<std::string> generate_thumbnail(IItem::Pointer item) {
if ((item->type() == IItem::FileType::Image ||
item->type() == IItem::FileType::Video) &&
!item->url().empty()) {
try {
std::vector<uint8_t> buffer;
ffmpegthumbnailer::VideoThumbnailer thumbnailer;
thumbnailer.generateThumbnail(item->url(), ThumbnailerImageType::Png,
buffer);
auto ptr = reinterpret_cast<const char*>(buffer.data());
return std::string(ptr, ptr + buffer.size());
} catch (const std::exception& e) {
return Error{IHttpRequest::Bad, e.what()};
}
} else {
return Error{IHttpRequest::Bad, "invalid file type"};
EitherError<std::string> generate_thumbnail(const std::string& url) {
try {
std::vector<uint8_t> buffer;
ffmpegthumbnailer::VideoThumbnailer thumbnailer;
thumbnailer.generateThumbnail(url, ThumbnailerImageType::Png, buffer);
auto ptr = reinterpret_cast<const char*>(buffer.data());
return std::string(ptr, ptr + buffer.size());
} catch (const std::exception& e) {
return Error{IHttpRequest::Bad, e.what()};
}
}
......
......@@ -27,7 +27,7 @@
namespace cloudstorage {
EitherError<std::string> generate_thumbnail(IItem::Pointer);
EitherError<std::string> generate_thumbnail(const std::string& url);
} // namespace cloudstorage
......
......@@ -50,6 +50,11 @@ ICloudProvider::ExchangeCodeRequest::Pointer MockProvider::exchangeCodeAsync(
return nullptr;
}
ICloudProvider::GetItemUrlRequest::Pointer MockProvider::getItemUrlAsync(
IItem::Pointer, GetItemUrlCallback) {
return nullptr;
}
ICloudProvider::ListDirectoryRequest::Pointer MockProvider::listDirectoryAsync(
IItem::Pointer directory, IListDirectoryCallback::Pointer callback) {
return util::make_unique<MockListDirectoryRequest>(directory,
......
......@@ -39,7 +39,6 @@ class MockProvider : public ICloudProvider {
std::string extension() const override { return ""; }
std::string id() const override { return filename_; }
TimeStamp timestamp() const override { return UnknownTimeStamp; }
std::string url() const override { return ""; }
size_t size() const override { return 0; }
bool is_hidden() const override { return false; }
FileType type() const override { return type_; }
......@@ -147,6 +146,8 @@ class MockProvider : public ICloudProvider {
ExchangeCodeRequest::Pointer exchangeCodeAsync(const std::string&,
ExchangeCodeCallback) override;
GetItemUrlRequest::Pointer getItemUrlAsync(IItem::Pointer,
GetItemUrlCallback) override;
ListDirectoryRequest::Pointer listDirectoryAsync(
IItem::Pointer, IListDirectoryCallback::Pointer) override;
GetItemRequest::Pointer getItemAsync(const std::string& absolute_path,
......
......@@ -302,7 +302,7 @@ void Window::cancelRequests() {
if (list_directory_request_) list_directory_request_->cancel();
if (download_request_) download_request_->cancel();
if (upload_request_) upload_request_->cancel();
if (item_data_request_) item_data_request_->cancel();
if (url_request_) url_request_->cancel();
if (delete_item_request_) delete_item_request_->cancel();
if (create_directory_request_) create_directory_request_->cancel();
if (move_item_request_) move_item_request_->cancel();
......@@ -345,9 +345,15 @@ void Window::play(int item_id) {
if (item->item()->type() != IItem::FileType::Audio)
contentItem()->setFocus(false);
last_played_ = item_id;
item_data_request_ = cloud_provider_->getItemDataAsync(
item->item()->id(), [this](EitherError<IItem> i) {
if (i.right()) emit runPlayerFromUrl(i.right()->url().c_str());
url_request_ = cloud_provider_->getItemUrlAsync(
item->item(), [this](EitherError<std::string> i) {
if (i.right())
emit runPlayerFromUrl(i.right()->c_str());
else {
auto lock = stream_lock();
std::cerr << "[FAIL] Failed to get url: " << i.left()->description_
<< "\n";
}
});
}
......@@ -466,8 +472,11 @@ ItemModel::ItemModel(IItem::Pointer item, std::shared_ptr<ICloudProvider> p,
[=]() {
auto tinfo = thread_info_;
auto item = item_;
auto provider = provider_;
thumbnail_thread_ = std::thread([=] {
auto thumbnail = cloudstorage::generate_thumbnail(item);
auto url = provider->getItemUrlAsync(item)->result();
if (url.left()) return;
auto thumbnail = cloudstorage::generate_thumbnail(*url.right());
std::lock_guard<std::mutex> lock(tinfo->lock_);
if (!tinfo->nuked_ && thumbnail.right()) {
auto data = *thumbnail.right();
......
......@@ -177,7 +177,7 @@ class Window : public QQuickView {
ICloudProvider::ListDirectoryRequest::Pointer list_directory_request_;
ICloudProvider::DownloadFileRequest::Pointer download_request_;
ICloudProvider::UploadFileRequest::Pointer upload_request_;
ICloudProvider::GetItemDataRequest::Pointer item_data_request_;
ICloudProvider::GetItemUrlRequest::Pointer url_request_;
ICloudProvider::DeleteItemRequest::Pointer delete_item_request_;
ICloudProvider::CreateDirectoryRequest::Pointer create_directory_request_;
ICloudProvider::MoveItemRequest::Pointer move_item_request_;
......
......@@ -150,8 +150,10 @@ int main(int, char**) {
}
if (data->size() != IItem::UnknownSize)
std::cout << "size: " << data->size() << "\n";
if (!data->url().empty())
std::cout << "url: " << data->url() << "\n";
auto url_request =
current_provider->getItemUrlAsync(item)->result();
if (auto url = url_request.right())
std::cout << "url: " << url << "\n";
}
}
}
......
......@@ -49,6 +49,19 @@ bool Box::reauthorize(int code, const IHttpRequest::HeaderParameters&) const {
return IHttpRequest::isClientError(code) && code != IHttpRequest::NotFound;
}
IHttpRequest::Pointer Box::getItemUrlRequest(const IItem& item,
std::ostream&) const {
auto request = http()->create(
endpoint() + "/2.0/files/" + item.id() + "/content", "GET", false);
return request;
}
std::string Box::getItemUrlResponse(std::istream& response) const {
std::string url;
response >> url;
return url;
}
ICloudProvider::GetItemDataRequest::Pointer Box::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
......@@ -80,30 +93,6 @@ ICloudProvider::GetItemDataRequest::Pointer Box::getItemDataAsync(
}
},
output);
} else {
try {
Json::Value response;
*output >> response;
auto item = toItem(response);
r->sendRequest(
[this, id](util::Output) {
auto request = http()->create(
endpoint() + "/2.0/files/" + id + "/content", "GET",
false);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left() &&
IHttpRequest::isRedirect(e.left()->code_)) {
static_cast<Item*>(item.get())
->set_url(e.left()->description_);
}
r->done(item);
},
output);
} catch (Json::Exception e) {
r->done(Error{IHttpRequest::Failure, e.what()});
}
}
},
output);
......
......@@ -43,6 +43,8 @@ class Box : public CloudProvider {
GetItemDataCallback) override;
private:
IHttpRequest::Pointer getItemUrlRequest(
const IItem&, std::ostream& input_stream) const override;
IHttpRequest::Pointer listDirectoryRequest(
const IItem&, const std::string& page_token,
std::ostream& input_stream) const override;
......@@ -66,6 +68,7 @@ class Box : public CloudProvider {
IItem::Pointer getItemDataResponse(std::istream& response) const override;
std::vector<IItem::Pointer> listDirectoryResponse(
const IItem&, std::istream&, std::string& next_page_token) const override;
std::string getItemUrlResponse(std::istream& response) const override;
IItem::Pointer toItem(const Json::Value&) const;
......
......@@ -37,6 +37,7 @@
#include "Request/ExchangeCodeRequest.h"
#include "Request/GetItemDataRequest.h"
#include "Request/GetItemRequest.h"
#include "Request/GetItemUrlRequest.h"
#include "Request/ListDirectoryPageRequest.h"
#include "Request/ListDirectoryRequest.h"
#include "Request/MoveItemRequest.h"
......@@ -400,6 +401,13 @@ ICloudProvider::RenameItemRequest::Pointer CloudProvider::renameItemAsync(
->run();
}
ICloudProvider::GetItemUrlRequest::Pointer CloudProvider::getItemUrlAsync(
IItem::Pointer i, GetItemUrlCallback callback) {
return std::make_shared<cloudstorage::GetItemUrlRequest>(shared_from_this(),
i, callback)
->run();
}
ICloudProvider::ListDirectoryPageRequest::Pointer
CloudProvider::listDirectoryPageAsync(IItem::Pointer directory,
const std::string& token,
......@@ -443,6 +451,11 @@ IHttpRequest::Pointer CloudProvider::getItemDataRequest(const std::string&,
return nullptr;
}
IHttpRequest::Pointer CloudProvider::getItemUrlRequest(const IItem&,
std::ostream&) const {
return nullptr;
}
IHttpRequest::Pointer CloudProvider::listDirectoryRequest(const IItem&,
const std::string&,
std::ostream&) const {
......@@ -493,6 +506,10 @@ IItem::Pointer CloudProvider::getItemDataResponse(std::istream&) const {
return nullptr;
}
std::string CloudProvider::getItemUrlResponse(std::istream&) const {
return "";
}
std::vector<IItem::Pointer> CloudProvider::listDirectoryResponse(
const IItem&, std::istream&, std::string&) const {
return {};
......
......@@ -58,6 +58,8 @@ class CloudProvider : public ICloudProvider,
virtual AuthorizeRequest::Pointer authorizeAsync();
GetItemUrlRequest::Pointer getItemUrlAsync(IItem::Pointer,
GetItemUrlCallback) override;
ExchangeCodeRequest::Pointer exchangeCodeAsync(const std::string&,
ExchangeCodeCallback) override;
ListDirectoryRequest::Pointer listDirectoryAsync(
......@@ -110,6 +112,9 @@ class CloudProvider : public ICloudProvider,
virtual IHttpRequest::Pointer getItemDataRequest(
const std::string& id, std::ostream& input_stream) const;
virtual IHttpRequest::Pointer getItemUrlRequest(
const IItem&, std::ostream& input_stream) const;
/**
* Used by default implementation of listDirectoryAsync.
*
......@@ -203,6 +208,8 @@ class CloudProvider : public ICloudProvider,
*/
virtual IItem::Pointer getItemDataResponse(std::istream& response) const;
virtual std::string getItemUrlResponse(std::istream& response) const;
/**
* Used by default implementation of listDirectoryAsync, should extract items
* from response.
......
......@@ -52,63 +52,38 @@ bool Dropbox::reauthorize(int code,
return code == IHttpRequest::Bad || code == IHttpRequest::Unauthorized;
}
ICloudProvider::GetItemDataRequest::Pointer Dropbox::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output input) {
auto request =
http()->create(endpoint() + "/2/files/get_metadata", "POST");
request->setHeaderParameter("Content-Type", "application/json");
Json::Value parameter;
parameter["path"] = id;
parameter["include_media_info"] = true;
*input << Json::FastWriter().write(parameter);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left()) return r->done(e.left());
try {
Json::Value response;
*output >> response;
auto item = toItem(response);
if (item->type() == IItem::FileType::Directory)
return r->done(item);
r->sendRequest(
[=](util::Output input) {
auto request = http()->create(
endpoint() + "/2/files/get_temporary_link", "POST");
request->setHeaderParameter("Content-Type",
"application/json");
Json::Value parameter;
parameter["path"] = id;
*input << Json::FastWriter().write(parameter);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left()) return r->done(item);
try {
Json::Value response;
*output >> response;
static_cast<Item*>(item.get())
->set_url(response["link"].asString());
r->done(item);
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
}
},
output);
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
}
},
output);
},
callback);
return r->run();
IHttpRequest::Pointer Dropbox::getItemUrlRequest(const IItem& item,
std::ostream& input) const {
auto request =
http()->create(endpoint() + "/2/files/get_temporary_link", "POST");
request->setHeaderParameter("Content-Type", "application/json");
Json::Value parameter;
parameter["path"] = item.id();
input << Json::FastWriter().write(parameter);
return request;
}
std::string Dropbox::getItemUrlResponse(std::istream& output) const {
Json::Value response;
output >> response;
return response["link"].asString();
}
IHttpRequest::Pointer Dropbox::getItemDataRequest(const std::string& id,
std::ostream& input) const {
auto request = http()->create(endpoint() + "/2/files/get_metadata", "POST");
request->setHeaderParameter("Content-Type", "application/json");
Json::Value parameter;
parameter["path"] = id;
parameter["include_media_info"] = true;
input << Json::FastWriter().write(parameter);
return request;
}
IItem::Pointer Dropbox::getItemDataResponse(std::istream& stream) const {
Json::Value response;
stream >> response;
return toItem(response);
}
IHttpRequest::Pointer Dropbox::listDirectoryRequest(
......
......@@ -40,9 +40,10 @@ class Dropbox : public CloudProvider {
bool reauthorize(int code,
const IHttpRequest::HeaderParameters&) const override;
GetItemDataRequest::Pointer getItemDataAsync(const std::string& id,
GetItemDataCallback) override;
IHttpRequest::Pointer getItemUrlRequest(
const IItem&, std::ostream& input_stream) const override;
IHttpRequest::Pointer getItemDataRequest(
const std::string&, std::ostream& input_stream) const override;
IHttpRequest::Pointer listDirectoryRequest(
const IItem&, const std::string& page_token,
std::ostream& input_stream) const override;
......@@ -68,6 +69,8 @@ class Dropbox : public CloudProvider {
std::vector<IItem::Pointer> listDirectoryResponse(
const IItem&, std::istream&, std::string& next_page_token) const override;
IItem::Pointer createDirectoryResponse(std::istream&) const override;
std::string getItemUrlResponse(std::istream& response) const override;
IItem::Pointer getItemDataResponse(std::istream& response) const override;
void authorizeRequest(IHttpRequest&) const override;
......
......@@ -45,69 +45,30 @@ bool PCloud::reauthorize(int, const IHttpRequest::HeaderParameters& h) const {
return (it != h.end() && (it->second == "1000" || it->second == "2000"));
}
ICloudProvider::GetItemDataRequest::Pointer PCloud::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
using RequestType = Request<EitherError<IItem>>;
auto r = std::make_shared<RequestType>(shared_from_this());
auto get_history = [=](RequestType::Pointer r,
std::function<void(EitherError<IItem>)> f) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto r = http()->create(endpoint() + "/checksumfile");
r->setParameter("fileid", id);
return r;
},
[=](EitherError<util::Output> e) {
if (e.left()) return f(e.left());
try {
Json::Value json;
*output >> json;
auto entries = json["entries"];
f(toItem(entries[entries.size() - 1]["metadata"]));
} catch (std::exception e) {
f(Error{IHttpRequest::Failure, e.what()});
}
},
output);
};
auto get_link = [=](RequestType::Pointer r, const std::string& path,
std::function<void(EitherError<std::string>)> f) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto r = http()->create(endpoint() + path);
r->setParameter("fileid", id);
return r;
},
[=](EitherError<util::Output> e) {
if (e.left()) return f(e.left());
try {
Json::Value json;
*output >> json;
f("https://" + json["hosts"][0].asString() +
json["path"].asString());
} catch (std::exception e) {
f(Error{IHttpRequest::Failure, e.what()});
}
},
output);
};
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
get_history(r, [=](EitherError<IItem> e) {
if (e.left()) return r->done(e);
auto i = e.right();
if (i->type() == IItem::FileType::Directory) return r->done(e);
get_link(r, "/getfilelink", [=](EitherError<std::string> e) {
if (auto str = e.right())
static_cast<Item*>(i.get())->set_url(*str);
r->done(i);
});
});
},
callback);
return r->run();
IHttpRequest::Pointer PCloud::getItemUrlRequest(const IItem& item,
std::ostream&) const {
auto r = http()->create(endpoint() + "/getfilelink");
r->setParameter("fileid", item.id());
return r;
}
std::string PCloud::getItemUrlResponse(std::istream& response) const {
Json::Value json;
response >> json;
return "https://" + json["hosts"][0].asString() + json["path"].asString();
}
IHttpRequest::Pointer PCloud::getItemDataRequest(const std::string& id,
std::ostream&) const {
auto r = http()->create(endpoint() + "/checksumfile");
r->setParameter("fileid", id);
return r;
}
IItem::Pointer PCloud::getItemDataResponse(std::istream& response) const {
Json::Value json;
response >> json;
return toItem(json["metadata"]);
}
IHttpRequest::Pointer PCloud::listDirectoryRequest(const IItem& item,
......
......@@ -37,8 +37,10 @@ class PCloud : public CloudProvider {
std::string endpoint() const override;
bool reauthorize(int, const IHttpRequest::HeaderParameters&) const override;
GetItemDataRequest::Pointer getItemDataAsync(
const std::string&, GetItemDataCallback callback) override;
IHttpRequest::Pointer getItemUrlRequest(
const IItem&, std::ostream& input_stream) const override;
IHttpRequest::Pointer getItemDataRequest(
const std::string&, std::ostream& input_stream) const override;
IHttpRequest::Pointer listDirectoryRequest(
const IItem&, const std::string& page_token,
std::ostream& input_stream) const override;
......@@ -59,6 +61,8 @@ class PCloud : public CloudProvider {
std::vector<IItem::Pointer> listDirectoryResponse(
const IItem&, std::istream&, std::string& next_page_token) const override;
std::string getItemUrlResponse(std::istream& response) const override;
IItem::Pointer getItemDataResponse(std::istream& response) const override;
IItem::Pointer toItem(const Json::Value&) const;
......
......@@ -49,57 +49,31 @@ IItem::Pointer YandexDisk::rootDirectory() const {
IItem::FileType::Directory);
}
ICloudProvider::GetItemDataRequest::Pointer YandexDisk::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto request =
http()->create(endpoint() + "/v1/disk/resources", "GET");
request->setParameter("path", id);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left()) return r->done(e.left());
try {
Json::Value json;
*output >> json;
auto item = toItem(json);
if (item->type() != IItem::FileType::Directory) {
r->sendRequest(
[=](util::Output) {
auto request = http()->create(
endpoint() + "/v1/disk/resources/download", "GET");
request->setParameter("path", id);
return request;
},
[=](EitherError<util::Output> e) {
try {
if (!e.left()) {
Json::Value json;
*output >> json;
static_cast<Item*>(item.get())
->set_url(json["href"].asString());
}
r->done(item);
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
}
},
output);
} else
r->done(item);
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
}
},
output);
},
callback);
return r->run();
IHttpRequest::Pointer YandexDisk::getItemUrlRequest(const IItem& item,
std::ostream&) const {
auto request =
http()->create(endpoint() + "/v1/disk/resources/download", "GET");
request->setParameter("path", item.id());
return request;
}
std::string YandexDisk::getItemUrlResponse(std::istream& response) const {
Json::Value json;
response >> json;
return json["href"].asString();
}
IHttpRequest::Pointer YandexDisk::getItemDataRequest(const std::string& id,
std::ostream&) const {
auto request = http()->create(endpoint() + "/v1/disk/resources", "GET");
request->setParameter("path", id);
return request;
}
IItem::Pointer YandexDisk::getItemDataResponse(std::istream& response) const {
Json::Value json;
response >> json;
return toItem(json);
}
ICloudProvider::DownloadFileRequest::Pointer YandexDisk::downloadFileAsync(
......
......@@ -38,8 +38,6 @@ class YandexDisk : public CloudProvider {
std::string endpoint() const override;
IItem::Pointer rootDirectory() const override;
GetItemDataRequest::Pointer getItemDataAsync(
const std::string& id, GetItemDataCallback callback) override;
DownloadFileRequest::Pointer downloadFileAsync(IItem::Pointer,
IDownloadFileCallback::Pointer,
Range) override;
......@@ -49,6 +47,10 @@ class YandexDisk : public CloudProvider {
CreateDirectoryRequest::Pointer createDirectoryAsync(
IItem::Pointer, const std::string&, CreateDirectoryCallback) override;
IHttpRequest::Pointer getItemUrlRequest(
const IItem&, std::ostream& input_stream) const override;
IHttpRequest::Pointer getItemDataRequest(
const std::string&, std::ostream& input_stream) const override;
IHttpRequest::Pointer listDirectoryRequest(
const IItem&, const std::string& page_token,
std::ostream& input_stream) const override;
......@@ -61,6 +63,8 @@ class YandexDisk : public CloudProvider {
std::vector<IItem::Pointer> listDirectoryResponse(
const IItem&, std::istream&, std::string& next_page_token) const override;
IItem::Pointer getItemDataResponse(std::istream& response) const override;
std::string getItemUrlResponse(std::istream& response) const override;
IItem::Pointer toItem(const Json::Value&) const;
void authorizeRequest(IHttpRequest&) const override;
......
......@@ -109,6 +109,24 @@ ICloudProvider::ListDirectoryRequest::Pointer YouTube::listDirectoryAsync(
->run();
}
IHttpRequest::Pointer YouTube::getItemUrlRequest(const IItem& item,
std::ostream&) const {
auto request = http()->create(youtube_dl_url_ + "/api/info", "GET");
auto id_data = from_string(item.id());
request->setParameter("format", id_data.audio ? "bestaudio" : "best");
request->setParameter("url", "http://youtube.com/watch?v=" + id_data.id);
return request;
}
std::string YouTube::getItemUrlResponse(std::istream& stream) const {
Json::Value response;
stream >> response;
for (auto v : response["info"]["formats"])
if (v["format_id"] == response["info"]["format_id"])
return v["url"].asString();
throw std::logic_error("invalid response");
}
ICloudProvider::GetItemDataRequest::Pointer YouTube::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
......@@ -127,64 +145,10 @@ ICloudProvider::GetItemDataRequest::Pointer YouTube::getItemDataAsync(
[=](EitherError<util::Output> e) {
if (e.left()) return r->done(e.left());
auto id_data = from_string(id);
auto item = getItemDataResponse(*response_stream, id_data.audio);
auto stream = std::make_shared<std::stringstream>();
auto request = [=](std::string format) {
return [=](util::Output) {
auto request =
http()->create(youtube_dl_url_ + "/api/info", "GET");
request->setParameter("format", format);
request->setParameter(
"url", "http://youtube.com/watch?v=" + id_data.id);
return request;
};
};
auto response = [=](EitherError<util::Output> e) {
if (e.left()) return r->done(e.left());
try {
Json::Value response;
*stream >> response;
for (auto v : response["info"]["formats"]) {
if (v["format_id"] == response["info"]["format_id"]) {
auto nitem = static_cast<Item*>(item.get());
auto size = v["filesize"].isNull()
? IItem::UnknownSize
: v["filesize"].asUInt64();
nitem->set_size(size);
nitem->set_filename(item->filename() + "." +
v["ext"].asString());
nitem->set_url(v["url"].asString());
}
}
if (item->size() == IItem::UnknownSize) {
auto request = http()->create(item->url(), "HEAD");
auto input = std::make_shared<std::stringstream>();
auto output = std::make_shared<std::stringstream>();
auto error = std::make_shared<std::stringstream>();
r->send(
request.get(),