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
58 L"/CloudServices/AudioCom/AuthWithRedirectURL",
59 L"https://audio.com/auth/check-and-redirect"
60};
61
62StringSetting audioComFrontendURL { L"/CloudServices/AudioCom/FrontendURL",
63 L"https://audio.com" };
64
66 L"/CloudServices/AudioCom/DownloadMimeType", L"audio/x-wav"
67};
68
69std::string Substitute(
70 std::string pattern,
71 std::initializer_list<std::pair<std::string_view, std::string_view>>
72 substitutions)
73{
74 for (auto& [key, value] : substitutions)
75 {
76 auto pos = pattern.find(key);
77
78 while (pos > 0 && pos != std::string::npos)
79 {
80 // There is no need to check that pos + key.size() is valid, there
81 // will be a zero terminator in the worst case.
82 if (pattern[pos - 1] == '{' && pattern[pos + key.size()] == '}')
83 pattern.replace(pos - 1, key.size() + 2, value);
84
85 pos = pattern.find(key, pos + 1);
86 }
87 }
88
89 return std::move(pattern);
90}
91
92std::string GetButtonName(AudiocomTrace trace)
93{
94 switch (trace)
95 {
97 return "ignore";
99 return "Share_Audio_Button";
101 return "Share_Audio_Menu";
103 return "Share_Audio_Export_Menu";
105 return "Share_Audio_Export_Extra_Menu";
107 return "Save_to_Cloud_Menu";
109 return "Save_Project_Save_to_Cloud_Menu";
111 return "Prefs_Panel";
113 return "Project_Opened_And_Upload_Resumed";
115 return "Update_Cloud_Audio_Preview_Menu";
117 return "Link_Audiocom_Account_Help_Menu";
119 return "Open_From_Cloud_Menu";
120 }
121
122 assert(false);
123 return {};
124}
125} // namespace
126
128{
138}
139
141{
142 return mApiEndpoint;
143}
144
146{
147 return Substitute(
149 { { "auth_client_id", GetOAuthClientID() },
150 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
151 { "button_name", GetButtonName(trace) } });
152}
153
155{
156 return mOAuthClientID;
157}
158
160{
161 return mOAuthClientSecret;
162}
163
165{
166 return mOAuthRedirectURL;
167}
168
170{
172}
173
174std::string ServiceConfig::GetAPIUrl(std::string_view apiURI) const
175{
176 return mApiEndpoint + std::string(apiURI);
177}
178
180 std::string_view audioID, std::string_view token, AudiocomTrace trace) const
181{
182 return Substitute(
184 { { "audio_id", audioID },
185 { "auth_token", token },
186 { "auth_client_id", mOAuthClientID },
187 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
188 { "button_name", GetButtonName(trace) } });
189}
190
192 std::string_view userSlug, std::string_view audioSlug,
193 AudiocomTrace trace) const
194{
195 return Substitute(
196 "{frontend_url}/{user_slug}/audio/{audio_slug}/edit?" MTM_CAMPAIGN,
197 { { "frontend_url", mFrontendURL },
198 { "user_slug", userSlug },
199 { "audio_slug", audioSlug },
200 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
201 { "button_name", GetButtonName(trace) } });
202}
203
204std::chrono::milliseconds ServiceConfig::GetProgressCallbackTimeout() const
205{
206 return std::chrono::seconds(3);
207}
208
209std::vector<std::string>
211{
212 if (preferLossless)
213 return { "audio/x-wavpack", "audio/x-flac", "audio/x-wav" };
214 else
215 return { "audio/mpeg" };
216}
217
218rapidjson::Document
219ServiceConfig::GetExportConfig(const std::string& mimeType) const
220{
221 if (mimeType == "audio/x-wavpack")
222 {
223 rapidjson::Document config;
224 config.SetObject();
225 config.AddMember("quality", rapidjson::Value(2), config.GetAllocator());
226 config.AddMember("bit_rate", rapidjson::Value(40), config.GetAllocator());
227 config.AddMember("bit_depth", 24, config.GetAllocator());
228 config.AddMember("hybrid_mode", false, config.GetAllocator());
229 return config;
230 }
231 else if (mimeType == "audio/x-flac")
232 {
233 rapidjson::Document config;
234 config.SetObject();
235 config.AddMember(
236 "bit_depth", rapidjson::Value(24), config.GetAllocator());
237 config.AddMember("level", rapidjson::Value(5), config.GetAllocator());
238 }
239 else if (mimeType == "audio/x-wav")
240 {
241 return {};
242 }
243 else if (mimeType == "audio/mpeg")
244 {
245 rapidjson::Document config;
246 config.SetObject();
247 config.AddMember("mode", rapidjson::Value("VBR"), config.GetAllocator());
248 config.AddMember("quality", rapidjson::Value(5), config.GetAllocator());
249 return config;
250 }
251
252 throw std::invalid_argument("unknown mime-type");
253}
254
256{
257 return mPreferredMimeType;
258}
259
261{
262 auto language = Languages::GetLang();
263
264 if (language.Contains(L"-") && language.Length() > 2)
265 return wxString::Format(
266 "%s;q=1.0, %s;q=0.7, *;q=0.5", language, language.Left(2))
267 .ToStdString();
268 else
269 return wxString::Format("%s;q=1.0, *;q=0.5", language).ToStdString();
270}
271
273{
274 return GetAPIUrl("/project");
275}
276
277std::string
278ServiceConfig::GetCreateSnapshotUrl(std::string_view projectId) const
279{
280 return Substitute(
281 "{api_url}/project/{project_id}/snapshot",
282 { { "api_url", mApiEndpoint }, { "project_id", projectId } });
283}
284
286 std::string_view projectId, std::string_view snapshotId) const
287{
288 return Substitute(
289 "{api_url}/project/{project_id}/snapshot/{snapshot_id}/sync",
290 { { "api_url", mApiEndpoint },
291 { "project_id", projectId },
292 { "snapshot_id", snapshotId } });
293}
294
296 int page, int pageSize, std::string_view searchTerm) const
297{
298 if (searchTerm.empty())
299 return Substitute(
300 "{api_url}/project?page={page}&per-page={page_size}",
301 { { "api_url", mApiEndpoint },
302 { "page", std::to_string(page) },
303 { "page_size", std::to_string(pageSize) }, });
304
305 return Substitute(
306 "{api_url}/project?page={page}&per-page={page_size}&q={search_term}",
307 { { "api_url", mApiEndpoint },
308 { "page", std::to_string(page) },
309 { "page_size", std::to_string(pageSize) },
310 { "search_term", searchTerm }, });
311}
312
313std::string ServiceConfig::GetProjectInfoUrl(std::string_view projectId) const
314{
315 return Substitute(
316 "{api_url}/project/{project_id}", {
317 { "api_url", mApiEndpoint },
318 { "project_id", projectId },
319 });
320}
321
323 std::string_view projectId, std::string_view snapshotId) const
324{
325 return Substitute(
326 "{api_url}/project/{project_id}/snapshot/{snapshot_id}?expand=blocks,file_url",
327 {
328 { "api_url", mApiEndpoint },
329 { "project_id", projectId },
330 { "snapshot_id", snapshotId },
331 });
332}
333
335 std::string_view projectId, std::string_view snapshotId) const
336{
337 return Substitute(
338 "{api_url}/project/{project_id}/snapshot/{snapshot_id}",
339 {
340 { "api_url", mApiEndpoint },
341 { "project_id", projectId },
342 { "snapshot_id", snapshotId },
343 });
344}
345
346std::string ServiceConfig::GetNetworkStatsUrl(std::string_view projectId) const
347{
348 return Substitute(
349 "{api_url}/project/{project_id}/network-stats",
350 {
351 { "api_url", mApiEndpoint },
352 { "project_id", projectId },
353 });
354}
355
357 std::string_view userSlug, std::string_view projectSlug,
358 AudiocomTrace trace) const
359{
360 return Substitute(
361 "/{user_slug}/projects/{project_slug}&" MTM_CAMPAIGN,
362 {
363 { "user_slug", userSlug },
364 { "project_slug", projectSlug },
365 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
366 { "button_name", GetButtonName(trace) },
367 });
368}
369
371 std::string_view userSlug, AudiocomTrace trace) const
372{
373 return Substitute(
374 "/{user_slug}/projects?" MTM_CAMPAIGN,
375 {
376 { "user_slug", userSlug },
377 { "version_number", audacity::ToUTF8(AUDACITY_VERSION_STRING) },
378 { "button_name", GetButtonName(trace) },
379 });
380}
381
383{
384 static ServiceConfig config;
385 return config;
386}
387
388} // 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 GetOAuthRedirectURL() const
OAuth2 redirect URL. Only used to satisfy the protocol.
std::string GetProjectsPagePath(std::string_view userSlug, AudiocomTrace) const
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 GetProjectPagePath(std::string_view userSlug, std::string_view projectSlug, AudiocomTrace) 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 GetAuthWithRedirectURL() const
Audio.com authorization API to automatically login current user.
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 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)