1
1
// credit @filleduchaos
2
2
3
- use crate :: api:: { PresignedS3PutRequest , PresignedS3PutRequestMethod , S3VideoMeta , UploadedPart } ;
4
- use crate :: web_api:: ManagerExt ;
5
- use crate :: { UploadProgress , VideoUploadInfo , api} ;
3
+ use crate :: {
4
+ UploadProgress , VideoUploadInfo , api,
5
+ api:: { PresignedS3PutRequest , PresignedS3PutRequestMethod , S3VideoMeta , UploadedPart } ,
6
+ web_api:: ManagerExt ,
7
+ } ;
6
8
use async_stream:: { stream, try_stream} ;
7
- use axum:: body :: Body ;
9
+ use axum:: http :: { self , Uri } ;
8
10
use bytes:: Bytes ;
9
- use cap_project:: { RecordingMeta , RecordingMetaInner , UploadState } ;
11
+ use cap_project:: { RecordingMeta , UploadState } ;
10
12
use cap_utils:: spawn_actor;
11
13
use ffmpeg:: ffi:: AV_TIME_BASE ;
12
14
use flume:: Receiver ;
13
15
use futures:: { Stream , StreamExt , TryStreamExt , stream} ;
14
- use image:: ImageReader ;
15
- use image:: codecs:: jpeg:: JpegEncoder ;
16
+ use image:: { ImageReader , codecs:: jpeg:: JpegEncoder } ;
16
17
use reqwest:: StatusCode ;
17
- use reqwest:: header:: CONTENT_LENGTH ;
18
18
use serde:: { Deserialize , Serialize } ;
19
- use serde_json:: json;
20
19
use specta:: Type ;
21
- use std:: error:: Error ;
22
- use std:: io;
23
- use std:: path:: Path ;
24
- use std:: pin:: pin;
25
20
use std:: {
26
- path:: PathBuf ,
21
+ io,
22
+ path:: { Path , PathBuf } ,
23
+ pin:: pin,
24
+ str:: FromStr ,
27
25
time:: { Duration , Instant } ,
28
26
} ;
29
27
use tauri:: { AppHandle , ipc:: Channel } ;
30
28
use tauri_plugin_clipboard_manager:: ClipboardExt ;
31
29
use tauri_specta:: Event ;
32
- use tokio:: fs:: File ;
33
- use tokio:: io:: { AsyncReadExt , AsyncSeekExt , BufReader } ;
34
- use tokio:: sync:: watch;
35
- use tokio:: task:: { self , JoinHandle } ;
36
- use tokio:: time:: sleep;
30
+ use tokio:: {
31
+ fs:: File ,
32
+ io:: { AsyncReadExt , AsyncSeekExt , BufReader } ,
33
+ sync:: watch,
34
+ task:: { self , JoinHandle } ,
35
+ } ;
37
36
use tokio_util:: io:: ReaderStream ;
38
- use tracing:: { debug, error, info, trace, warn } ;
37
+ use tracing:: { debug, error, info, trace} ;
39
38
40
39
#[ derive( Deserialize , Serialize , Clone , Type , Debug ) ]
41
40
pub struct S3UploadMeta {
@@ -65,15 +64,6 @@ pub struct UploadedImage {
65
64
pub id : String ,
66
65
}
67
66
68
- pub fn upload_v2 ( app : AppHandle ) {
69
- // TODO: Progress reporting
70
- // TODO: Multipart or regular upload automatically sorted out
71
- // TODO: Allow either FS derived or Rust progress derived multipart upload source
72
- // TODO: Support screenshots, or videos
73
-
74
- todo ! ( ) ;
75
- }
76
-
77
67
pub struct UploadProgressUpdater {
78
68
video_state : Option < VideoProgressState > ,
79
69
app : AppHandle ,
@@ -123,7 +113,7 @@ impl UploadProgressUpdater {
123
113
tokio:: spawn ( {
124
114
let video_id = self . video_id . clone ( ) ;
125
115
async move {
126
- Self :: send_api_update ( & app, video_id, uploaded, total) . await ;
116
+ api :: desktop_video_progress ( & app, & video_id, uploaded, total) . await ;
127
117
}
128
118
} ) ;
129
119
@@ -135,7 +125,7 @@ impl UploadProgressUpdater {
135
125
let video_id = self . video_id . clone ( ) ;
136
126
tokio:: spawn ( async move {
137
127
tokio:: time:: sleep ( Duration :: from_secs ( 2 ) ) . await ;
138
- Self :: send_api_update ( & app, video_id, uploaded, total) . await ;
128
+ api :: desktop_video_progress ( & app, & video_id, uploaded, total) . await ;
139
129
} )
140
130
} ;
141
131
@@ -144,30 +134,6 @@ impl UploadProgressUpdater {
144
134
}
145
135
}
146
136
}
147
-
148
- async fn send_api_update ( app : & AppHandle , video_id : String , uploaded : u64 , total : u64 ) {
149
- let response = app
150
- . authed_api_request ( "/api/desktop/video/progress" , |client, url| {
151
- client
152
- . post ( url)
153
- . header ( "X-Cap-Desktop-Version" , env ! ( "CARGO_PKG_VERSION" ) )
154
- . json ( & json ! ( {
155
- "videoId" : video_id,
156
- "uploaded" : uploaded,
157
- "total" : total,
158
- "updatedAt" : chrono:: Utc :: now( ) . to_rfc3339( )
159
- } ) )
160
- } )
161
- . await ;
162
-
163
- match response {
164
- Ok ( resp) if resp. status ( ) . is_success ( ) => {
165
- trace ! ( "Progress update sent successfully" ) ;
166
- }
167
- Ok ( resp) => error ! ( "Failed to send progress update: {}" , resp. status( ) ) ,
168
- Err ( err) => error ! ( "Failed to send progress update: {err}" ) ,
169
- }
170
- }
171
137
}
172
138
173
139
#[ derive( Default , Debug ) ]
@@ -740,8 +706,6 @@ fn uploader(
740
706
upload_id : String ,
741
707
stream : impl Stream < Item = io:: Result < Chunk > > ,
742
708
) -> impl Stream < Item = Result < UploadedPart , String > > {
743
- let client = reqwest:: Client :: default ( ) ;
744
-
745
709
try_stream ! {
746
710
let mut stream = pin!( stream) ;
747
711
let mut prev_part_number = None ;
@@ -755,8 +719,17 @@ fn uploader(
755
719
api:: upload_multipart_presign_part( & app, & video_id, & upload_id, part_number, & md5_sum)
756
720
. await ?;
757
721
758
- // TODO: Retries
759
- let resp = client
722
+ let url = Uri :: from_str( & presigned_url) . map_err( |err| format!( "uploader/part/{part_number}/invalid_url: {err:?}" ) ) ?;
723
+ let resp = reqwest:: Client :: builder( )
724
+ . retry( reqwest:: retry:: for_host( url. host( ) . unwrap_or( "<unknown>" ) . to_string( ) ) . classify_fn( |req_rep| {
725
+ if req_rep. status( ) . map_or( false , |s| s. is_server_error( ) ) {
726
+ req_rep. retryable( )
727
+ } else {
728
+ req_rep. success( )
729
+ }
730
+ } ) )
731
+ . build( )
732
+ . map_err( |err| format!( "uploader/part/{part_number}/client: {err:?}" ) ) ?
760
733
. put( & presigned_url)
761
734
. header( "Content-MD5" , & md5_sum)
762
735
. header( "Content-Length" , chunk. len( ) )
0 commit comments