Skip to content

Commit

Permalink
Add possibility to download files synchronously.
Browse files Browse the repository at this point in the history
GitOrigin-RevId: 2b633fcc06a8d8770a6fb31a0d0bd9dac971df9e
  • Loading branch information
levlam committed Mar 27, 2019
1 parent ad2690b commit db66ddf
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 13 deletions.
2 changes: 1 addition & 1 deletion example/uwp/app/MainPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private void Button_Click(object sender, RoutedEventArgs e)
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.DownloadFile(Int32.Parse(args[1]), 1, 0, 0), _handler);
_client.Send(new TdApi.DownloadFile(Int32.Parse(args[1]), 1, 0, 0, false), _handler);
}
else if (command.StartsWith("bench"))
{
Expand Down
6 changes: 4 additions & 2 deletions td/generate/scheme/td_api.tl
Original file line number Diff line number Diff line change
Expand Up @@ -3099,12 +3099,14 @@ resetAllNotificationSettings = Ok;
setPinnedChats chat_ids:vector<int53> = Ok;


//@description Asynchronously downloads a file from the cloud. updateFile will be used to notify about the download progress and successful completion of the download. Returns file state just after the download has been started
//@description Downloads a file from the cloud. Download progress and completion of the download will be notified through updateFile updates
//@file_id Identifier of the file to download
//@priority Priority of the download (1-32). The higher the priority, the earlier the file will be downloaded. If the priorities of two files are equal, then the last one for which downloadFile was called will be downloaded first
//@offset File will be downloaded starting from that offset in bytes first. Supposed to be used for streaming
//@limit Download will be automatically cancelled when at least limit bytes are downloaded starting from the specified offset; use 0 to download without limit
downloadFile file_id:int32 priority:int32 offset:int32 limit:int32 = File;
//@synchronous If false, this request returns file state just after the download has been started. If true, this request returns file state only after
//-the download has succeeded, has failed, has been cancelled or a new downloadFile request with different offset/limit parameters was sent
downloadFile file_id:int32 priority:int32 offset:int32 limit:int32 synchronous:Bool = File;

//@description Returns file downloaded prefix size from a given offset @file_id Identifier of the file @offset Offset from which downloaded prefix size should be calculated
getFileDownloadedPrefixSize file_id:int32 offset:int32 = Count;
Expand Down
Binary file modified td/generate/scheme/td_api.tlo
Binary file not shown.
63 changes: 57 additions & 6 deletions td/telegram/Td.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3915,9 +3915,11 @@ class Td::DownloadFileCallback : public FileManager::DownloadCallback {
}

void on_download_ok(FileId file_id) override {
send_closure(G()->td(), &Td::on_file_download_finished, file_id);
}

void on_download_error(FileId file_id, Status error) override {
send_closure(G()->td(), &Td::on_file_download_finished, file_id);
}
};

