Skip to content

Commit 6598aad

Browse files
Improve "Video upload info not found" (#1116)
* wip * fix merge conflict * wip * fix error handling * format * fix * fix
1 parent 7369e1f commit 6598aad

File tree

14 files changed

+214
-115
lines changed

14 files changed

+214
-115
lines changed

apps/desktop/src-tauri/src/api.rs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ use serde::{Deserialize, Serialize};
55
use serde_json::json;
66
use tauri::AppHandle;
77

8-
use crate::web_api::ManagerExt;
8+
use crate::web_api::{AuthedApiError, ManagerExt};
99

10-
pub async fn upload_multipart_initiate(app: &AppHandle, video_id: &str) -> Result<String, String> {
10+
pub async fn upload_multipart_initiate(
11+
app: &AppHandle,
12+
video_id: &str,
13+
) -> Result<String, AuthedApiError> {
1114
#[derive(Deserialize)]
1215
#[serde(rename_all = "camelCase")]
1316
pub struct Response {
@@ -32,14 +35,12 @@ pub async fn upload_multipart_initiate(app: &AppHandle, video_id: &str) -> Resul
3235
.text()
3336
.await
3437
.unwrap_or_else(|_| "<no response body>".to_string());
35-
return Err(format!(
36-
"api/upload_multipart_initiate/{status}: {error_body}"
37-
));
38+
return Err(format!("api/upload_multipart_initiate/{status}: {error_body}").into());
3839
}
3940

4041
resp.json::<Response>()
4142
.await
42-
.map_err(|err| format!("api/upload_multipart_initiate/response: {err}"))
43+
.map_err(|err| format!("api/upload_multipart_initiate/response: {err}").into())
4344
.map(|data| data.upload_id)
4445
}
4546

@@ -49,7 +50,7 @@ pub async fn upload_multipart_presign_part(
4950
upload_id: &str,
5051
part_number: u32,
5152
md5_sum: &str,
52-
) -> Result<String, String> {
53+
) -> Result<String, AuthedApiError> {
5354
#[derive(Deserialize)]
5455
#[serde(rename_all = "camelCase")]
5556
pub struct Response {
@@ -76,14 +77,12 @@ pub async fn upload_multipart_presign_part(
7677
.text()
7778
.await
7879
.unwrap_or_else(|_| "<no response body>".to_string());
79-
return Err(format!(
80-
"api/upload_multipart_presign_part/{status}: {error_body}"
81-
));
80+
return Err(format!("api/upload_multipart_presign_part/{status}: {error_body}").into());
8281
}
8382

8483
resp.json::<Response>()
8584
.await
86-
.map_err(|err| format!("api/upload_multipart_presign_part/response: {err}"))
85+
.map_err(|err| format!("api/upload_multipart_presign_part/response: {err}").into())
8786
.map(|data| data.presigned_url)
8887
}
8988

@@ -114,7 +113,7 @@ pub async fn upload_multipart_complete(
114113
upload_id: &str,
115114
parts: &[UploadedPart],
116115
meta: Option<S3VideoMeta>,
117-
) -> Result<Option<String>, String> {
116+
) -> Result<Option<String>, AuthedApiError> {
118117
#[derive(Serialize)]
119118
#[serde(rename_all = "camelCase")]
120119
pub struct MultipartCompleteRequest<'a> {
@@ -150,14 +149,12 @@ pub async fn upload_multipart_complete(
150149
.text()
151150
.await
152151
.unwrap_or_else(|_| "<no response body>".to_string());
153-
return Err(format!(
154-
"api/upload_multipart_complete/{status}: {error_body}"
155-
));
152+
return Err(format!("api/upload_multipart_complete/{status}: {error_body}").into());
156153
}
157154

158155
resp.json::<Response>()
159156
.await
160-
.map_err(|err| format!("api/upload_multipart_complete/response: {err}"))
157+
.map_err(|err| format!("api/upload_multipart_complete/response: {err}").into())
161158
.map(|data| data.location)
162159
}
163160

@@ -179,7 +176,10 @@ pub struct PresignedS3PutRequest {
179176
pub meta: Option<S3VideoMeta>,
180177
}
181178

182-
pub async fn upload_signed(app: &AppHandle, body: PresignedS3PutRequest) -> Result<String, String> {
179+
pub async fn upload_signed(
180+
app: &AppHandle,
181+
body: PresignedS3PutRequest,
182+
) -> Result<String, AuthedApiError> {
183183
#[derive(Deserialize)]
184184
struct Data {
185185
url: String,
@@ -204,12 +204,12 @@ pub async fn upload_signed(app: &AppHandle, body: PresignedS3PutRequest) -> Resu
204204
.text()
205205
.await
206206
.unwrap_or_else(|_| "<no response body>".to_string());
207-
return Err(format!("api/upload_signed/{status}: {error_body}"));
207+
return Err(format!("api/upload_signed/{status}: {error_body}").into());
208208
}
209209

210210
resp.json::<Response>()
211211
.await
212-
.map_err(|err| format!("api/upload_signed/response: {err}"))
212+
.map_err(|err| format!("api/upload_signed/response: {err}").into())
213213
.map(|data| data.presigned_put_data.url)
214214
}
215215

@@ -218,7 +218,7 @@ pub async fn desktop_video_progress(
218218
video_id: &str,
219219
uploaded: u64,
220220
total: u64,
221-
) -> Result<(), String> {
221+
) -> Result<(), AuthedApiError> {
222222
let resp = app
223223
.authed_api_request("/api/desktop/video/progress", |client, url| {
224224
client.post(url).json(&json!({
@@ -237,7 +237,7 @@ pub async fn desktop_video_progress(
237237
.text()
238238
.await
239239
.unwrap_or_else(|_| "<no response body>".to_string());
240-
return Err(format!("api/desktop_video_progress/{status}: {error_body}"));
240+
return Err(format!("api/desktop_video_progress/{status}: {error_body}").into());
241241
}
242242

243243
Ok(())

apps/desktop/src-tauri/src/auth.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,3 @@ impl AuthStore {
118118
store.save().map_err(|e| e.to_string())
119119
}
120120
}
121-
122-
#[derive(specta::Type, serde::Serialize, tauri_specta::Event, Debug, Clone, serde::Deserialize)]
123-
pub struct AuthenticationInvalid;

apps/desktop/src-tauri/src/deeplink_actions.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ impl DeepLinkAction {
138138
mode,
139139
};
140140

141-
crate::recording::start_recording(app.clone(), state, inputs).await
141+
crate::recording::start_recording(app.clone(), state, inputs)
142+
.await
143+
.map(|_| ())
142144
}
143145
DeepLinkAction::StopRecording => {
144146
crate::recording::stop_recording(app.clone(), app.state()).await

apps/desktop/src-tauri/src/hotkeys.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ async fn handle_hotkey(app: AppHandle, action: HotkeyAction) -> Result<(), Strin
146146
Ok(())
147147
}
148148
HotkeyAction::StopRecording => recording::stop_recording(app.clone(), app.state()).await,
149-
HotkeyAction::RestartRecording => {
150-
recording::restart_recording(app.clone(), app.state()).await
151-
}
149+
HotkeyAction::RestartRecording => recording::restart_recording(app.clone(), app.state())
150+
.await
151+
.map(|_| ()),
152152
HotkeyAction::OpenRecordingPicker => {
153153
let _ = RequestOpenRecordingPicker { target_mode: None }.emit(&app);
154154
Ok(())

apps/desktop/src-tauri/src/lib.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mod web_api;
2828
mod windows;
2929

3030
use audio::AppSounds;
31-
use auth::{AuthStore, AuthenticationInvalid, Plan};
31+
use auth::{AuthStore, Plan};
3232
use camera::CameraPreviewState;
3333
use cap_editor::{EditorInstance, EditorState};
3434
use cap_project::{
@@ -87,6 +87,7 @@ use tokio::sync::Mutex;
8787
use tokio::sync::{RwLock, oneshot};
8888
use tracing::{error, trace, warn};
8989
use upload::{create_or_get_video, upload_image, upload_video};
90+
use web_api::AuthedApiError;
9091
use web_api::ManagerExt as WebManagerExt;
9192
use windows::{CapWindowId, EditorWindowIds, ShowCapWindow, set_window_transparent};
9293

@@ -1084,7 +1085,7 @@ async fn upload_exported_video(
10841085

10851086
channel.send(UploadProgress { progress: 0.0 }).ok();
10861087

1087-
let s3_config = async {
1088+
let s3_config = match async {
10881089
let video_id = match mode {
10891090
UploadMode::Initial { pre_created_video } => {
10901091
if let Some(pre_created) = pre_created_video {
@@ -1094,7 +1095,7 @@ async fn upload_exported_video(
10941095
}
10951096
UploadMode::Reupload => {
10961097
let Some(sharing) = meta.sharing.clone() else {
1097-
return Err("No sharing metadata found".to_string());
1098+
return Err("No sharing metadata found".into());
10981099
};
10991100

11001101
Some(sharing.id)
@@ -1110,7 +1111,13 @@ async fn upload_exported_video(
11101111
)
11111112
.await
11121113
}
1113-
.await?;
1114+
.await
1115+
{
1116+
Ok(data) => data,
1117+
Err(AuthedApiError::InvalidAuthentication) => return Ok(UploadResult::NotAuthenticated),
1118+
Err(AuthedApiError::UpgradeRequired) => return Ok(UploadResult::UpgradeRequired),
1119+
Err(err) => return Err(err.to_string()),
1120+
};
11141121

11151122
let screenshot_path = meta.project_path.join("screenshots/display.jpg");
11161123
meta.upload = Some(UploadMeta::SinglePartUpload {
@@ -1154,17 +1161,20 @@ async fn upload_exported_video(
11541161
NotificationType::ShareableLinkCopied.send(&app);
11551162
Ok(UploadResult::Success(uploaded_video.link))
11561163
}
1164+
Err(AuthedApiError::UpgradeRequired) => Ok(UploadResult::UpgradeRequired),
11571165
Err(e) => {
11581166
error!("Failed to upload video: {e}");
11591167

11601168
NotificationType::UploadFailed.send(&app);
11611169

1162-
meta.upload = Some(UploadMeta::Failed { error: e.clone() });
1170+
meta.upload = Some(UploadMeta::Failed {
1171+
error: e.to_string(),
1172+
});
11631173
meta.save_for_project()
11641174
.map_err(|e| error!("Failed to save recording meta: {e}"))
11651175
.ok();
11661176

1167-
Err(e)
1177+
Err(e.to_string().into())
11681178
}
11691179
}
11701180
}
@@ -1597,16 +1607,10 @@ async fn check_upgraded_and_update(app: AppHandle) -> Result<bool, String> {
15971607
.await
15981608
.map_err(|e| {
15991609
println!("Failed to fetch plan: {e}");
1600-
format!("Failed to fetch plan: {e}")
1610+
e.to_string()
16011611
})?;
16021612

16031613
println!("Plan fetch response status: {}", response.status());
1604-
if response.status() == reqwest::StatusCode::UNAUTHORIZED {
1605-
println!("Unauthorized response, clearing auth store");
1606-
AuthStore::set(&app, None).map_err(|e| e.to_string())?;
1607-
return Ok(false);
1608-
}
1609-
16101614
let plan_data = response.json::<serde_json::Value>().await.map_err(|e| {
16111615
println!("Failed to parse plan response: {e}");
16121616
format!("Failed to parse plan response: {e}")
@@ -1998,7 +2002,6 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
19982002
RequestOpenSettings,
19992003
RequestScreenCapturePrewarm,
20002004
NewNotification,
2001-
AuthenticationInvalid,
20022005
audio_meter::AudioInputLevelChange,
20032006
captions::DownloadProgress,
20042007
recording::RecordingEvent,
@@ -2548,7 +2551,7 @@ async fn resume_uploads(app: AppHandle) -> Result<(), String> {
25482551
error!("Error completing resumed upload for video: {error}");
25492552

25502553
if let Ok(mut meta) = RecordingMeta::load_for_project(&recording_dir).map_err(|err| error!("Error loading project metadata: {err}")) {
2551-
meta.upload = Some(UploadMeta::Failed { error });
2554+
meta.upload = Some(UploadMeta::Failed { error: error.to_string() });
25522555
meta.save_for_project().map_err(|err| error!("Error saving project metadata: {err}")).ok();
25532556
}
25542557
})

apps/desktop/src-tauri/src/recording.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use tauri_plugin_dialog::{DialogExt, MessageDialogBuilder};
3737
use tauri_specta::Event;
3838
use tracing::*;
3939

40+
use crate::web_api::AuthedApiError;
4041
use crate::{
4142
App, CurrentRecordingChanged, MutableState, NewStudioRecordingAdded, RecordingState,
4243
RecordingStopped, VideoUploadInfo,
@@ -245,14 +246,21 @@ pub enum RecordingEvent {
245246
Failed { error: String },
246247
}
247248

249+
#[derive(Serialize, Type)]
250+
pub enum RecordingAction {
251+
Started,
252+
InvalidAuthentication,
253+
UpgradeRequired,
254+
}
255+
248256
#[tauri::command]
249257
#[specta::specta]
250258
#[tracing::instrument(name = "recording", skip_all)]
251259
pub async fn start_recording(
252260
app: AppHandle,
253261
state_mtx: MutableState<'_, App>,
254262
inputs: StartRecordingInputs,
255-
) -> Result<(), String> {
263+
) -> Result<RecordingAction, String> {
256264
if !matches!(state_mtx.read().await.recording_state, RecordingState::None) {
257265
return Err("Recording already in progress".to_string());
258266
}
@@ -294,7 +302,7 @@ pub async fn start_recording(
294302
match AuthStore::get(&app).ok().flatten() {
295303
Some(_) => {
296304
// Pre-create the video and get the shareable link
297-
let s3_config = create_or_get_video(
305+
let s3_config = match create_or_get_video(
298306
&app,
299307
false,
300308
None,
@@ -305,10 +313,19 @@ pub async fn start_recording(
305313
None,
306314
)
307315
.await
308-
.map_err(|err| {
309-
error!("Error creating instant mode video: {err}");
310-
err
311-
})?;
316+
{
317+
Ok(meta) => meta,
318+
Err(AuthedApiError::InvalidAuthentication) => {
319+
return Ok(RecordingAction::InvalidAuthentication);
320+
}
321+
Err(AuthedApiError::UpgradeRequired) => {
322+
return Ok(RecordingAction::UpgradeRequired);
323+
}
324+
Err(err) => {
325+
error!("Error creating instant mode video: {err}");
326+
return Err(err.to_string());
327+
}
328+
};
312329

313330
let link = app.make_app_url(format!("/s/{}", s3_config.id)).await;
314331
info!("Pre-created shareable link: {}", link);
@@ -618,7 +635,7 @@ pub async fn start_recording(
618635

619636
AppSounds::StartRecording.play();
620637

621-
Ok(())
638+
Ok(RecordingAction::Started)
622639
}
623640

624641
#[tauri::command]
@@ -663,7 +680,10 @@ pub async fn stop_recording(app: AppHandle, state: MutableState<'_, App>) -> Res
663680

664681
#[tauri::command]
665682
#[specta::specta]
666-
pub async fn restart_recording(app: AppHandle, state: MutableState<'_, App>) -> Result<(), String> {
683+
pub async fn restart_recording(
684+
app: AppHandle,
685+
state: MutableState<'_, App>,
686+
) -> Result<RecordingAction, String> {
667687
let Some(recording) = state.write().await.clear_current_recording() else {
668688
return Err("No recording in progress".to_string());
669689
};
@@ -877,7 +897,7 @@ async fn handle_recording_finish(
877897
.handle
878898
.await
879899
.map_err(|e| e.to_string())
880-
.and_then(|r| r)
900+
.and_then(|r| r.map_err(|v| v.to_string()))
881901
{
882902
Ok(()) => {
883903
info!(
@@ -935,7 +955,9 @@ async fn handle_recording_finish(
935955
error!("Error in upload_video: {error}");
936956

937957
if let Ok(mut meta) = RecordingMeta::load_for_project(&recording_dir) {
938-
meta.upload = Some(UploadMeta::Failed { error });
958+
meta.upload = Some(UploadMeta::Failed {
959+
error: error.to_string(),
960+
});
939961
meta.save_for_project()
940962
.map_err(|e| format!("Failed to save recording meta: {e}"))
941963
.ok();

0 commit comments

Comments
 (0)