feature/share-libs
rs 253 lines 7.41 KB
Raw
1 use super::{
2 constants::{base_url_builder, PATH_AUDIO},
3 download_result_files::save_result_files,
4 };
5 use crate::{
6 command::{
7 download_result_files::{download_result_files, get_provider_id_dir_path},
8 player_set_volume::Mode,
9 }, get_soundcard, models::player::{AudioFile, AudioFileResponse, PreparePlayerResponse, TauriResponse}, players::{BASS_SINK, DRUMS_SINK, OTHER_SINK, VOCALS_SINK}, record::record_instrument::AudioDevice
10 };
11 use log::{debug, error, info};
12 use reqwest::Client;
13 use rodio::{Decoder, DeviceTrait, OutputStream, Sink};
14 use std::{
15 fs::{self, DirEntry, File},
16 io::BufReader,
17 path::{Path, PathBuf},
18 };
19 use tauri::async_runtime::spawn;
20
21 #[tauri::command]
22 pub async fn player_prepare(audio_file_id: String) -> PreparePlayerResponse {
23 debug!("Preparing player for provider_id {}.", audio_file_id);
24
25 // Get audio file response
26 let response = Client::new()
27 .get(build_audio_detail_url(audio_file_id))
28 .send()
29 .await;
30
31 match response {
32 Ok(response) => {
33 debug!("Got response: {:?}", response);
34 let res: AudioFileResponse = match response.json().await {
35 Ok(audio_file) => audio_file,
36 Err(e) => {
37 error!("Failed to parse response: {:?}", e);
38 return PreparePlayerResponse {
39 status: TauriResponse::Error,
40 message: e.to_string(),
41 audio_file_name: None,
42 };
43 }
44 };
45
46 prepare_result_files(res.audio_file).await
47 }
48 Err(e) => {
49 error!("Failed to get response: {:?}", e);
50 PreparePlayerResponse {
51 status: TauriResponse::Error,
52 message: e.to_string(),
53 audio_file_name: None,
54 }
55 }
56 }
57 }
58
59 async fn prepare_result_files(audio_file: AudioFile) -> PreparePlayerResponse {
60 debug!("Preparing result files: {:?}", audio_file);
61 match audio_file.results {
62 Some(results) => {
63 if results.is_empty() {
64 return PreparePlayerResponse {
65 status: TauriResponse::Error,
66 message: "No results found.".to_string(),
67 audio_file_name: Some(audio_file.name),
68 };
69 }
70 save_result_files(&audio_file.id.to_string(), &results).await;
71 download_result_files(audio_file.id.to_string(), results).await;
72 prepare_players(audio_file.id.to_string()).await;
73 PreparePlayerResponse {
74 status: TauriResponse::Success,
75 message: "Success".to_string(),
76 audio_file_name: Some(audio_file.name),
77 }
78 }
79 None => {
80 return PreparePlayerResponse {
81 status: TauriResponse::Error,
82 message: "No results found.".to_string(),
83 audio_file_name: Some(audio_file.name),
84 };
85 }
86
87 }
88 }
89
90 fn build_audio_detail_url(id: String) -> String {
91 let mut url_builder = base_url_builder();
92 url_builder.add_route(PATH_AUDIO);
93 url_builder.add_route(&id);
94 url_builder.build()
95 }
96
97 pub async fn prepare_players(audio_id: String) {
98 info!("Preparing player audio files: {}", audio_id);
99
100 // Check if we have the ~/.sfai/audio/{audio_id} folder
101 let audio_dir_path: PathBuf = match get_provider_id_dir_path(&audio_id).await {
102 Ok(path) => path,
103 Err(e) => {
104 info!("Failed to get audio directory: {:?}", e);
105 return;
106 }
107 };
108
109 // get the files in the directory
110 let dir_entries = if let Ok(paths) = fs::read_dir(audio_dir_path) {
111 paths
112 } else {
113 info!("Failed to read audio directory.");
114 return;
115 };
116
117 let get_path = |dir_entry: Result<DirEntry, std::io::Error>| -> Option<PathBuf> {
118 debug!("Getting path to audio file.");
119 let dir_entry = match dir_entry {
120 Ok(dir_entry) => dir_entry,
121 Err(e) => {
122 info!("Failed to get dir entry: {:?}", e);
123 return None;
124 }
125 };
126 Some(dir_entry.path())
127 };
128
129 let sink = |path: &Path| -> Option<(Mode, Sink)> {
130 debug!("Playing: {:?}", path);
131 let device = if let Ok(device) = get_soundcard(AudioDevice::Output, true) {
132 device
133 } else {
134 info!("Failed to get soundcard output.");
135 return None;
136 };
137
138 if let Ok(name) = device.name() {
139 info!("Output device is: {}", name);
140 }
141
142 let (_stream, handle) = if let Ok(x) = OutputStream::try_from_device(&device) {
143 x
144 } else {
145 info!("Failed to get output stream.");
146 return None;
147 };
148
149 let sink = if let Ok(z) = Sink::try_new(&handle) {
150 z
151 } else {
152 info!("Failed to get sink.");
153 return None;
154 };
155
156 let file = if let Ok(f) = File::open(path) {
157 f
158 } else {
159 info!("Failed to open file.");
160 return None;
161 };
162
163 match Decoder::new(BufReader::new(file)) {
164 Ok(decoder) => {
165 sink.append(decoder);
166 sink.pause();
167 Some((get_mode(path), sink))
168 }
169 Err(e) => {
170 info!("Failed to create decoder: {:?}", e);
171 None
172 }
173 }
174 };
175
176 let file_paths = dir_entries
177 .into_iter()
178 .flat_map(get_path)
179 .collect::<Vec<_>>();
180
181 file_paths
182 .into_iter()
183 .filter_map(|x| {
184 // Only play .wav
185 if let Some("wav") = x.extension().and_then(|x| x.to_str()) {
186 Some(x)
187 } else {
188 None
189 }
190 })
191 .map(|x| {
192 spawn(async move {
193 let sink = sink(&x);
194 persist_sinks(sink)
195 })
196 })
197 .for_each(drop);
198 }
199
200 fn persist_sinks(sink: Option<(Mode, Sink)>) {
201 // Return early if we don't have a sink
202 let (mode, sink) = match sink {
203 Some(x) => x,
204 None => return,
205 };
206 match mode {
207 Mode::Vocals => {
208 info!("Persisting vocals sink.");
209 let _ = unsafe { VOCALS_SINK.set(sink) };
210 }
211 Mode::Bass => {
212 info!("Persisting bass sink.");
213 let _ = unsafe { BASS_SINK.set(sink) };
214 }
215 Mode::Other => {
216 info!("Persisting other sink.");
217 let _ = unsafe { OTHER_SINK.set(sink) };
218 }
219 Mode::Drums => {
220 info!("Persisting drums sink.");
221 let _ = unsafe { DRUMS_SINK.set(sink) };
222 }
223 Mode::Unknown => {
224 error!("Got an unkown mode.");
225 panic!("Should not be here.")
226 }
227 }
228 }
229
230 fn get_mode(path: &Path) -> Mode {
231 if let Some(file_name) = path.file_name() {
232 if let Some(file_name) = file_name.to_str() {
233 return get_mode_from_filename(file_name);
234 } else {
235 return Mode::Unknown;
236 }
237 }
238 Mode::Unknown
239 }
240
241 pub fn get_mode_from_filename(filename: &str) -> Mode {
242 if filename.contains("vocal") {
243 Mode::Vocals
244 } else if filename.contains("drums") {
245 Mode::Drums
246 } else if filename.contains("bass") {
247 Mode::Bass
248 } else if filename.contains("other") {
249 Mode::Other
250 } else {
251 Mode::Unknown
252 }
253 }