48 static std::mutex mutex;
52std::vector<std::shared_ptr<audacity::network_manager::IResponse>>&
55 static std::vector<std::shared_ptr<audacity::network_manager::IResponse>>
69 requests.begin(), requests.end(),
70 [request](
const auto& r) { return r.get() == request; }),
88 auto request =
Request(std::move(url));
100 response->setRequestFinishedCallback(
101 [dataCallback = std::move(dataCallback)](
auto response)
104 [dataCallback = std::move(dataCallback), response]
119 std::string projectId,
128 if (result.Code != SyncResultCode::Success)
130 callback(sync::ProjectInfo {}, std::move(result));
144 callback(std::move(*projectInfo), std::move(result));
150 std::string projectId, std::string snapshotId,
159 if (result.Code != SyncResultCode::Success)
161 callback({}, std::move(result));
175 callback(std::move(*snapshotInfo), std::move(result));
181 std::string projectId, std::string snapshotId,
189 oAuthService, serviceConfig, projectId,
190 [callback = std::move(callback), projectId,
191 snapshotId = std::move(snapshotId), &oAuthService,
194 if (result.
Code != SyncResultCode::Success)
196 callback({}, {}, std::move(result));
200 const auto id = !snapshotId.empty() ?
210 { SyncResultCode::Success });
215 oAuthService, serviceConfig, projectId,
id,
216 [callback = std::move(callback),
217 projectInfo = std::move(projectInfo)](
221 std::move(projectInfo), std::move(snapshotInfo),
229 auto connection = sqlite::Connection::Open(path, sqlite::OpenMode::ReadOnly);
234 if (!connection->CheckTableExists(
"autosave"))
238 connection->CreateStatement(
"SELECT COUNT(1) FROM autosave");
243 auto result = statement->Prepare().Run();
248 for (
const auto& row : result)
250 if (row.GetOr(0, 0) > 0)
260 sqlite::Connection::Open(path, sqlite::OpenMode::ReadWrite);
265 if (!connection->CheckTableExists(
"autosave"))
268 auto statement = connection->CreateStatement(
"DELETE FROM autosave");
273 auto result = statement->Prepare().Run();
275 return result.IsOk();
288 std::string_view searchString)
292 auto promise = std::make_shared<GetProjectsPromise>();
298 Request(serviceConfig.GetProjectsUrl(page, pageSize, searchString));
309 context->OnCancelled(response);
311 response->setRequestFinishedCallback(
312 [promise, response](
auto)
316 if (responseResult.Code != SyncResultCode::Success)
318 promise->set_value(responseResult);
329 std::move(responseResult.Content) });
333 promise->set_value(std::move(*projects));
336 return promise->get_future();
340 std::string projectId, std::string snapshotId,
SyncMode mode,
348 if (mSyncInProcess.exchange(
true))
350 CompleteSync({ sync::ProjectSyncResult::StatusCode::Blocked, {}, {} });
351 return mSyncPromise.get_future();
354 if (projectId.empty())
356 FailSync({ SyncResultCode::InternalClientError,
"Empty projectId" });
357 return mSyncPromise.get_future();
361 mProgressCallback = [](
auto...) {
return true; };
363 mProgressCallback = std::move(callback);
371 if (result.
Code != SyncResultCode::Success)
372 FailSync(std::move(result));
374 SyncCloudSnapshot(projectInfo, snapshotInfo, mode);
377 return mSyncPromise.get_future();
389 if (mSyncInProcess.exchange(
true))
391 CompleteSync({ sync::ProjectSyncResult::StatusCode::Blocked });
392 return mSyncPromise.get_future();
396 mProgressCallback = [](
auto...) {
return true; };
398 mProgressCallback = std::move(callback);
402 auto projectInfo = cloudDatabase.GetProjectDataForPath(path);
408 return mSyncPromise.get_future();
413 [projectInfo = *projectInfo, &
project, path,
414 mode = forceSync ? SyncMode::ForceOverwrite : SyncMode::Normal,
417 if (result.Code != SyncResultCode::Success)
419 FailSync(std::move(result));
426 CompleteSync({ sync::ProjectSyncResult::StatusCode::Succeeded });
430 const auto snapshotId = remoteInfo.HeadSnapshot.Synced > 0 ?
431 remoteInfo.HeadSnapshot.Id :
432 remoteInfo.LastSyncedSnapshotId;
439 if (result.Code != SyncResultCode::Success)
440 FailSync(std::move(result));
442 SyncCloudSnapshot(remoteInfo, snapshotInfo, mode);
446 return mSyncPromise.get_future();
449bool CloudSyncService::IsCloudProject(
const std::string& path)
452 auto projectInfo = cloudDatabase.GetProjectDataForPath(path);
454 return projectInfo.has_value();
458CloudSyncService::GetProjectState(
const std::string& projectId)
462 auto projectInfo = cloudDatabase.GetProjectData(projectId);
465 return ProjectState::NotAvaliable;
467 if (!wxFileExists(
ToWXString(projectInfo->LocalPath)))
468 return ProjectState::NotAvaliable;
470 if (projectInfo->SyncStatus == sync::DBProjectData::SyncStatusSynced)
471 return ProjectState::FullySynced;
473 return ProjectState::PendingSync;
477CloudSyncService::GetHeadSnapshotID(std::string_view projectId)
481 auto promise = std::make_shared<GetHeadSnapshotIDPromise>();
487 if (result.
Code != SyncResultCode::Success)
490 sync::GetHeadSnapshotIDResult { std::move(result) });
496 return promise->get_future();
503 std::move(responseResult),
507void CloudSyncService::CompleteSync(std::string path)
510 sync::ProjectSyncResult::StatusCode::Succeeded, {}, std::move(path) });
517 result.
Stats = mRemoteSnapshot->GetTransferStats();
518 ReportUploadStats(mRemoteSnapshot->GetProjectId(), result.
Stats);
521 mSyncPromise.set_value(std::move(result));
522 mRemoteSnapshot.reset();
523 mSyncInProcess.store(
false);
526void CloudSyncService::SyncCloudSnapshot(
531 auto localProjectInfo =
534 const auto createNew = !localProjectInfo || mode == SyncMode::ForceNew;
536 const auto wxPath = createNew ?
544 const auto fileExists = wxFileExists(wxPath);
548 if (snapshotInfo.
Id.empty())
550 FailSync({ SyncResultCode::SyncImpossible });
562 assert(localProjectInfo.has_value());
563 assert(mode != SyncMode::ForceNew);
570 localProjectInfo->SnapshotId == snapshotInfo.
Id &&
571 localProjectInfo->SyncStatus != sync::DBProjectData::SyncStatusDownloading)
574 { sync::ProjectSyncResult::StatusCode::Succeeded, {}, utf8Path });
579 mode == SyncMode::Normal &&
580 localProjectInfo->SyncStatus == sync::DBProjectData::SyncStatusUploading)
587 { sync::ProjectSyncResult::StatusCode::Succeeded, {}, utf8Path });
593 if (mode == SyncMode::Normal)
603 if (snapshotInfo.
Id.empty())
605 FailSync({ SyncResultCode::SyncImpossible });
610 projectInfo, snapshotInfo, utf8Path,
611 [
this, createNew, path = utf8Path, projectId = projectInfo.
Id,
614 UpdateDowloadProgress(
615 (state.ProjectDownloaded + state.BlocksDownloaded) /
616 (state.BlocksTotal + 1.0));
618 if (state.IsComplete())
620 const bool success = state.Result.Code == SyncResultCode::Success;
622 CompleteSync({ success ?
623 sync::ProjectSyncResult::StatusCode::Succeeded :
624 sync::ProjectSyncResult::StatusCode::Failed,
625 std::move(state.Result), std::move(path) });
628 mode == SyncMode::ForceNew);
631void CloudSyncService::UpdateDowloadProgress(
double downloadProgress)
633 mDownloadProgress.store(downloadProgress);
635 if (mProgressUpdateQueued.exchange(
true))
641 auto remoteSnapshot = mRemoteSnapshot;
643 if (remoteSnapshot && !mProgressCallback(mDownloadProgress.load()))
644 remoteSnapshot->Cancel();
646 mProgressUpdateQueued.store(
false);
650void CloudSyncService::ReportUploadStats(
658 networkStats.
Mixes = 0;
675 response->setRequestFinishedCallback([response](
auto) {});
Toolkit-neutral facade for basic user interface services.
#define ASSERT_MAIN_THREAD()
Declare functions to perform UTF-8 to std::wstring conversions.
Declare an interface for HTTP response.
Declare a class for performing HTTP requests.
Declare a class for constructing HTTP requests.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Makes a temporary project that doesn't display on the screen.
std::optional< TentativeConnection > LoadProject(const FilePath &fileName, bool ignoreAutosave)
static ProjectFileIO & Get(AudacityProject &project)
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined */
std::future< sync::GetProjectsResult > GetProjectsFuture
std::future< sync::GetHeadSnapshotIDResult > GetHeadSnapshotIDFuture
std::future< sync::ProjectSyncResult > SyncFuture
Service responsible for OAuth authentication against the audio.com service.
bool HasAccessToken() const
Indicates, that service has a valid access token, i. e. that the user is authorized.
Configuration for the audio.com.
std::string GetSnapshotInfoUrl(std::string_view projectId, std::string_view snapshotId) const
std::string GetProjectInfoUrl(std::string_view projectId) const
Interface, that provides access to the data from the HTTP response.
static NetworkManager & GetInstance()
ResponsePtr doGet(const Request &request)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
FILES_API wxString MkDir(const wxString &Str)
void Sync(const wxString &string)
FrameStatistics & GetInstance() noexcept
std::vector< std::shared_ptr< audacity::network_manager::IResponse > > & GetPendingRequests()
void PerformProjectGetRequest(OAuthService &oAuthService, std::string url, std::function< void(ResponseResult)> dataCallback)
void RemovePendingRequest(audacity::network_manager::IResponse *request)
std::mutex & GetResponsesMutex()
bool DropAutosave(const std::string &path)
bool HasAutosave(const std::string &path)
void GetProjectInfo(OAuthService &oAuthService, const ServiceConfig &serviceConfig, std::string projectId, std::function< void(sync::ProjectInfo, ResponseResult)> callback)
void GetSnapshotInfo(OAuthService &oAuthService, const ServiceConfig &serviceConfig, std::string projectId, std::string snapshotId, std::function< void(sync::ProjectInfo, sync::SnapshotInfo, ResponseResult result)> callback)
std::optional< ProjectInfo > DeserializeProjectInfo(const std::string &data)
wxString MakeSafeProjectPath(const wxString &rootDir, const wxString &projectName)
std::string Serialize(const ProjectForm &form)
std::optional< PaginatedProjectsResponse > DeserializePaginatedProjectsResponse(const std::string &data)
std::optional< SnapshotInfo > DeserializeSnapshotInfo(const std::string &data)
std::function< bool(double)> ProgressCallback
StringSetting CloudProjectsSavePath
void SetCommonHeaders(Request &request)
OAuthService & GetOAuthService()
Returns the instance of the OAuthService.
ResponseResult GetResponseResult(IResponse &response, bool readBody)
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
std::shared_ptr< CancellationContext > CancellationContextPtr
const std::string ApplicationJson
std::string ToUTF8(const std::wstring &wstr)
wxString ToWXString(const std::string &str)
int64_t ProjectFilesTransferred
int64_t BlocksTransferred
SnapshotInfo HeadSnapshot
std::string LastSyncedSnapshotId