11// credit @filleduchaos
22
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+ } ;
68use async_stream:: { stream, try_stream} ;
7- use axum:: body :: Body ;
9+ use axum:: http :: { self , Uri } ;
810use bytes:: Bytes ;
9- use cap_project:: { RecordingMeta , RecordingMetaInner , UploadState } ;
11+ use cap_project:: { RecordingMeta , UploadState } ;
1012use cap_utils:: spawn_actor;
1113use ffmpeg:: ffi:: AV_TIME_BASE ;
1214use flume:: Receiver ;
1315use futures:: { Stream , StreamExt , TryStreamExt , stream} ;
14- use image:: ImageReader ;
15- use image:: codecs:: jpeg:: JpegEncoder ;
16+ use image:: { ImageReader , codecs:: jpeg:: JpegEncoder } ;
1617use reqwest:: StatusCode ;
17- use reqwest:: header:: CONTENT_LENGTH ;
1818use serde:: { Deserialize , Serialize } ;
19- use serde_json:: json;
2019use specta:: Type ;
21- use std:: error:: Error ;
22- use std:: io;
23- use std:: path:: Path ;
24- use std:: pin:: pin;
2520use std:: {
26- path:: PathBuf ,
21+ io,
22+ path:: { Path , PathBuf } ,
23+ pin:: pin,
24+ str:: FromStr ,
2725 time:: { Duration , Instant } ,
2826} ;
2927use tauri:: { AppHandle , ipc:: Channel } ;
3028use tauri_plugin_clipboard_manager:: ClipboardExt ;
3129use 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+ } ;
3736use tokio_util:: io:: ReaderStream ;
38- use tracing:: { debug, error, info, trace, warn } ;
37+ use tracing:: { debug, error, info, trace} ;
3938
4039#[ derive( Deserialize , Serialize , Clone , Type , Debug ) ]
4140pub struct S3UploadMeta {
@@ -65,15 +64,6 @@ pub struct UploadedImage {
6564 pub id : String ,
6665}
6766
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-
7767pub struct UploadProgressUpdater {
7868 video_state : Option < VideoProgressState > ,
7969 app : AppHandle ,
@@ -123,7 +113,7 @@ impl UploadProgressUpdater {
123113 tokio:: spawn ( {
124114 let video_id = self . video_id . clone ( ) ;
125115 async move {
126- Self :: send_api_update ( & app, video_id, uploaded, total) . await ;
116+ api :: desktop_video_progress ( & app, & video_id, uploaded, total) . await ;
127117 }
128118 } ) ;
129119
@@ -135,7 +125,7 @@ impl UploadProgressUpdater {
135125 let video_id = self . video_id . clone ( ) ;
136126 tokio:: spawn ( async move {
137127 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 ;
139129 } )
140130 } ;
141131
@@ -144,30 +134,6 @@ impl UploadProgressUpdater {
144134 }
145135 }
146136 }
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- }
171137}
172138
173139#[ derive( Default , Debug ) ]
@@ -740,8 +706,6 @@ fn uploader(
740706 upload_id : String ,
741707 stream : impl Stream < Item = io:: Result < Chunk > > ,
742708) -> impl Stream < Item = Result < UploadedPart , String > > {
743- let client = reqwest:: Client :: default ( ) ;
744-
745709 try_stream ! {
746710 let mut stream = pin!( stream) ;
747711 let mut prev_part_number = None ;
@@ -755,8 +719,17 @@ fn uploader(
755719 api:: upload_multipart_presign_part( & app, & video_id, & upload_id, part_number, & md5_sum)
756720 . await ?;
757721
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:?}" ) ) ?
760733 . put( & presigned_url)
761734 . header( "Content-MD5" , & md5_sum)
762735 . header( "Content-Length" , chunk. len( ) )
0 commit comments