From 07630feb8cd1a2237a1b7c5d3d6da3a3d20d684d Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 24 Jul 2021 07:19:22 +0300 Subject: [PATCH] Add td_api::getSuggestedFileName. --- td/generate/scheme/td_api.tl | 3 ++ td/telegram/Td.cpp | 8 +++++ td/telegram/Td.h | 2 ++ td/telegram/cli.cpp | 5 +++ td/telegram/files/FileLoaderUtils.cpp | 46 +++++++++++++++++++++++++++ td/telegram/files/FileLoaderUtils.h | 2 ++ td/telegram/files/FileManager.cpp | 12 +++++++ td/telegram/files/FileManager.h | 2 ++ 8 files changed, 80 insertions(+) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index f778a10d8..0cd0e57ca 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4636,6 +4636,9 @@ getFileDownloadedPrefixSize file_id:int32 offset:int32 = Count; //@description Stops the downloading of a file. If a file has already been downloaded, does nothing @file_id Identifier of a file to stop downloading @only_if_pending Pass true to stop downloading only if it hasn't been started, i.e. request hasn't been sent to server cancelDownloadFile file_id:int32 only_if_pending:Bool = Ok; +//@description Returns suggested name for saving a file in a given directory @file_id Identifier of the file @directory Directory in which the file is supposed to be saved +getSuggestedFileName file_id:int32 directory:string = Text; + //@description Asynchronously uploads a file to the cloud without sending it in a message. updateFile will be used to notify about upload progress and successful completion of the upload. The file will not have a persistent remote identifier until it will be sent in a message @file File to upload @file_type File type //@priority Priority of the upload (1-32). The higher the priority, the earlier the file will be uploaded. If the priorities of two files are equal, then the first one for which uploadFile was called will be uploaded first uploadFile file:InputFile file_type:FileType priority:int32 = File; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 39d79a06c..ca869f1c4 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -6668,6 +6668,14 @@ void Td::on_request(uint64 id, const td_api::cancelDownloadFile &request) { send_closure(actor_id(this), &Td::send_result, id, make_tl_object()); } +void Td::on_request(uint64 id, const td_api::getSuggestedFileName &request) { + Result r_file_name = file_manager_->get_suggested_file_name(FileId(request.file_id_, 0), request.directory_); + if (r_file_name.is_error()) { + return send_closure(actor_id(this), &Td::send_error, id, r_file_name.move_as_error()); + } + send_closure(actor_id(this), &Td::send_result, id, td_api::make_object(r_file_name.ok())); +} + void Td::on_request(uint64 id, td_api::uploadFile &request) { auto priority = request.priority_; if (!(1 <= priority && priority <= 32)) { diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 158568150..13102d562 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -874,6 +874,8 @@ class Td final : public NetQueryCallback { void on_request(uint64 id, const td_api::cancelDownloadFile &request); + void on_request(uint64 id, const td_api::getSuggestedFileName &request); + void on_request(uint64 id, td_api::uploadFile &request); void on_request(uint64 id, const td_api::cancelUploadFile &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 0c544c07f..6f5c4a675 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -2666,6 +2666,11 @@ class CliClient final : public Actor { } } else if (op == "cdf") { send_request(td_api::make_object(as_file_id(args), false)); + } else if (op == "gsfn") { + string file_id; + string directory_name; + get_args(args, file_id, directory_name); + send_request(td_api::make_object(as_file_id(file_id), directory_name)); } else if (op == "uf" || op == "ufs" || op == "ufse") { string file_path; int32 priority; diff --git a/td/telegram/files/FileLoaderUtils.cpp b/td/telegram/files/FileLoaderUtils.cpp index 8860d6fe0..c30c728b5 100644 --- a/td/telegram/files/FileLoaderUtils.cpp +++ b/td/telegram/files/FileLoaderUtils.cpp @@ -16,6 +16,7 @@ #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/PathView.h" +#include "td/utils/port/Clocks.h" #include "td/utils/port/FileFd.h" #include "td/utils/port/path.h" #include "td/utils/Random.h" @@ -137,6 +138,51 @@ Result search_file(CSlice dir, CSlice name, int64 expected_size) { return res; } +Result get_suggested_file_name(CSlice directory, Slice file_name) { + string cleaned_name = clean_filename(file_name.str()); + file_name = cleaned_name; + + if (directory.empty()) { + directory = "./"; + } + + auto dir_stat = stat(directory); + if (dir_stat.is_error() || !dir_stat.ok().is_dir_) { + return cleaned_name; + } + + PathView path_view(file_name); + auto stem = path_view.file_stem(); + auto ext = path_view.extension(); + + if (stem.empty()) { + return cleaned_name; + } + + Slice directory_slice = directory; + while (directory_slice.size() > 1 && (directory_slice.back() == '/' || directory_slice.back() == '\\')) { + directory_slice.remove_suffix(1); + } + + auto check_file_name = [directory_slice](Slice name) { + return stat(PSLICE() << directory_slice << TD_DIR_SLASH << name).is_error(); // in case of success, the name is bad + }; + + string checked_name = PSTRING() << stem << Ext{ext}; + if (check_file_name(checked_name)) { + return checked_name; + } + + for (int i = 1; i < 100; i++) { + checked_name = PSTRING() << stem << " (" << i << ")" << Ext{ext}; + if (check_file_name(checked_name)) { + return checked_name; + } + } + + return PSTRING() << stem << " - " << StringBuilder::FixedDouble(Clocks::system(), 3) << Ext{ext}; +} + Result save_file_bytes(FileType type, BufferSlice bytes, CSlice file_name) { auto r_old_path = search_file(get_files_dir(type), file_name, bytes.size()); if (r_old_path.is_ok()) { diff --git a/td/telegram/files/FileLoaderUtils.h b/td/telegram/files/FileLoaderUtils.h index 1a4fdda77..42332008e 100644 --- a/td/telegram/files/FileLoaderUtils.h +++ b/td/telegram/files/FileLoaderUtils.h @@ -28,6 +28,8 @@ Result create_from_temp(CSlice temp_path, CSlice dir, CSlice name) TD_WA Result search_file(CSlice dir, CSlice name, int64 expected_size) TD_WARN_UNUSED_RESULT; +Result get_suggested_file_name(CSlice dir, Slice file_name) TD_WARN_UNUSED_RESULT; + Result save_file_bytes(FileType type, BufferSlice bytes, CSlice file_name); Slice get_files_base_dir(FileType file_type); diff --git a/td/telegram/files/FileManager.cpp b/td/telegram/files/FileManager.cpp index 6624ef7d8..7bfb61cf3 100644 --- a/td/telegram/files/FileManager.cpp +++ b/td/telegram/files/FileManager.cpp @@ -3844,6 +3844,18 @@ FullRemoteFileLocation *FileManager::get_remote(int32 key) { return &remote_location_info_.get(key).remote_; } +Result FileManager::get_suggested_file_name(FileId file_id, const string &directory) { + if (!file_id.is_valid()) { + return Status::Error(400, "Invalid file identifier"); + } + auto node = get_sync_file_node(file_id); + if (!node) { + return Status::Error(400, "Wrong file identifier"); + } + + return ::td::get_suggested_file_name(directory, PathView(node->suggested_path()).file_name()); +} + void FileManager::hangup() { file_db_.reset(); file_generate_manager_.reset(); diff --git a/td/telegram/files/FileManager.h b/td/telegram/files/FileManager.h index 5d16b5ad3..69610b44b 100644 --- a/td/telegram/files/FileManager.h +++ b/td/telegram/files/FileManager.h @@ -452,6 +452,8 @@ class FileManager final : public FileLoadManager::Callback { void delete_file_reference(FileId file_id, std::string file_reference); void get_content(FileId file_id, Promise promise); + Result get_suggested_file_name(FileId file_id, const string &directory); + void read_file_part(FileId file_id, int32 offset, int32 count, int left_tries, Promise> promise);