@@ -10,54 +10,66 @@ use axum::{
1010} ;
1111use bytes:: Bytes ;
1212
13- use crate :: services:: ws:: WsSetting ;
13+ pub struct WsRecordSetting {
14+ pub record_callback_url : Option < String > ,
15+ }
16+
17+ async fn post_to_callback_url ( callback_url : & str , id : & str , file_path : & str ) -> anyhow:: Result < ( ) > {
18+ let client = reqwest:: Client :: new ( ) ;
19+
20+ let resp = client
21+ . post ( callback_url)
22+ . json ( & serde_json:: json!( {
23+ "id" : id,
24+ "download_uri" : format!( "/downloads/{}" , file_path) ,
25+ } ) )
26+ . send ( )
27+ . await ?;
28+
29+ log:: info!(
30+ "[Record] {} callback to {} success: {}" ,
31+ id,
32+ callback_url,
33+ resp. status( )
34+ ) ;
35+
36+ Ok ( ( ) )
37+ }
1438
1539pub async fn ws_handler (
16- Extension ( pool ) : Extension < Arc < WsSetting > > ,
40+ Extension ( setting ) : Extension < Arc < WsRecordSetting > > ,
1741 ws : WebSocketUpgrade ,
1842 Path ( id) : Path < String > ,
1943) -> impl IntoResponse {
2044 let request_id = uuid:: Uuid :: new_v4 ( ) . as_u128 ( ) ;
2145 log:: info!( "[Record] {id}:{request_id:x} connected." ) ;
2246
2347 ws. on_upgrade ( move |socket| async move {
24- let id = id. clone ( ) ;
25- let pool = pool. clone ( ) ;
26- if let Err ( e) = handle_socket ( socket, & id, pool. clone ( ) ) . await {
27- log:: error!( "{id}:{request_id:x} error: {e}" ) ;
28- } ;
48+ match handle_socket ( socket, & id) . await {
49+ Ok ( file_path) => {
50+ if let Some ( callback_url) = & setting. record_callback_url {
51+ if let Err ( e) = post_to_callback_url ( callback_url, & id, & file_path) . await {
52+ log:: error!( "[Record] {} callback to {} failed: {}" , id, callback_url, e) ;
53+ }
54+ }
55+ }
56+ Err ( e) => {
57+ log:: error!( "{id}:{request_id:x} error: {e}" ) ;
58+ }
59+ }
2960 log:: info!( "{id}:{request_id:x} disconnected." ) ;
3061 } )
3162}
3263
3364enum ProcessMessageResult {
3465 Audio ( Bytes ) ,
35- Submit ,
36- Text ( String ) ,
37- StartRecord ,
38- StartChat ,
3966 Close ,
4067 Skip ,
4168}
4269
4370fn process_message ( msg : Message ) -> ProcessMessageResult {
4471 match msg {
45- Message :: Text ( t) => {
46- if let Ok ( cmd) = serde_json:: from_str :: < crate :: protocol:: ClientCommand > ( & t) {
47- match cmd {
48- crate :: protocol:: ClientCommand :: StartRecord => {
49- ProcessMessageResult :: StartRecord
50- }
51- crate :: protocol:: ClientCommand :: StartChat => ProcessMessageResult :: StartChat ,
52- crate :: protocol:: ClientCommand :: Submit => ProcessMessageResult :: Submit ,
53- crate :: protocol:: ClientCommand :: Text { input } => {
54- ProcessMessageResult :: Text ( input)
55- }
56- }
57- } else {
58- ProcessMessageResult :: Skip
59- }
60- }
72+ Message :: Text ( _) => ProcessMessageResult :: Skip ,
6173 Message :: Binary ( d) => ProcessMessageResult :: Audio ( d) ,
6274 Message :: Close ( c) => {
6375 if let Some ( cf) = c {
@@ -76,29 +88,35 @@ fn process_message(msg: Message) -> ProcessMessageResult {
7688 }
7789}
7890
79- // TODO: implement recording logic
80- async fn handle_socket (
81- mut socket : WebSocket ,
82- id : & str ,
83- pool : Arc < WsSetting > ,
84- ) -> anyhow:: Result < ( ) > {
85- std:: fs:: create_dir_all ( format ! ( "./record/{id}" ) ) ?;
91+ async fn handle_socket ( mut socket : WebSocket , id : & str ) -> anyhow:: Result < String > {
92+ let now = chrono:: Local :: now ( ) ;
93+ let date_str = now. format ( "%Y%m%d_%H%M%S%z" ) . to_string ( ) ;
94+ let file_path = format ! ( "{id}/record_{date_str}.wav" ) ;
95+ let path = format ! ( "./record/{file_path}" ) ;
8696
87- while let Some ( message) = socket. recv ( ) . await {
88- let message = message. map_err ( |e| anyhow:: anyhow!( "recv ws error: {e}" ) ) ?;
97+ let mut wav_file = crate :: util:: UnlimitedWavFileWriter :: new (
98+ & path,
99+ crate :: util:: WavConfig {
100+ sample_rate : 16000 ,
101+ channels : 1 ,
102+ bits_per_sample : 16 ,
103+ } ,
104+ )
105+ . await ?;
89106
107+ while let Ok ( Some ( Ok ( message) ) ) =
108+ tokio:: time:: timeout ( std:: time:: Duration :: from_secs ( 60 ) , socket. recv ( ) ) . await
109+ {
90110 match process_message ( message) {
91- ProcessMessageResult :: Audio ( _) => { }
92- ProcessMessageResult :: Submit => { }
93- ProcessMessageResult :: Text ( _) => { }
94- ProcessMessageResult :: Skip => { }
95- ProcessMessageResult :: StartRecord => { }
96- ProcessMessageResult :: StartChat => { }
111+ ProcessMessageResult :: Audio ( chunk) => {
112+ wav_file. write_pcm_data ( & chunk) . await ?;
113+ }
97114 ProcessMessageResult :: Close => {
98115 return Err ( anyhow:: anyhow!( "ws closed" ) ) ;
99116 }
117+ ProcessMessageResult :: Skip => { }
100118 }
101119 }
102120
103- Ok ( ( ) )
121+ Ok ( file_path )
104122}
0 commit comments