Expand Down Expand Up @@ -5734,18 +5736,67 @@ void Td::on_request(uint64 id, const td_api::downloadFile &request) {
if (!(1 <= priority && priority <= 32)) {
return send_error_raw(id, 5, "Download priority must be in [1;32] range");
}
if (request.offset_ < 0) {
auto offset = request.offset_;
if (offset < 0) {
return send_error_raw(id, 5, "Download offset must be non-negative");
}
file_manager_->download(FileId(request.file_id_, 0), download_file_callback_, priority, request.offset_,
request.limit_);
auto limit = request.limit_;
if (limit < 0) {
return send_error_raw(id, 5, "Download limit must be non-negative");
}

auto file = file_manager_->get_file_object(FileId(request.file_id_, 0), false);
if (file->id_ == 0) {
FileId file_id(request.file_id_, 0);
auto file_view = file_manager_->get_file_view(file_id);
if (file_view.empty()) {
return send_error_raw(id, 400, "Invalid file id");
}

send_closure(actor_id(this), &Td::send_result, id, std::move(file));
auto info_it = pending_file_downloads_.find(file_id);
DownloadInfo *info = info_it == pending_file_downloads_.end() ? nullptr : &info_it->second;
if (info != nullptr && (offset != info->offset || limit != info->limit)) {
// we can't have two pending requests with different offset and limit, so cancel all previous requests
for (auto request_id : info->request_ids) {
send_closure(actor_id(this), &Td::send_error, request_id,
Status::Error(200, "Cancelled by another downloadFile request"));
}
info->request_ids.clear();
}
if (request.synchronous_) {
if (info == nullptr) {
info = &pending_file_downloads_[file_id];
}
info->offset = offset;
info->limit = limit;
info->request_ids.push_back(id);
}
file_manager_->download(file_id, download_file_callback_, priority, offset, limit);
if (!request.synchronous_) {
send_closure(actor_id(this), &Td::send_result, id, file_manager_->get_file_object(file_id, false));
}
}

void Td::on_file_download_finished(FileId file_id) {
auto it = pending_file_downloads_.find(file_id);
if (it == pending_file_downloads_.end()) {
return;
}
for (auto id : it->second.request_ids) {
// there was send_closure to call this function
auto file_object = file_manager_->get_file_object(file_id, false);
CHECK(file_object != nullptr);
auto download_offset = file_object->local_->download_offset_;
auto downloaded_size = file_object->local_->downloaded_prefix_size_;
auto file_size = file_object->size_;
if (file_object->local_->is_downloading_completed_ ||
(download_offset <= it->second.offset && download_offset + downloaded_size >= it->second.offset &&
((file_size != 0 && download_offset + downloaded_size == file_size) ||
download_offset + downloaded_size - it->second.offset >= it->second.limit))) {
send_result(id, std::move(file_object));
} else {
send_error_impl(id, td_api::make_object<td_api::error>(400, "File download has failed or was cancelled"));
}
}
pending_file_downloads_.erase(it);
}

void Td::on_request(uint64 id, const td_api::cancelDownloadFile &request) {
Expand Down
10 changes: 10 additions & 0 deletions td/telegram/Td.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
#pragma once

#include "td/telegram/files/FileId.h"
#include "td/telegram/net/MtprotoHeader.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/StateManager.h"
Expand Down Expand Up @@ -275,6 +276,13 @@ class Td final : public NetQueryCallback {

TermsOfService pending_terms_of_service_;

struct DownloadInfo {
int32 offset = -1;
int32 limit = -1;
vector<uint64> request_ids;
};
std::unordered_map<FileId, DownloadInfo, FileIdHash> pending_file_downloads_;

vector<std::pair<uint64, td_api::object_ptr<td_api::Function>>> pending_preauthentication_requests_;

template <class T>
Expand Down Expand Up @@ -303,6 +311,8 @@ class Td final : public NetQueryCallback {

void clear_requests();

void on_file_download_finished(FileId file_id);

static bool is_internal_config_option(Slice name);

void on_config_option_updated(const string &name);
Expand Down
6 changes: 3 additions & 3 deletions td/telegram/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2371,7 +2371,7 @@ class CliClient final : public Actor {
send_request(td_api::make_object<td_api::getMapThumbnailFile>(
as_location(latitude, longitude), to_integer<int32>(zoom), to_integer<int32>(width),
to_integer<int32>(height), to_integer<int32>(scale), as_chat_id(chat_id)));
} else if (op == "df" || op == "DownloadFile" || op == "dff") {
} else if (op == "df" || op == "DownloadFile" || op == "dff" || op == "dfs") {
string file_id;
string priority;
string offset;
Expand All @@ -2386,8 +2386,8 @@ class CliClient final : public Actor {
int32 max_file_id = as_file_id(file_id);
int32 min_file_id = (op == "dff" ? 1 : max_file_id);
for (int32 i = min_file_id; i <= max_file_id; i++) {
send_request(td_api::make_object<td_api::downloadFile>(i, to_integer<int32>(priority),
to_integer<int32>(offset), to_integer<int32>(limit)));
send_request(td_api::make_object<td_api::downloadFile>(
i, to_integer<int32>(priority), to_integer<int32>(offset), to_integer<int32>(limit), op == "dfs"));
}
} else if (op == "cdf") {
send_request(td_api::make_object<td_api::cancelDownloadFile>(as_file_id(args), false));
Expand Down
7 changes: 7 additions & 0 deletions td/telegram/files/FileManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1900,6 +1900,13 @@ void FileManager::download(FileId file_id, std::shared_ptr<DownloadCallback> cal
node->set_download_limit(limit);
auto *file_info = get_file_id_info(file_id);
CHECK(new_priority == 0 || callback);
if (file_info->download_callback_ != nullptr && file_info->download_callback_.get() != callback.get()) {
// the callback will be destroyed soon and lost forever
// this would be an error and should never happen, unless we cancel previous download query
// in that case we send an error to the callback
CHECK(new_priority == 0);
file_info->download_callback_->on_download_error(file_id, Status::Error(200, "Cancelled"));
}
file_info->download_priority_ = narrow_cast<int8>(new_priority);
file_info->download_callback_ = std::move(callback);
// TODO: send current progress?
Expand Down
2 changes: 1 addition & 1 deletion test/tdclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ class CheckTestC : public Task {
if (text.substr(0, tag_.size()) == tag_) {
file_id_to_check_ = messageDocument->document_->document_->id_;
LOG(ERROR) << "GOT FILE " << to_string(messageDocument->document_->document_);
this->send_query(make_tl_object<td_api::downloadFile>(file_id_to_check_, 1, 0, 0),
this->send_query(make_tl_object<td_api::downloadFile>(file_id_to_check_, 1, 0, 0, false),
[](auto res) { check_td_error(res); });
}
}
Expand Down

0 comments on commit db66ddf

Please sign in to comment.