1const CONTENT_TYPE_APPLICATION_JSON: &'static str = "application/json";
2
3pub mod Resemble {
4 use lazy_static::lazy_static;
5 use std::sync::RwLock;
6
7 lazy_static! {
8 static ref API_TOKEN_RW: RwLock<String> = RwLock::new(String::new());
9 }
10
11 lazy_static! {
12 static ref BASE_URL_RW: RwLock<String> =
13 RwLock::new("https://app.resemble.ai/api/".to_string());
14 }
15
16 pub fn set_api_key(api_key: String) {
17 let mut api_token = API_TOKEN_RW.write().unwrap();
18 *api_token = api_key;
19 }
20
21 pub fn set_base_url(url: String) {
22 let mut base_url = BASE_URL_RW.write().unwrap();
23 *base_url = url;
24 }
25
26 fn token() -> String {
27 let api_token = API_TOKEN_RW.read();
28 let token = (*api_token.unwrap()).clone();
29
30 format!("Token token={}", token)
31 }
32
33 fn endpoint(version: &str, endpoint: &str) -> String {
34 let api_endpoint = if endpoint.starts_with('/') {
35 endpoint.to_string()
36 } else {
37 format!("/{}", endpoint)
38 };
39
40 let base_url = BASE_URL_RW.read();
41
42 format!("{}{}{}", *base_url.unwrap(), version, api_endpoint)
43 }
44
45 pub mod v2 {
46 use serde::{de, Deserialize};
47 type APIResponse<T> = Result<T, ErrorResponse>;
48
49 #[derive(Clone, Deserialize, Debug)]
50 pub struct ErrorResponse {
51 pub success: bool,
52 pub message: Option<String>,
53 }
54
55 #[derive(Clone, Deserialize, Debug)]
56 pub struct MessageResponse {
57 pub success: bool,
58 pub message: Option<String>,
59 }
60
61 #[derive(Clone, Deserialize, Debug)]
62 pub struct PaginationResponse<T> {
63 pub success: bool,
64 pub message: Option<String>,
65 pub page: isize,
66 pub num_pages: isize,
67 pub page_size: isize,
68 pub items: Vec<T>,
69 }
70
71 #[derive(Clone, Deserialize, Debug)]
72 pub struct ReadResponse<T> {
73 pub success: bool,
74 pub message: Option<String>,
75 pub item: Option<T>,
76 }
77
78 #[derive(Clone, Deserialize, Debug)]
79 pub struct WriteResponse<T> {
80 pub success: bool,
81 pub message: Option<String>,
82 pub item: Option<T>,
84 }
85
86 #[derive(Clone, Deserialize, Debug)]
87 pub struct UpdateResponse<T> {
88 pub success: bool,
89 pub message: Option<String>,
90 pub item: Option<T>,
92 }
93
94 #[derive(Clone, Deserialize, Debug)]
95 pub struct DeleteResponse {
96 pub success: bool,
97 pub message: Option<String>,
98 }
99
100 fn return_json_error<T>(err: Option<reqwest::Error>) -> Result<T, ErrorResponse> {
101 Err(ErrorResponse {
102 message: Some(format!("Client error: {:#?}", err)),
103 success: false,
104 })
105 }
106
107 pub fn return_json_response<T: de::DeserializeOwned>(
108 response: Result<reqwest::blocking::Response, reqwest::Error>,
109 ) -> Result<T, ErrorResponse> {
110 if response.is_err() {
111 return return_json_error::<T>(response.err());
112 }
113
114 let response = response.unwrap();
115
116 if response.status().as_u16() == 401 {
117 let json = response.json::<ErrorResponse>();
118
119 if json.is_err() {
120 return return_json_error::<T>(json.err());
121 }
122
123 Err(json.unwrap())
124 } else {
125 let json = response.json::<T>();
126
127 if json.is_err() {
128 return return_json_error::<T>(json.err());
129 }
130
131 Ok(json.unwrap())
132 }
133 }
134
135 pub mod project {
136 use chrono::{DateTime, Utc};
137 use reqwest::header::{AUTHORIZATION, CONTENT_TYPE};
138 use serde::{Deserialize, Serialize};
139
140 use crate::Resemble::{
141 endpoint, token,
142 v2::{
143 return_json_response, APIResponse, DeleteResponse, PaginationResponse,
144 ReadResponse, UpdateResponse, WriteResponse,
145 },
146 };
147
148 #[derive(Clone, Deserialize, Debug)]
149 pub struct Project {
150 pub uuid: String,
151 pub name: String,
152 pub description: String,
153 pub is_public: bool,
154 pub is_collaborative: bool,
155 pub is_archived: bool,
156 pub created_at: DateTime<Utc>,
157 pub updated_at: DateTime<Utc>,
158 }
159
160 #[derive(Serialize, Debug)]
161 pub struct ProjectInput {
162 pub name: String,
163 pub description: String,
164 pub is_public: bool,
165 pub is_collaborative: bool,
166 pub is_archived: bool,
167 }
168
169 pub fn all(
170 page: usize,
171 page_size: Option<usize>,
172 ) -> APIResponse<PaginationResponse<Project>> {
173 let mut params = vec![("page", page)];
174 if page_size.is_some() {
175 params.push(("page_size", page_size.unwrap()));
176 }
177
178 let response = reqwest::blocking::Client::new()
179 .get(endpoint("v2", "projects"))
180 .header(AUTHORIZATION, token())
181 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
182 .query(¶ms)
183 .send();
184
185 return_json_response(response)
186 }
187
188 pub fn create(project: ProjectInput) -> APIResponse<WriteResponse<Project>> {
189 let response = reqwest::blocking::Client::new()
190 .post(endpoint("v2", "projects"))
191 .header(AUTHORIZATION, token())
192 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
193 .json(&project)
194 .send();
195
196 return_json_response(response)
197 }
198
199 pub fn update(
200 uuid: String,
201 project: ProjectInput,
202 ) -> APIResponse<UpdateResponse<Project>> {
203 let response = reqwest::blocking::Client::new()
204 .put(endpoint("v2", &format!("projects/{}", uuid)))
205 .header(AUTHORIZATION, token())
206 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
207 .json(&project)
208 .send();
209
210 return_json_response(response)
211 }
212
213 pub fn get(uuid: String) -> APIResponse<ReadResponse<Project>> {
214 let response = reqwest::blocking::Client::new()
215 .get(endpoint("v2", &format!("projects/{}", uuid)))
216 .header(AUTHORIZATION, token())
217 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
218 .send();
219
220 return_json_response(response)
221 }
222
223 pub fn delete(uuid: String) -> APIResponse<DeleteResponse> {
224 let response = reqwest::blocking::Client::new()
225 .delete(endpoint("v2", &format!("projects/{}", uuid)))
226 .header(AUTHORIZATION, token())
227 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
228 .send();
229
230 return_json_response(response)
231 }
232 }
233
234 pub mod voice {
235 use chrono::{DateTime, Utc};
236 use reqwest::header::{AUTHORIZATION, CONTENT_TYPE};
237 use serde::{Deserialize, Serialize};
238
239 use crate::Resemble::{
240 endpoint, token,
241 v2::{
242 return_json_response, APIResponse, DeleteResponse, PaginationResponse,
243 ReadResponse, UpdateResponse, WriteResponse, MessageResponse
244 },
245 };
246
247 #[derive(Clone, Deserialize, Debug)]
248 pub struct Voice {
249 pub uuid: String,
250 pub name: String,
251 pub status: String,
252 pub dataset_url: Option<String>,
253 pub callback_uri: Option<String>,
254 pub created_at: DateTime<Utc>,
255 pub updated_at: DateTime<Utc>,
256 }
257
258 #[derive(Serialize, Debug)]
259 pub struct VoiceInput {
260 pub name: String,
261 pub dataset_url: Option<String>,
262 pub callback_uri: Option<String>
263 }
264
265 #[derive(Serialize, Debug)]
266 pub struct UpdateVoiceInput {
267 pub name: String,
268 pub callback_uri: Option<String>
269 }
270
271
272 pub fn all(
273 page: usize,
274 page_size: Option<usize>,
275 ) -> APIResponse<PaginationResponse<Voice>> {
276 let mut params = vec![("page", page)];
277 if page_size.is_some() {
278 params.push(("page_size", page_size.unwrap()));
279 }
280
281 let response = reqwest::blocking::Client::new()
282 .get(endpoint("v2", "voices"))
283 .header(AUTHORIZATION, token())
284 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
285 .query(¶ms)
286 .send();
287
288 return_json_response(response)
289 }
290
291 pub fn create(voice: VoiceInput) -> APIResponse<WriteResponse<Voice>> {
292 let response = reqwest::blocking::Client::new()
293 .post(endpoint("v2", "voices"))
294 .header(AUTHORIZATION, token())
295 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
296 .json(&voice)
297 .send();
298
299 return_json_response(response)
300 }
301
302 pub fn update(uuid: String, voice: UpdateVoiceInput) -> APIResponse<UpdateResponse<Voice>> {
303 let response = reqwest::blocking::Client::new()
304 .put(endpoint("v2", &format!("voices/{}", uuid)))
305 .header(AUTHORIZATION, token())
306 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
307 .json(&voice)
308 .send();
309
310 return_json_response(response)
311 }
312
313 pub fn build(uuid: String) -> APIResponse<MessageResponse> {
314 let response = reqwest::blocking::Client::new()
315 .post(endpoint("v2", &format!("voices/{}/build", uuid)))
316 .header(AUTHORIZATION, token())
317 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
318 .send();
319
320 return_json_response(response)
321 }
322
323 pub fn get(uuid: String) -> APIResponse<ReadResponse<Voice>> {
324 let response = reqwest::blocking::Client::new()
325 .get(endpoint("v2", &format!("voices/{}", uuid)))
326 .header(AUTHORIZATION, token())
327 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
328 .send();
329
330 return_json_response(response)
331 }
332
333 pub fn delete(uuid: String) -> APIResponse<DeleteResponse> {
334 let response = reqwest::blocking::Client::new()
335 .delete(endpoint("v2", &format!("voices/{}", uuid)))
336 .header(AUTHORIZATION, token())
337 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
338 .send();
339
340 return_json_response(response)
341 }
342 }
343
344 pub mod recording {
345 use chrono::{DateTime, Utc};
346 use reqwest::blocking::multipart::Form;
347 use reqwest::header::{AUTHORIZATION, CONTENT_TYPE};
348 use serde::{Deserialize, Serialize};
349
350 use crate::Resemble::{
351 endpoint, token,
352 v2::{
353 return_json_error, return_json_response, APIResponse, DeleteResponse,
354 ErrorResponse, PaginationResponse, ReadResponse, UpdateResponse, WriteResponse,
355 },
356 };
357
358 #[derive(Clone, Deserialize, Debug)]
359 pub struct Recording {
360 pub uuid: String,
361 pub name: String,
362 pub text: String,
363 pub emotion: String,
364 pub is_active: bool,
365 pub audio_src: String,
366 pub created_at: DateTime<Utc>,
367 pub updated_at: DateTime<Utc>,
368 }
369
370 #[derive(Serialize, Debug)]
371 pub struct RecordingInput {
372 pub name: String,
373 pub text: String,
374 pub emotion: String,
375 pub is_active: bool,
376 }
377
378 pub fn all(
379 voice_uuid: String,
380 page: usize,
381 page_size: Option<usize>,
382 ) -> APIResponse<PaginationResponse<Recording>> {
383 let mut params = vec![("page", page)];
384 if page_size.is_some() {
385 params.push(("page_size", page_size.unwrap()));
386 }
387
388 let response = reqwest::blocking::Client::new()
389 .get(endpoint("v2", &format!("voices/{}/recordings", voice_uuid)))
390 .header(AUTHORIZATION, token())
391 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
392 .query(¶ms)
393 .send();
394
395 return_json_response(response)
396 }
397
398 pub fn create(
399 voice_uuid: String,
400 recording: RecordingInput,
401 audio_file: std::path::PathBuf,
402 ) -> APIResponse<WriteResponse<Recording>> {
403 let form = Form::new()
404 .text("name", recording.name)
405 .text("text", recording.text)
406 .text("emotion", recording.emotion)
407 .text(
408 "is_active",
409 if recording.is_active { "true" } else { "false" },
410 )
411 .file("file", audio_file);
412
413 if form.is_err() {
414 return Err(ErrorResponse {
415 message: Some(format!("Client error: {:#?}", form.err())),
416 success: false,
417 });
418 }
419
420 let response = reqwest::blocking::Client::new()
421 .post(endpoint("v2", &format!("voices/{}/recordings", voice_uuid)))
422 .multipart(form.unwrap())
423 .header(AUTHORIZATION, token())
424 .send();
425
426 return_json_response(response)
427 }
428
429 pub fn update(
430 voice_uuid: String,
431 uuid: String,
432 recording: RecordingInput,
433 ) -> APIResponse<UpdateResponse<Recording>> {
434 let response = reqwest::blocking::Client::new()
435 .put(endpoint(
436 "v2",
437 &format!("voices/{}/recordings/{}", voice_uuid, uuid),
438 ))
439 .header(AUTHORIZATION, token())
440 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
441 .json(&recording)
442 .send();
443
444 return_json_response(response)
445 }
446
447 pub fn get(voice_uuid: String, uuid: String) -> APIResponse<ReadResponse<Recording>> {
448 let response = reqwest::blocking::Client::new()
449 .get(endpoint(
450 "v2",
451 &format!("voices/{}/recordings/{}", voice_uuid, uuid),
452 ))
453 .header(AUTHORIZATION, token())
454 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
455 .send();
456
457 return_json_response(response)
458 }
459
460 pub fn delete(voice_uuid: String, uuid: String) -> APIResponse<DeleteResponse> {
461 let response = reqwest::blocking::Client::new()
462 .delete(endpoint(
463 "v2",
464 &format!("voices/{}/recordings/{}", voice_uuid, uuid),
465 ))
466 .header(AUTHORIZATION, token())
467 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
468 .send();
469
470 return_json_response(response)
471 }
472 }
473
474 pub mod clip {
475 use chrono::{DateTime, Utc};
476 use reqwest::header::{AUTHORIZATION, CONTENT_TYPE};
477 use serde::{Deserialize, Serialize};
478
479 use crate::Resemble::{
480 endpoint, token,
481 v2::{
482 return_json_error, return_json_response, APIResponse, DeleteResponse,
483 ErrorResponse, PaginationResponse, ReadResponse, UpdateResponse, WriteResponse,
484 },
485 };
486
487 #[derive(Clone, Deserialize, Debug)]
488 pub struct Timestamps {
489 pub phonemes: String,
490 pub end_times: Vec<f64>,
491 pub phoneme_chars: Vec<String>,
492 }
493
494 #[derive(Clone, Deserialize, Debug)]
495 pub struct Clip {
496 pub uuid: String,
497 pub title: Option<String>,
498 pub body: String,
499 pub voice_uuid: String,
500 pub is_public: bool,
501 pub is_archived: bool,
502 pub timestamps: Option<Timestamps>,
503 pub audio_src: Option<String>,
504 pub raw_audio: Option<String>,
505 pub created_at: DateTime<Utc>,
506 pub updated_at: DateTime<Utc>,
507 }
508
509 #[derive(Serialize, Debug)]
510 pub struct SyncClipInput {
511 pub title: Option<String>,
512 pub body: String,
513 pub voice_uuid: String,
514 pub is_public: bool,
515 pub is_archived: bool,
516 pub sample_rate: Option<usize>, pub output_format: Option<String>, pub precision: Option<String>, pub include_timestamps: Option<bool>,
520
521 pub raw: Option<bool>,
522 }
523
524 #[derive(Serialize, Debug)]
525 pub struct AsyncClipInput {
526 pub title: Option<String>,
527 pub body: String,
528 pub voice_uuid: String,
529 pub is_public: bool,
530 pub is_archived: bool,
531 pub sample_rate: Option<usize>, pub output_format: Option<String>, pub precision: Option<String>, pub include_timestamps: Option<bool>,
535
536 pub callback_uri: String,
537 }
538
539 pub fn all(
540 project_uuid: String,
541 page: usize,
542 page_size: Option<usize>,
543 ) -> APIResponse<PaginationResponse<Clip>> {
544 let mut params = vec![("page", page)];
545 if page_size.is_some() {
546 params.push(("page_size", page_size.unwrap()));
547 }
548
549 let response = reqwest::blocking::Client::new()
550 .get(endpoint("v2", &format!("projects/{}/clips", project_uuid)))
551 .header(AUTHORIZATION, token())
552 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
553 .query(¶ms)
554 .send();
555
556 return_json_response(response)
557 }
558
559 pub fn create_sync(
560 project_uuid: String,
561 clip: SyncClipInput,
562 ) -> APIResponse<WriteResponse<Clip>> {
563 let response = reqwest::blocking::Client::new()
564 .post(endpoint("v2", &format!("projects/{}/clips", project_uuid)))
565 .header(AUTHORIZATION, token())
566 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
567 .json(&clip)
568 .send();
569
570 return_json_response(response)
571 }
572
573 pub fn create_async(
574 project_uuid: String,
575 clip: AsyncClipInput,
576 ) -> APIResponse<WriteResponse<Clip>> {
577 let response = reqwest::blocking::Client::new()
578 .post(endpoint("v2", &format!("projects/{}/clips", project_uuid)))
579 .header(AUTHORIZATION, token())
580 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
581 .json(&clip)
582 .send();
583
584 return_json_response(response)
585 }
586
587 pub fn update_async(
588 project_uuid: String,
589 uuid: String,
590 clip: AsyncClipInput,
591 ) -> APIResponse<UpdateResponse<Clip>> {
592 let response = reqwest::blocking::Client::new()
593 .put(endpoint(
594 "v2",
595 &format!("projects/{}/clips/{}", project_uuid, uuid),
596 ))
597 .header(AUTHORIZATION, token())
598 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
599 .json(&clip)
600 .send();
601
602 return_json_response(response)
603 }
604
605 pub fn get(project_uuid: String, uuid: String) -> APIResponse<ReadResponse<Clip>> {
606 let response = reqwest::blocking::Client::new()
607 .get(endpoint(
608 "v2",
609 &format!("projects/{}/clips/{}", project_uuid, uuid),
610 ))
611 .header(AUTHORIZATION, token())
612 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
613 .send();
614
615 return_json_response(response)
616 }
617
618 pub fn delete(project_uuid: String, uuid: String) -> APIResponse<DeleteResponse> {
619 let response = reqwest::blocking::Client::new()
620 .delete(endpoint(
621 "v2",
622 &format!("projects/{}/clips/{}", project_uuid, uuid),
623 ))
624 .header(AUTHORIZATION, token())
625 .header(CONTENT_TYPE, crate::CONTENT_TYPE_APPLICATION_JSON)
626 .send();
627
628 return_json_response::<DeleteResponse>(response)
629 }
630 }
631 }
632}
633
634#[cfg(test)]
635mod tests {
636 use crate::Resemble::{
637 self,
638 v2::clip::{AsyncClipInput, SyncClipInput},
639 v2::project::ProjectInput,
640 v2::recording::RecordingInput,
641 v2::voice::VoiceInput,
642 };
643 use std::{env::var, panic};
644
645 fn get_test_voice_uuid() -> String {
646 std::env::var("TEST_VOICE_UUID")
647 .expect("Invalid voice_uuid; please set the TEST_VOICE_UUID environment variable")
648 }
649
650 fn get_test_callback_uri() -> String {
651 std::env::var("TEST_CALLBACK_URI")
652 .expect("Invalid callback_uri; please set the TEST_CALLBACK_URI environment variable")
653 }
654
655 #[test]
656 fn test_v2_projects() {
657 run_test(|| {
658 let projects = Resemble::v2::project::all(1, None).unwrap();
660 assert_eq!(projects.success, true);
661
662 let project = Resemble::v2::project::create(ProjectInput {
664 name: "Test Project".to_string(),
665 description: "Test Description".to_string(),
666 is_archived: false,
667 is_collaborative: false,
668 is_public: false,
669 })
670 .unwrap();
671 assert_eq!(project.success, true);
672
673 let updated_project = Resemble::v2::project::update(
675 project.item.unwrap().uuid,
676 ProjectInput {
677 name: "Updated Test Project".to_string(),
678 description: "Updated Test Description".to_string(),
679 is_archived: false,
680 is_collaborative: false,
681 is_public: false,
682 },
683 )
684 .unwrap();
685 assert_eq!(updated_project.success, true);
686
687 let fetched_project =
689 Resemble::v2::project::get(updated_project.item.unwrap().uuid).unwrap();
690 assert_eq!(fetched_project.success, true);
691
692 let delete_op =
694 Resemble::v2::project::delete(fetched_project.item.unwrap().uuid).unwrap();
695 assert_eq!(delete_op.success, true);
696 })
697 }
698
699 #[test]
700 fn test_v2_voices() {
701 run_test(|| {
702 let voices = Resemble::v2::voice::all(1, None).unwrap();
704 assert_eq!(voices.success, true);
705
706 let voice = Resemble::v2::voice::create(VoiceInput {
708 name: "Test voice".to_string(),
709 })
710 .unwrap();
711 assert_eq!(voice.success, true);
712
713 let updated_voice = Resemble::v2::voice::update(
715 voice.item.unwrap().uuid,
716 VoiceInput {
717 name: "Updated Test voice".to_string(),
718 },
719 )
720 .unwrap();
721 assert_eq!(updated_voice.success, true);
722
723 let fetched_voice = Resemble::v2::voice::get(updated_voice.item.unwrap().uuid).unwrap();
725 assert_eq!(fetched_voice.success, true);
726
727 let delete_op = Resemble::v2::voice::delete(fetched_voice.item.unwrap().uuid).unwrap();
729 assert_eq!(delete_op.success, true);
730 })
731 }
732
733 #[test]
734 fn test_v2_clips() {
735 run_test(|| {
736 let project = Resemble::v2::project::create(ProjectInput {
737 description: "Test Description".to_string(),
738 name: "Test Project".to_string(),
739 is_archived: false,
740 is_collaborative: false,
741 is_public: false,
742 })
743 .unwrap();
744 let project_uuid = project.item.unwrap().uuid;
745
746 let clips = Resemble::v2::clip::all(project_uuid.clone(), 1, None).unwrap();
748 assert_eq!(clips.success, true);
749
750 let clip_create_sync = Resemble::v2::clip::create_sync(
752 project_uuid.clone(),
753 SyncClipInput {
754 body: "this is a test".to_string(),
755 include_timestamps: Some(true),
756 is_archived: false,
757 is_public: false,
758 output_format: None,
759 precision: None,
760 raw: None,
761 sample_rate: None,
762 title: None,
763 voice_uuid: get_test_voice_uuid(),
764 },
765 )
766 .unwrap();
767 assert_eq!(clip_create_sync.success, true);
768
769 let clip_uuid = clip_create_sync.clone().item.unwrap().uuid;
770
771 let clip_create_async = Resemble::v2::clip::create_async(
773 project_uuid.clone(),
774 AsyncClipInput {
775 body: "this is a test".to_string(),
776 include_timestamps: Some(true),
777 is_archived: false,
778 is_public: false,
779 output_format: None,
780 precision: None,
781 sample_rate: None,
782 title: None,
783 voice_uuid: get_test_voice_uuid(),
784 callback_uri: get_test_callback_uri(),
785 },
786 )
787 .unwrap();
788 assert_eq!(clip_create_async.success, true);
789
790 let clip_update_async = Resemble::v2::clip::update_async(
792 project_uuid.clone(),
793 clip_uuid.clone(),
794 AsyncClipInput {
795 body: "this is another test".to_string(),
796 include_timestamps: Some(true),
797 is_archived: false,
798 is_public: false,
799 output_format: None,
800 precision: None,
801 sample_rate: None,
802 title: None,
803 voice_uuid: get_test_voice_uuid(),
804 callback_uri: get_test_callback_uri(),
805 },
806 )
807 .unwrap();
808 assert_eq!(clip_update_async.success, true);
809
810 let fetched_clip =
812 Resemble::v2::clip::get(project_uuid.clone(), clip_uuid.clone()).unwrap();
813 assert_eq!(fetched_clip.success, true);
814
815 let delete_op =
817 Resemble::v2::clip::delete(project_uuid.clone(), clip_uuid.clone()).unwrap();
818 assert_eq!(delete_op.success, true);
819 })
820 }
821
822 #[test]
823 fn test_v2_recordings() {
824 run_test(|| {
825 let voice = Resemble::v2::voice::create(VoiceInput {
826 name: "Test Voice".to_string(),
827 })
828 .unwrap();
829 let voice_uuid = voice.item.unwrap().uuid;
830
831 let recordings = Resemble::v2::recording::all(voice_uuid.clone(), 1, None).unwrap();
833 assert_eq!(recordings.success, true);
834
835 let path = std::path::PathBuf::from("spec_sample_audio.wav");
836 assert!(path.exists());
837
838 let created_recording = Resemble::v2::recording::create(
840 voice_uuid.clone(),
841 RecordingInput {
842 emotion: "neutral".to_string(),
843 is_active: true,
844 name: "recording".to_string(),
845 text: "this is a test".to_string(),
846 },
847 path,
848 )
849 .unwrap();
850 assert_eq!(created_recording.success, true);
851
852 let recording_uuid = created_recording.clone().item.unwrap().uuid;
853
854 let updated_recording = Resemble::v2::recording::update(
856 voice_uuid.clone(),
857 recording_uuid.clone(),
858 RecordingInput {
859 emotion: "neutral".to_string(),
860 is_active: false,
861 name: "updated recording".to_string(),
862 text: "this is an updated test".to_string(),
863 },
864 )
865 .unwrap();
866 assert_eq!(updated_recording.success, true);
867
868 let fetched_recording =
870 Resemble::v2::recording::get(voice_uuid.clone(), recording_uuid.clone()).unwrap();
871 assert_eq!(fetched_recording.success, true);
872
873 let delete_op =
875 Resemble::v2::recording::delete(voice_uuid.clone(), recording_uuid.clone())
876 .unwrap();
877 assert_eq!(delete_op.success, true);
878 })
879 }
880
881 fn run_test<T>(test: T) -> ()
882 where
883 T: FnOnce() -> () + panic::UnwindSafe,
884 {
885 let api_key = var("TEST_API_KEY").unwrap();
886 let base_url = var("TEST_BASE_URL").unwrap_or_default();
887 Resemble::set_api_key(api_key);
888
889 if !base_url.is_empty() {
890 Resemble::set_base_url(base_url);
891 }
892
893 let result = panic::catch_unwind(|| test());
894
895 assert!(result.is_ok())
896 }
897}