Audacity 3.2.0
ServiceConfig.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 ServiceConfig.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11#include "ServiceConfig.h"
12#include "ExportUtils.h"
13#include "Languages.h"
14
15#include <cassert>
16#include <stdexcept>
17#include <string_view>
18
19#include <rapidjson/document.h>
20
21#include "CodeConversions.h"
22#include "Prefs.h"
23
25{
26namespace
27{
28#define MTM_CAMPAIGN \
29 "mtm_campaign=Audacity&mtm_source=Audacity-{version_number}&mtm_content={button_name}"
30
31StringSetting audioComApiEndpoint { L"/CloudServices/AudioCom/ApiEndpoint",
32 L"https://api.audio.com" };
33
34StringSetting audioComOAuthClientID { L"/CloudServices/AudioCom/OAuthClientID",
35 L"1741964426607541" };
36
38 L"/CloudServices/AudioCom/OAuthClientSecret",
39 L"shKqnY2sLTfRK7hztwzNEVxnmhJfOy1i"
40};
41
43 L"/CloudServices/AudioCom/OAuthRedirectURL",
44 L"https://audio.com/auth/sign-in/success"
45};
46
48 L"/CloudServices/AudioCom/OAuthLoginPage",
49 L"https://audio.com/audacity/link?clientId={auth_client_id}&" MTM_CAMPAIGN
50};
51
53 L"/CloudServices/AudioCom/FinishUploadPage",
54 L"https://audio.com/audacity/upload?audioId={audio_id}&token={auth_token}&clientId={auth_client_id}&" MTM_CAMPAIGN
55};
56
57StringSetting audioComFrontendUrl { L"/CloudServices/AudioCom/FrontendURL",
58 L"https://audio.com" };
59
61 L"/CloudServices/AudioCom/DownloadMimeType", L"audio/x-wav"
62};
63
64std::string Substitute(
65 std::string pattern,
66 std::initializer_list<std::pair<std::string_view, std::string_view>>
67 substitutions)
68{
69 for (auto& [key, value] : substitutions)
70 {
71 auto pos = pattern.find(key);
72
73 while (pos > 0 && pos != std::string::npos)
74 {
75 // There is no need to check that pos + key.size() is valid, there
76 // will be a zero terminator in the worst case.
77 if (pattern[pos - 1] == '{' && pattern[pos + key.size()] == '}')
78 pattern.replace(pos - 1, key.size() + 2, value);
79
80 pos = pattern.find(key, pos + 1);
81 }
82 }
83
84 return std::move(pattern);
85}
86
87std::string GetButtonName(AudiocomTrace trace)
88{
89 switch (trace)
90 {
92 return "ignore";
94 return "Share_Audio_Button";
96 return "Share_Audio_Menu";
98 return "Share_Audio_Export_Menu";
100 return "Share_Audio_Export_Extra_Menu";
102 return "Save_to_Cloud_Menu";
104 return "Save_Project_Save_to_Cloud_Menu";
106 return "Prefs_Panel";
108 return "Project_Opened_And_Upload_Resumed";
110 return "Update_Cloud_Audio_Preview_Menu";
112 return "Link_Audiocom_Account_Help_Menu";
114 return "Open_From_Cloud_Menu";
115 }
116
117 assert(false);
118 return {};
119}
120} // namespace
121
123{
132}
133
135{
136 return mApiEndpoint;
137}
138
140{
141 return Substitute(
143 { { "auth_client_id", GetOAuthClientID() },
144 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
145 { "button_name", GetButtonName(trace) } });
146}
147
149{
150 return mOAuthClientID;
151}
152
154{
155 return mOAuthClientSecret;
156}
157
159{
160 return mOAuthRedirectURL;
161}
162
163std::string ServiceConfig::GetAPIUrl(std::string_view apiURI) const
164{
165 return mApiEndpoint + std::string(apiURI);
166}
167
169 std::string_view audioID, std::string_view token, AudiocomTrace trace) const
170{
171 return Substitute(
173 { { "audio_id", audioID },
174 { "auth_token", token },
175 { "auth_client_id", mOAuthClientID },
176 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
177 { "button_name", GetButtonName(trace) } });
178}
179
181 std::string_view userSlug, std::string_view audioSlug,
182 AudiocomTrace trace) const
183{
184 return Substitute(
185 "{frontend_url}/{user_slug}/audio/{audio_slug}/edit?" MTM_CAMPAIGN,
186 { { "frontend_url", mFrontendURL },
187 { "user_slug", userSlug },
188 { "audio_slug", audioSlug },
189 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
190 { "button_name", GetButtonName(trace) } });
191}
192
193std::chrono::milliseconds ServiceConfig::GetProgressCallbackTimeout() const
194{
195 return std::chrono::seconds(3);
196}
197
198std::vector<std::string>
200{
201 if (preferLossless)
202 return { "audio/x-wavpack", "audio/x-flac", "audio/x-wav" };
203 else
204 return { "audio/mpeg" };
205}
206
207rapidjson::Document
208ServiceConfig::GetExportConfig(const std::string& mimeType) const
209{
210 if (mimeType == "audio/x-wavpack")
211 {
212 rapidjson::Document config;
213 config.SetObject();
214 config.AddMember("quality", rapidjson::Value(2), config.GetAllocator());
215 config.AddMember("bit_rate", rapidjson::Value(40), config.GetAllocator());
216 config.AddMember("bit_depth", 24, config.GetAllocator());
217 config.AddMember("hybrid_mode", false, config.GetAllocator());
218 return config;
219 }
220 else if (mimeType == "audio/x-flac")
221 {
222 rapidjson::Document config;
223 config.SetObject();
224 config.AddMember(
225 "bit_depth", rapidjson::Value(24), config.GetAllocator());
226 config.AddMember("level", rapidjson::Value(5), config.GetAllocator());
227 }
228 else if (mimeType == "audio/x-wav")
229 {
230 return {};
231 }
232 else if (mimeType == "audio/mpeg")
233 {
234 rapidjson::Document config;
235 config.SetObject();
236 config.AddMember("mode", rapidjson::Value("VBR"), config.GetAllocator());
237 config.AddMember("quality", rapidjson::Value(5), config.GetAllocator());
238 return config;
239 }
240
241 throw std::invalid_argument("unknown mime-type");
242}
243
245{
246 return mPreferredMimeType;
247}
248
250{
251 auto language = Languages::GetLang();
252
253 if (language.Contains(L"-") && language.Length() > 2)
254 return wxString::Format(
255 "%s;q=1.0, %s;q=0.7, *;q=0.5", language, language.Left(2))
256 .ToStdString();
257 else
258 return wxString::Format("%s;q=1.0, *;q=0.5", language).ToStdString();
259}
260
262{
263 return GetAPIUrl("/project");
264}
265
266std::string
267ServiceConfig::GetCreateSnapshotUrl(std::string_view projectId) const
268{
269 return Substitute(
270 "{api_url}/project/{project_id}/snapshot",
271 { { "api_url", mApiEndpoint }, { "project_id", projectId } });
272}
273
275 std::string_view projectId, std::string_view snapshotId) const
276{
277 return Substitute(
278 "{api_url}/project/{project_id}/snapshot/{snapshot_id}/sync",
279 { { "api_url", mApiEndpoint },
280 { "project_id", projectId },
281 { "snapshot_id", snapshotId } });
282}
283
285 int page, int pageSize, std::string_view searchTerm) const
286{
287 if (searchTerm.empty())
288 return Substitute(
289 "{api_url}/project?page={page}&per-page={page_size}",
290 { { "api_url", mApiEndpoint },
291 { "page", std::to_string(page) },
292 { "page_size", std::to_string(pageSize) }, });
293
294 return Substitute(
295 "{api_url}/project?page={page}&per-page={page_size}&q={search_term}",
296 { { "api_url", mApiEndpoint },
297 { "page", std::to_string(page) },
298 { "page_size", std::to_string(pageSize) },
299 { "search_term", searchTerm }, });
300}
301
302std::string ServiceConfig::GetProjectInfoUrl(std::string_view projectId) const
303{
304 return Substitute(
305 "{api_url}/project/{project_id}", {
306 { "api_url", mApiEndpoint },
307 { "project_id", projectId },
308 });
309}
310
312 std::string_view projectId, std::string_view snapshotId) const
313{
314 return Substitute(
315 "{api_url}/project/{project_id}/snapshot/{snapshot_id}?expand=blocks,file_url",
316 {
317 { "api_url", mApiEndpoint },
318 { "project_id", projectId },
319 { "snapshot_id", snapshotId },
320 });
321}
322
324 std::string_view projectId, std::string_view snapshotId) const
325{
326 return Substitute(
327 "{api_url}/project/{project_id}/snapshot/{snapshot_id}",
328 {
329 { "api_url", mApiEndpoint },
330 { "project_id", projectId },
331 { "snapshot_id", snapshotId },
332 });
333}
334
335std::string ServiceConfig::GetNetworkStatsUrl(std::string_view projectId) const
336{
337 return Substitute(
338 "{api_url}/project/{project_id}/network-stats",
339 {
340 { "api_url", mApiEndpoint },
341 { "project_id", projectId },
342 });
343}
344
346 std::string_view userId, std::string_view projectId,
347 AudiocomTrace trace) const
348{
349 return Substitute(
350 "{frontend_url}/{user_slug}/projects/{project_id}?" MTM_CAMPAIGN,
351 {
352 { "frontend_url", mFrontendURL },
353 { "user_slug", userId },
354 { "project_id", projectId },
355 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
356 { "button_name", GetButtonName(trace) },
357 });
358}
359
361 std::string_view userId, AudiocomTrace trace) const
362{
363 return Substitute(
364 "{frontend_url}/{user_slug}/projects?" MTM_CAMPAIGN,
365 {
366 { "frontend_url", mFrontendURL },
367 { "user_slug", userId },
368 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
369 { "button_name", GetButtonName(trace) },
370 });
371}
372
374{
375 static ServiceConfig config;
376 return config;
377}
378
379} // namespace audacity::cloud::audiocom
Declare functions to perform UTF-8 to std::wstring conversions.
AudiocomTrace
Definition: ExportUtils.h:27
@ LinkAudiocomAccountHelpMenu
@ UpdateCloudAudioPreviewMenu
@ SaveProjectSaveToCloudMenu
@ ShareAudioExportExtraMenu
@ ProjectOpenedAndUploadResumed
static const AudacityProject::AttachedObjects::RegisteredFactory key
#define MTM_CAMPAIGN
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
Specialization of Setting for strings.
Definition: Prefs.h:370
Configuration for the audio.com.
Definition: ServiceConfig.h:25
std::string GetOAuthLoginPage(AudiocomTrace) const
Page to open in browser to initiate OAuth.
std::string GetProjectsUrl(int page, int pageSize, std::string_view searchTerm) const
std::vector< std::string > GetPreferredAudioFormats(bool preferLossless=true) const
Preferred audio format.
std::string GetNetworkStatsUrl(std::string_view projectId) const
std::string GetAudioURL(std::string_view userSlug, std::string_view audioSlug, AudiocomTrace) const
Helper to construct the page URL for the authorised upload.
std::string GetProjectsPageUrl(std::string_view userId, AudiocomTrace) const
std::string GetOAuthRedirectURL() const
OAuth2 redirect URL. Only used to satisfy the protocol.
std::string GetFinishUploadPage(std::string_view audioID, std::string_view token, AudiocomTrace) const
Helper to construct the page URL for the anonymous upload last stage.
std::string GetCreateSnapshotUrl(std::string_view projectId) const
std::string GetOAuthClientSecret() const
OAuth2 client secret.
std::string GetAPIEndpoint() const
API endpoint.
rapidjson::Document GetExportConfig(const std::string &exporterName) const
Export configuration suitable for the mime type provided.
std::string GetSnapshotInfoUrl(std::string_view projectId, std::string_view snapshotId) const
std::chrono::milliseconds GetProgressCallbackTimeout() const
Timeout between progress callbacks.
std::string GetAPIUrl(std::string_view apiURI) const
Helper to construct the full URLs for the API.
std::string GetDeleteSnapshotUrl(std::string_view projectId, std::string_view snapshotId) const
std::string GetProjectInfoUrl(std::string_view projectId) const
std::string GetAcceptLanguageValue() const
Returns the preferred language.
std::string GetSnapshotSyncUrl(std::string_view projectId, std::string_view snapshotId) const
std::string GetProjectPageUrl(std::string_view userId, std::string_view projectId, AudiocomTrace) const
std::string GetOAuthClientID() const
OAuth2 client ID.
wxString GetLang()
Definition: Languages.cpp:395
constexpr size_t npos(-1)
std::string Substitute(std::string pattern, std::initializer_list< std::pair< std::string_view, std::string_view > > substitutions)
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
std::string ToUTF8(const std::wstring &wstr)