selene_core/library/
clean.rs1use std::sync::Arc;
2
3use barber::{ProgressBar, ProgressRenderer};
4use lunar_lib::database::{Database, DbHandle, TransactionError, db_transaction};
5use thiserror::Error;
6
7use crate::{
8 database::LibraryDb,
9 library::{hash_source_files, track::Track},
10};
11
12#[derive(Debug, Error)]
13pub enum OrphanRelinkError {
14 #[error("IoError: {0}")]
15 Io(#[from] std::io::Error),
16
17 #[error("Transaction Error: {0}")]
18 Transaction(#[from] TransactionError),
19}
20
21pub fn relink_orphans(
22 mut check_tracks: Vec<Track>,
23 progress_renderer: Arc<dyn ProgressRenderer>,
24) -> Result<(), OrphanRelinkError> {
25 if check_tracks.is_empty() {
26 return Ok(());
27 }
28
29 let sources = hash_source_files(progress_renderer.clone())?;
30
31 check_tracks.retain(|t| {
32 let expected_source = t.container().path();
33 !expected_source.exists() || sources.get(&t.id()).is_some_and(|t| t != expected_source)
34 });
35
36 let progress_bar = ProgressBar::new(0, check_tracks.len(), progress_renderer);
37 progress_bar.set_label("Relinking orphaned sources...");
38
39 let db = DbHandle::<LibraryDb>::open().unwrap();
40 for mut track in check_tracks {
41 if let Some(source) = sources.get(&track.id()) {
42 track.set_source_file(source.to_owned());
43
44 db_transaction(
45 |cas_tx| cas_tx.tx_upsert(track.clone()).map_err(Into::into),
46 db.clone(),
47 false,
48 )
49 .map_err(TransactionError::from)?;
50
51 progress_bar.set_label(&format!(
52 "Relinked {} to {}",
53 track.metadata.safe_title(),
54 source.display()
55 ));
56 } else {
57 db.db()
58 .remove(*track.id())
59 .map_err(TransactionError::Sled)?;
60
61 progress_bar.set_label(&format!(
62 "Failed to relink {}: No source found. Removing from library",
63 track.metadata.safe_title()
64 ));
65 }
66
67 progress_bar.increment();
68 }
69 progress_bar.flush();
70 Ok(())
71}