Audacity 3.2.0
DataUploader.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 DataUploader.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11
12#include "DataUploader.h"
13
14#include <variant>
15
16#include <wx/file.h>
17
18#include "CodeConversions.h"
19
20#include "IResponse.h"
21#include "NetworkManager.h"
22#include "Request.h"
23
24#include "RequestPayload.h"
25
26#include "BasicUI.h"
27
28using namespace audacity::network_manager;
29
31{
32constexpr int RetriesCount { 3 };
33
34using UploadData = std::variant<std::vector<uint8_t>, std::string>;
35
37 std::enable_shared_from_this<DataUploader::UploadOperation>
38{
41 std::function<void(ResponseResult)> Callback;
42 std::function<void(double)> ProgressCallback;
43
44 std::string MimeType;
46
49
50 std::atomic<bool> UploadFailed { false };
51
53 DataUploader& uploader, CancellationContextPtr cancellationContex,
54 const UploadUrls& target, UploadData data, std::string mimeType,
55 std::function<void(ResponseResult)> callback,
56 std::function<void(double)> progressCallback)
57 : Uploader { uploader }
58 , Target { target }
59 , Callback { std::move(callback) }
60 , ProgressCallback { std::move(progressCallback) }
61 , MimeType { std::move(mimeType) }
62 , Data { std::move(data) }
63 , CancelContext { std::move(cancellationContex) }
64 {
65 }
66
67 void PerformUpload(int retriesLeft)
68 {
69 Request request { Target.UploadUrl };
70 request.setHeader(common_headers::ContentType, MimeType);
71
72 ResponsePtr networkResponse;
73
74 if (std::holds_alternative<std::vector<uint8_t>>(Data))
75 {
76 auto data = *std::get_if<std::vector<uint8_t>>(&Data);
77
78 networkResponse = NetworkManager::GetInstance().doPut(
79 request, data.data(), data.size());
80 }
81 else
82 {
83 auto filePath = *std::get_if<std::string>(&Data);
84
85 networkResponse = NetworkManager::GetInstance().doPut(
86 request, CreateRequestPayloadStream(filePath));
87 }
88
89 CancelContext->OnCancelled(networkResponse);
90
91 networkResponse->setRequestFinishedCallback(
92 [this, retriesLeft, networkResponse, operation = weak_from_this()](auto)
93 {
94 auto strongThis = operation.lock();
95 if (!strongThis)
96 return;
97
98 CurrentResult = GetResponseResult(*networkResponse, false);
99
102 else if (
104 retriesLeft > 0)
105 PerformUpload(retriesLeft - 1);
106 else
108 });
109
110 networkResponse->setUploadProgressCallback(
111 [this, operation = weak_from_this()](
112 int64_t current, int64_t total)
113 {
114 auto strongThis = operation.lock();
115 if (!strongThis)
116 return;
117
118 if (total <= 0)
119 {
120 total = 1;
121 current = 0;
122 }
123
124 ProgressCallback(static_cast<double>(current) / total);
125 });
126 }
127
128 void ConfirmUpload(int retriesLeft)
129 {
130 Data = {};
131 Request request { Target.SuccessUrl };
132
133 auto networkResponse =
134 NetworkManager::GetInstance().doPost(request, nullptr, 0);
135 CancelContext->OnCancelled(networkResponse);
136
137 networkResponse->setRequestFinishedCallback(
138 [this, retriesLeft, networkResponse, operation = weak_from_this()](auto)
139 {
140 auto strongThis = operation.lock();
141 if (!strongThis)
142 return;
143
144 CurrentResult = GetResponseResult(*networkResponse, false);
145
147 {
149 CleanUp();
150 }
151 else if (
153 retriesLeft > 0)
154 {
155 ConfirmUpload(retriesLeft - 1);
156 }
157 else
158 {
160 }
161 });
162 }
163
164 void FailUpload(int retriesLeft)
165 {
166 if (!UploadFailed.exchange(true))
167 {
168 Data = {};
170 }
171
172 Request request { Target.FailUrl };
173
174 auto networkResponse =
175 NetworkManager::GetInstance().doPost(request, nullptr, 0);
176 CancelContext->OnCancelled(networkResponse);
177
178 networkResponse->setRequestFinishedCallback(
179 [this, retriesLeft, networkResponse, operation = weak_from_this()](auto)
180 {
181 auto strongThis = operation.lock();
182 if (!strongThis)
183 return;
184
185 const auto result = GetResponseResult(*networkResponse, false);
186
187 if (
188 result.Code == SyncResultCode::ConnectionFailed &&
189 retriesLeft > 0)
190 FailUpload(retriesLeft - 1);
191 else
192 CleanUp();
193
194 // Ignore other errors, server will collect garbage
195 // and delete the file eventually
196 });
197 }
198
199 void CleanUp()
200 {
201 BasicUI::CallAfter([this]() { Uploader.RemoveResponse(*this); });
202 }
203};
204
206{
207}
208
210{
211 static DataUploader instance;
212 return instance;
213}
214
216 CancellationContextPtr cancellationContex, const ServiceConfig&,
217 const UploadUrls& target, std::vector<uint8_t> data,
218 std::function<void(ResponseResult)> callback,
219 std::function<void(double)> progressCallback)
220{
221 if (!callback)
222 callback = [](auto...) {};
223
224 if (!progressCallback)
225 progressCallback = [](auto...) { return true; };
226
227 if (!cancellationContex)
228 cancellationContex = concurrency::CancellationContext::Create();
229
230 auto lock = std::lock_guard { mResponseMutex };
231
232 mResponses.emplace_back(std::make_unique<UploadOperation>(
233 *this, cancellationContex, target, std::move(data),
235 std::move(callback), std::move(progressCallback)));
236
237 mResponses.back()->PerformUpload(RetriesCount);
238}
239
241 CancellationContextPtr cancellationContex, const ServiceConfig& config,
242 const UploadUrls& target, std::string filePath,
243 std::function<void(ResponseResult)> callback,
244 std::function<void(double)> progressCallback)
245{
246 if (!callback)
247 callback = [](auto...) {};
248
249 if (!progressCallback)
250 progressCallback = [](auto...) { return true; };
251
252 if (!cancellationContex)
253 cancellationContex = concurrency::CancellationContext::Create();
254
255 if (!wxFileExists(audacity::ToWXString(filePath)))
256 {
257 if (callback)
258 callback(ResponseResult {
260 audacity::ToUTF8(XO("File not found").Translation()) });
261
262 return;
263 }
264
265 auto lock = std::lock_guard { mResponseMutex };
266
267 mResponses.emplace_back(std::make_shared<UploadOperation>(
268 *this, cancellationContex, target, std::move(filePath),
270 std::move(callback), std::move(progressCallback)));
271
272 mResponses.back()->PerformUpload(RetriesCount);
273}
274
276{
277 auto lock = std::lock_guard { mResponseMutex };
278
279 mResponses.erase(
280 std::remove_if(
281 mResponses.begin(), mResponses.end(),
282 [&response](const auto& item) { return item.get() == &response; }),
283 mResponses.end());
284}
285} // namespace audacity::cloud::audiocom::sync
Toolkit-neutral facade for basic user interface services.
Declare functions to perform UTF-8 to std::wstring conversions.
XO("Cut/Copy/Paste")
Declare an interface for HTTP response.
Declare a class for performing HTTP requests.
Declare a class for constructing HTTP requests.
Declare a class for constructing HTTP requests.
Configuration for the audio.com.
Definition: ServiceConfig.h:25
void Upload(CancellationContextPtr cancellationContex, const ServiceConfig &config, const UploadUrls &target, std::vector< uint8_t > data, std::function< void(ResponseResult)> callback, std::function< void(double)> progressCallback={})
void RemoveResponse(UploadOperation &response)
ResponsePtr doPost(const Request &request, const void *data, size_t size)
ResponsePtr doPut(const Request &request, const void *data, size_t size)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214
std::variant< std::vector< uint8_t >, std::string > UploadData
std::function< bool(double)> ProgressCallback
ResponseResult GetResponseResult(IResponse &response, bool readBody)
std::shared_ptr< CancellationContext > CancellationContextPtr
std::shared_ptr< IResponse > ResponsePtr
RequestPayloadStreamPtr CreateRequestPayloadStream(const void *data, int64_t size, bool copyData)
std::string ToUTF8(const std::wstring &wstr)
wxString ToWXString(const std::string &str)
STL namespace.
UploadOperation(DataUploader &uploader, CancellationContextPtr cancellationContex, const UploadUrls &target, UploadData data, std::string mimeType, std::function< void(ResponseResult)> callback, std::function< void(double)> progressCallback)