| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
use super::{BrowserState, ImportMode, NodeId, NodeType}; |
| 6 |
|
| 7 |
impl BrowserState { |
| 8 |
|
| 9 |
pub fn start_export_flow(&mut self, node_ids: Option<Vec<NodeId>>) { |
| 10 |
let Some(vfs_id) = self.current_vfs_id() else { |
| 11 |
self.status = "No VFS available".to_string(); |
| 12 |
return; |
| 13 |
}; |
| 14 |
|
| 15 |
let mut items = if let Some(ids) = node_ids { |
| 16 |
|
| 17 |
let mut all_items = Vec::new(); |
| 18 |
for id in ids { |
| 19 |
let Ok(node) = self.backend.get_node(id) else { |
| 20 |
continue; |
| 21 |
}; |
| 22 |
match node.node_type { |
| 23 |
NodeType::Directory => { |
| 24 |
if let Ok(sub_items) = self.backend.collect_export_items(vfs_id, Some(id)) { |
| 25 |
all_items.extend(sub_items); |
| 26 |
} |
| 27 |
} |
| 28 |
NodeType::Sample => { |
| 29 |
if let Some(hash) = &node.sample_hash { |
| 30 |
let ext = self.backend.sample_extension(hash).unwrap_or_default(); |
| 31 |
all_items.push(audiofiles_core::export::ExportItem { |
| 32 |
hash: hash.clone(), |
| 33 |
ext, |
| 34 |
relative_path: std::path::PathBuf::from(&node.name), |
| 35 |
name: node.name.clone(), |
| 36 |
bpm: None, |
| 37 |
musical_key: None, |
| 38 |
classification: None, |
| 39 |
duration: None, |
| 40 |
tags: Vec::new(), |
| 41 |
source_path: None, |
| 42 |
}); |
| 43 |
} |
| 44 |
} |
| 45 |
} |
| 46 |
} |
| 47 |
all_items |
| 48 |
} else if self.collections_ui.active_collection.is_some() { |
| 49 |
|
| 50 |
self.nav |
| 51 |
.contents |
| 52 |
.iter() |
| 53 |
.filter_map(|node| { |
| 54 |
let hash = node.node.sample_hash.as_ref()?; |
| 55 |
let ext = self.backend.sample_extension(hash).unwrap_or_default(); |
| 56 |
Some(audiofiles_core::export::ExportItem { |
| 57 |
hash: hash.clone(), |
| 58 |
ext, |
| 59 |
relative_path: std::path::PathBuf::from(&node.node.name), |
| 60 |
name: node.node.name.clone(), |
| 61 |
bpm: node.bpm, |
| 62 |
musical_key: node.musical_key.clone(), |
| 63 |
classification: node.classification.clone(), |
| 64 |
duration: node.duration, |
| 65 |
tags: node.tags.clone(), |
| 66 |
source_path: None, |
| 67 |
}) |
| 68 |
}) |
| 69 |
.collect() |
| 70 |
} else { |
| 71 |
|
| 72 |
self.backend |
| 73 |
.collect_export_items(vfs_id, self.nav.current_dir) |
| 74 |
.unwrap_or_default() |
| 75 |
}; |
| 76 |
|
| 77 |
super::log_backend_err( |
| 78 |
"enrich_export_with_tags", |
| 79 |
self.backend.enrich_export_with_tags(&mut items), |
| 80 |
); |
| 81 |
|
| 82 |
if items.is_empty() { |
| 83 |
self.status = "No samples to export".to_string(); |
| 84 |
return; |
| 85 |
} |
| 86 |
|
| 87 |
let default_dest = dirs::download_dir() |
| 88 |
.or_else(dirs::home_dir) |
| 89 |
.unwrap_or_else(|| std::path::PathBuf::from(".")) |
| 90 |
.join("audiofiles Export"); |
| 91 |
|
| 92 |
let available_profiles = self.backend.list_device_profiles().unwrap_or_default(); |
| 93 |
|
| 94 |
self.import_wf.import_mode = ImportMode::ConfigureExport { |
| 95 |
items, |
| 96 |
config: audiofiles_core::export::ExportConfig { |
| 97 |
format: audiofiles_core::export::ExportFormat::Original, |
| 98 |
sample_rate: None, |
| 99 |
bit_depth: None, |
| 100 |
channels: audiofiles_core::export::ExportChannels::Original, |
| 101 |
naming_pattern: None, |
| 102 |
flatten: false, |
| 103 |
metadata_sidecar: false, |
| 104 |
destination: default_dest, |
| 105 |
device_profile: None, |
| 106 |
naming_rules: None, |
| 107 |
max_file_size_bytes: None, |
| 108 |
name_overrides: None, |
| 109 |
}, |
| 110 |
available_profiles, |
| 111 |
}; |
| 112 |
} |
| 113 |
|
| 114 |
|
| 115 |
pub fn run_export( |
| 116 |
&mut self, |
| 117 |
items: Vec<audiofiles_core::export::ExportItem>, |
| 118 |
config: audiofiles_core::export::ExportConfig, |
| 119 |
) { |
| 120 |
|
| 121 |
|
| 122 |
|
| 123 |
|
| 124 |
self.import_wf.last_export_destination = Some(config.destination.clone()); |
| 125 |
self.import_wf.operation_progress = Some(crate::state::OperationProgress::new()); |
| 126 |
super::log_backend_err("start_export", self.backend.start_export(items, config)); |
| 127 |
|
| 128 |
self.import_wf.import_mode = ImportMode::Exporting { |
| 129 |
completed: 0, |
| 130 |
total: 0, |
| 131 |
current_name: String::new(), |
| 132 |
}; |
| 133 |
} |
| 134 |
|
| 135 |
|
| 136 |
pub fn cancel_cleanup(&mut self) { |
| 137 |
let _ = self.backend.cancel_cleanup(); |
| 138 |
self.import_wf.import_mode = ImportMode::None; |
| 139 |
self.refresh_vfs_list(); |
| 140 |
self.status = "Cleanup cancelled".to_string(); |
| 141 |
} |
| 142 |
|
| 143 |
|
| 144 |
|
| 145 |
|
| 146 |
|
| 147 |
pub fn cancel_export(&mut self) { |
| 148 |
let progress = match &self.import_wf.import_mode { |
| 149 |
ImportMode::Exporting { |
| 150 |
completed, total, .. |
| 151 |
} if *total > 0 => Some((*completed, *total)), |
| 152 |
_ => None, |
| 153 |
}; |
| 154 |
let _ = self.backend.cancel_export(); |
| 155 |
self.status = "Export cancelled".to_string(); |
| 156 |
self.import_wf.import_mode = match progress { |
| 157 |
Some((completed, total)) => ImportMode::OperationCancelled { |
| 158 |
kind: crate::state::CancelKind::Export, |
| 159 |
completed, |
| 160 |
total, |
| 161 |
destination: self.import_wf.last_export_destination.clone(), |
| 162 |
}, |
| 163 |
None => ImportMode::None, |
| 164 |
}; |
| 165 |
} |
| 166 |
|
| 167 |
|
| 168 |
|
| 169 |
pub(super) fn on_export_progress( |
| 170 |
&mut self, |
| 171 |
completed: usize, |
| 172 |
total: usize, |
| 173 |
current_name: String, |
| 174 |
) { |
| 175 |
self.import_wf.import_mode = ImportMode::Exporting { |
| 176 |
completed, |
| 177 |
total, |
| 178 |
current_name, |
| 179 |
}; |
| 180 |
} |
| 181 |
|
| 182 |
|
| 183 |
|
| 184 |
pub(super) fn on_export_complete(&mut self, total: usize, errors: Vec<(String, String)>) { |
| 185 |
let error_count = errors.len(); |
| 186 |
if error_count == 0 { |
| 187 |
self.status = format!("Exported {total} files"); |
| 188 |
} else { |
| 189 |
self.status = format!("Exported {total} files ({error_count} errors)"); |
| 190 |
} |
| 191 |
self.import_wf.import_mode = ImportMode::ExportComplete { total, errors }; |
| 192 |
} |
| 193 |
|
| 194 |
|
| 195 |
|
| 196 |
pub(super) fn on_cleanup_progress( |
| 197 |
&mut self, |
| 198 |
completed: usize, |
| 199 |
total: usize, |
| 200 |
current_name: String, |
| 201 |
) { |
| 202 |
self.import_wf.import_mode = ImportMode::Cleaning { |
| 203 |
completed, |
| 204 |
total, |
| 205 |
current_name, |
| 206 |
}; |
| 207 |
} |
| 208 |
|
| 209 |
|
| 210 |
|
| 211 |
pub(super) fn on_cleanup_complete(&mut self, removed: usize, errors: usize) { |
| 212 |
self.refresh_vfs_list(); |
| 213 |
if errors > 0 { |
| 214 |
self.status = format!("Library deleted ({removed} samples removed, {errors} errors)"); |
| 215 |
} else if removed > 0 { |
| 216 |
self.status = format!("Library deleted ({removed} samples removed)"); |
| 217 |
} else { |
| 218 |
self.status = "Library deleted".to_string(); |
| 219 |
} |
| 220 |
self.import_wf.import_mode = ImportMode::None; |
| 221 |
} |
| 222 |
} |
| 223 |
|