14#include <rapidjson/document.h>
16#include <wx/bmpbuttn.h>
18#include <wx/clipbrd.h>
20#include <wx/stattext.h>
21#include <wx/statline.h>
22#include <wx/textctrl.h>
23#include <wx/radiobut.h>
55#ifdef HAS_CUSTOM_URL_HANDLING
72 "%lld", std::chrono::system_clock::now().time_since_epoch().count()),
75 fileName.Mkdir(0700, wxPATH_MKDIR_FULL);
77 if (fileName.Exists())
79 if (!wxRemoveFile(fileName.GetFullPath()))
83 return fileName.GetFullPath();
88 XO(
"Anyone will be able to listen to this audio.");
92 "Only you and people you share a link with will be able to listen to this audio.");
122 mCancelled.store(
true, std::memory_order_release);
141 return mCancelled.load(std::memory_order_acquire);
151 mProgress.store(value, std::memory_order_release);
156 constexpr auto ProgressSteps = 1000ull;
172 parent, wxID_ANY,
XO(
"Share Audio"), wxDefaultPosition, { 480, 250 },
173 wxDEFAULT_DIALOG_STYLE)
175 , mInitialStatePanel(*
this)
176 , mServices(std::make_unique<Services>())
182 s.StartVerticalLay();
192 const auto size = GetSize();
195 SetMaxSize({
size.x, -1 });
197 mContinueAction = [
this]()
199 if (mInitialStatePanel.root->IsShown())
200 StartUploadProcess();
207 if (!IsEscapeKey(evt))
276 this,
XO(
"Are you sure you want to cancel?"),
XO(
"Cancel upload to Audio.com"),
277 wxYES_NO | wxICON_QUESTION | wxNO_DEFAULT | wxSTAY_ON_TOP);
279 const auto result = dlgMessage.ShowModal();
281 if (result != wxID_YES)
293 mServices->uploadPromise->DiscardResult();
295 EndModal(wxID_CANCEL);
308 return std::all_of(range.begin(), range.end(), [](
const WaveTrack *track){
309 return IsMono(*track) && track->GetPan() == 0;
318 const double t0 = 0.0;
319 const double t1 =
tracks.GetEndTime();
323 auto hasMimeType = [](
const auto&& mimeTypes,
const std::string& mimeType)
325 return std::find(mimeTypes.begin(), mimeTypes.end(), mimeType) != mimeTypes.end();
330 for(
const auto& preferredMimeType :
GetServiceConfig().GetPreferredAudioFormats())
334 auto pluginIt = std::find_if(registry.begin(), registry.end(), [&](
auto t)
336 auto [plugin, formatIndex] = t;
338 return hasMimeType(plugin->GetMimeTypes(formatIndex), preferredMimeType) &&
339 plugin->ParseConfig(formatIndex, config, parameters);
342 if(pluginIt == registry.end())
345 const auto [plugin, formatIndex] = *pluginIt;
347 const auto formatInfo = plugin->GetFormatInfo(formatIndex);
366 auto exportTask = builder.Build(
mProject);
368 auto f = exportTask.get_future();
373 if(f.wait_for(std::chrono::milliseconds(50)) != std::future_status::ready)
381 if(!success && wxFileExists(path))
425 [
this](
const auto& result)
432 if (result.result == UploadOperationCompleted::Result::Success)
435 assert(std::holds_alternative<UploadSuccessfulPayload>(result.payload));
439 std::get_if<UploadSuccessfulPayload>(&result.payload))
440 HandleUploadSucceeded(*payload);
442 HandleUploadSucceeded({});
446 result.result != UploadOperationCompleted::Result::Aborted)
450 std::get_if<UploadFailedPayload>(&result.payload))
451 HandleUploadFailed(*payload);
453 HandleUploadFailed({});
457 [
this](
auto current,
auto total)
460 [
this, current, total]()
462 UpdateProgress(current, total);
467void ShareAudioDialog::HandleUploadSucceeded(
470 EndModal(wxID_CLOSE);
476 EndModal(wxID_ABORT);
482 auto details = payload.
message;
485 details +=
" " + err.second;
487 message =
XO(
"Error: %s").Format(details);
492 "We are unable to upload this file. Please try again and make sure to link to your audio.com account before uploading.");
496 {},
XO(
"Upload error"),
503void ShareAudioDialog::HandleExportFailure()
505 EndModal(wxID_ABORT);
508 {},
XO(
"Export error"),
509 XO(
"We are unable to prepare this file for uploading."), {},
513void ShareAudioDialog::ResetProgress()
515 mStageStartTime = Clock::now();
516 mLastUIUpdateTime = mStageStartTime;
518 mProgressPanel.elapsedTime->SetLabel(
" 00:00:00");
519 mProgressPanel.remainingTime->SetLabel(
" 00:00:00");
520 mProgressPanel.progress->SetValue(0);
522 mLastProgressValue = 0;
524 mExportProgressUpdater.reset();
533 wxTimeSpan tsElapsed(0, 0, 0, time.count());
535 label->SetLabel(tsElapsed.Format(
wxT(
" %H:%M:%S")));
541void ShareAudioDialog::UpdateProgress(uint64_t current, uint64_t total)
543 using namespace std::chrono;
545 const auto now = Clock::now();
553 if (mLastProgressValue != current)
555 constexpr int scale = 10000;
557 mLastProgressValue =
static_cast<int>(current);
559 mProgressPanel.progress->SetRange(scale);
560 mProgressPanel.progress->SetValue((current * scale) / total);
562 if (current == total && mServices->uploadPromise)
564 mProgressPanel.timePanel->Hide();
565 mProgressPanel.title->SetLabel(
XO(
"Finalizing upload...").Translation());
569 const auto elapsedSinceUIUpdate = now - mLastUIUpdateTime;
571 constexpr auto uiUpdateTimeout = 500ms;
573 if (elapsedSinceUIUpdate < uiUpdateTimeout && current < total)
576 mLastUIUpdateTime = now;
578 const auto elapsed = duration_cast<milliseconds>(now - mStageStartTime);
582 const auto estimate = elapsed * total / current;
583 const auto remains = estimate - elapsed;
586 mProgressPanel.remainingTime,
587 std::chrono::duration_cast<std::chrono::milliseconds>(remains));
592 , mUserDataChangedSubscription(
629 wxEVT_BUTTON, [
this](
auto) { OnLinkButtonPressed(); });
647 trackTitle->SetName(
XO(
"Track Title").Translation());
648 trackTitle->SetFocus();
649 trackTitle->SetMaxLength(100);
655 "Your audio will be uploaded to our sharing service: %s,%%which requires a free account to use."));
658 L
"%s",
XO(
"audio.com"),
"https://audio.com");
688 auto rootParent = root->GetParent();
689 rootParent->Freeze();
691 auto layoutUpdater =
finally(
692 [rootParent = root->GetParent(),
this]()
696 rootParent->Layout();
700 rootParent->Refresh();
705 if (!oauthService.HasRefreshToken())
711 if (!oauthService.HasAccessToken())
712 oauthService.ValidateAuth({});
716 if (userService.GetUserSlug().empty())
722 const auto displayName = userService.GetDisplayName();
727 const auto avatarPath = userService.GetAvatarPath();
729 if (!avatarPath.empty())
730 avatar->SetBitmap(avatarPath);
734 oauthButton->SetLabel(
XXO(
"&Unlink Account").Translation());
736 parent.mIsAuthorised =
true;
738 anonInfoPanel->Hide();
739 authorizedInfoPanel->Show();
741 if (parent.mContinueButton !=
nullptr)
742 parent.mContinueButton->Enable(!trackTitle->GetValue().empty());
749 if (oauthService.HasAccessToken())
750 oauthService.UnlinkAccount();
756#ifdef HAS_CUSTOM_URL_HANDLING
768 parent.mIsAuthorised =
false;
770 name->SetLabel(
XO(
"Anonymous").Translation());
772 oauthButton->SetLabel(
XXO(
"&Link Account").Translation());
774 anonInfoPanel->Show();
775 authorizedInfoPanel->Hide();
777 if (parent.mContinueButton !=
nullptr)
778 parent.mContinueButton->Enable(
false);
783 wxString ret { trackTitle->GetValue() };
784 ret.Trim(
true).Trim(
false);
790 return !GetTrackTitle().empty();
835 wxFont font = elapsedTime->GetFont();
838 elapsedTime->SetFont(font);
839 remainingTime->SetFont(font);
Toolkit-neutral facade for basic user interface services.
Declare functions to perform UTF-8 to std::wstring conversions.
const TranslatableString name
XXO("&Cut/Copy/Paste Toolbar")
wxString FileExtension
File extension, not including any leading dot.
an object holding per-project preferred sample rate
declares abstract base class Track, TrackList, and iterators over TrackList
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Wrap wxMessageDialog so that caption IS translatable.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
const wxString & GetProjectName() const
static ExportPluginRegistry & Get()
std::vector< std::tuple< ExportOptionID, ExportValue > > Parameters
ExportTaskBuilder & SetPlugin(const ExportPlugin *plugin, int format=0) noexcept
ExportTaskBuilder & SetParameters(ExportProcessor::Parameters parameters) noexcept
ExportTaskBuilder & SetNumChannels(unsigned numChannels) noexcept
ExportTaskBuilder & SetSampleRate(double sampleRate) noexcept
ExportTaskBuilder & SetFileName(const wxFileName &filename)
ExportTaskBuilder & SetRange(double t0, double t1, bool selectedOnly=false) noexcept
static ProjectRate & Get(AudacityProject &project)
void SetBorder(int Border)
wxPanel * StartInvisiblePanel(int border=0)
wxTextCtrl * AddTextBox(const TranslatableString &Caption, const wxString &Value, const int nChars)
void StartVerticalLay(int iProp=1)
wxButton * AddButton(const TranslatableString &Text, int PositionFlags=wxALIGN_CENTRE, bool setDefault=false)
void StartWrapLay(int PositionFlags=wxEXPAND, int iProp=0)
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
wxWindow * AddWindow(wxWindow *pWindow, int PositionFlags=wxALIGN_CENTRE)
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
wxStaticText * AddVariableText(const TranslatableString &Str, bool bCenter=false, int PositionFlags=0, int wrapWidth=0)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
wxSizerItem * AddSpace(int width, int height, int prop=0)
wxBitmap & Bitmap(int iIndex)
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
auto Any() -> TrackIterRange< TrackType >
static TrackList & Get(AudacityProject &project)
Holds a msgid for the translation catalog; may also bind format arguments.
bool IsURLHandlingSupported() const noexcept
Returns true, if Audacity can handle custom URLs.
static URLSchemesRegistry & Get()
Retrieves the registry instance.
A Track that contains audio waveform data.
void PopSuppressDialogs()
void PushSuppressDialogs()
rapidjson::Document GetExportConfig(const std::string &exporterName) const
Export configuration suitable for the mime type provided.
void OnProgress(double value) override
std::atomic< bool > mCancelled
ShareAudioDialog & mParent
void SetResult(ExportResult result)
bool IsCancelled() const override
~ExportProgressUpdater() override
ExportProgressUpdater(ShareAudioDialog &parent)
void SetStatusString(const TranslatableString &str) override
ExportResult GetResult() const
bool IsStopped() const override
std::atomic< double > mProgress
std::unique_ptr< ExportProgressUpdater > mExportProgressUpdater
AudacityProject & mProject
void Populate(ShuttleGui &s)
void UpdateProgress(uint64_t current, uint64_t total)
wxButton * mContinueButton
void StartUploadProcess()
~ShareAudioDialog() override
struct cloud::audiocom::ShareAudioDialog::InitialStatePanel mInitialStatePanel
ShareAudioDialog(AudacityProject &project, wxWindow *parent=nullptr)
struct cloud::audiocom::ShareAudioDialog::ProgressPanel mProgressPanel
std::function< void()> mContinueAction
std::unique_ptr< Services > mServices
void HandleExportFailure()
A unique_ptr like class that holds a pointer to UploadOperation.
Service, responsible for uploading audio files to audio.com.
bool OpenInDefaultBrowser(const wxString &url)
Open an URL in default browser.
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
void Yield()
Dispatch waiting events, including actions enqueued by CallAfter.
void ExceptionWrappedCall(Callable callable)
double GetRate(const Track &track)
wxString ToWXString(const std::string &str)
wxString GenerateTempPath(FileExtension extension)
const auto publicDescriptionText
const auto unlistedDescriptionText
const auto unlistedLabelText
int CalculateChannels(const TrackList &trackList)
const auto publicLabelText
void SetTimeLabel(wxStaticText *label, std::chrono::milliseconds time)
StringSetting displayName
AuthorizationHandler & GetAuthorizationHandler()
wxString GetUploadTempPath()
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
OAuthService & GetOAuthService()
Returns the instance of the OAuthService.
UserService & GetUserService()
Options for variations of error dialogs; the default is for modal dialogs.
void OnLinkButtonPressed()
bool HasValidTitle() const
void PopulateInitialStatePanel(ShuttleGui &s)
wxString GetTrackTitle() const
void PopulateProgressPanel(ShuttleGui &s)
UploadOperationHandle uploadPromise
UploadService uploadService
This structure represents an upload error as returned by the server.
std::vector< AdditionalError > additionalErrors
This structure represents the payload associated with successful upload.
std::string audioUrl
URL to the uploaded audio.