Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ece462f58 | |||
| 72a5fd3d14 | |||
| 0a0fa18bfa |
+4
-1
@@ -10,5 +10,8 @@ pub mod sanitize;
|
|||||||
pub mod template;
|
pub mod template;
|
||||||
|
|
||||||
pub use error::{OrgError, OrgResult};
|
pub use error::{OrgError, OrgResult};
|
||||||
pub use organizer::{OrgConfig, OrgStats, organize_from_db, organize_from_directory};
|
pub use organizer::{
|
||||||
|
OrgConfig, OrgStats, cleanup_empty_dirs, organize_from_db, organize_from_directory,
|
||||||
|
organize_track,
|
||||||
|
};
|
||||||
pub use template::DEFAULT_FORMAT;
|
pub use template::DEFAULT_FORMAT;
|
||||||
|
|||||||
+63
-3
@@ -105,10 +105,10 @@ fn move_or_copy(source: &Path, target: &Path, copy: bool) -> OrgResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove empty directories starting from `dir` and walking up, stopping at `stop_at`.
|
/// Remove empty directories starting from `dir` and walking up, stopping before `stop_at`.
|
||||||
fn cleanup_empty_dirs(dir: &Path, stop_at: &Path) {
|
pub fn cleanup_empty_dirs(dir: &Path, stop_at: &Path) {
|
||||||
let mut current = dir.to_owned();
|
let mut current = dir.to_owned();
|
||||||
while current != stop_at && current.starts_with(stop_at) {
|
while current.starts_with(stop_at) && current != *stop_at {
|
||||||
match std::fs::read_dir(¤t) {
|
match std::fs::read_dir(¤t) {
|
||||||
Ok(mut entries) => {
|
Ok(mut entries) => {
|
||||||
if entries.next().is_none() {
|
if entries.next().is_none() {
|
||||||
@@ -131,6 +131,66 @@ fn cleanup_empty_dirs(dir: &Path, stop_at: &Path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Organize a single track by ID. Returns `Ok(true)` if moved, `Ok(false)` if skipped.
|
||||||
|
pub async fn organize_track(
|
||||||
|
conn: &DatabaseConnection,
|
||||||
|
track_id: i32,
|
||||||
|
config: &OrgConfig,
|
||||||
|
) -> OrgResult<bool> {
|
||||||
|
let track = queries::tracks::get_by_id(conn, track_id).await?;
|
||||||
|
let source = Path::new(&track.file_path);
|
||||||
|
|
||||||
|
if !source.exists() {
|
||||||
|
tracing::warn!(path = %track.file_path, "source file does not exist");
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let meta = TrackMetadata::from_db_track(&track);
|
||||||
|
let relative = template::render(&config.format, &meta);
|
||||||
|
let target = config.target_dir.join(&relative);
|
||||||
|
|
||||||
|
// Canonicalize for comparison
|
||||||
|
let source_canon = source.canonicalize().unwrap_or_else(|_| source.to_owned());
|
||||||
|
let target_parent = target.parent().unwrap_or(Path::new("."));
|
||||||
|
if target_parent.exists() {
|
||||||
|
let target_canon = target_parent
|
||||||
|
.canonicalize()
|
||||||
|
.map(|p| p.join(target.file_name().unwrap_or_default()))
|
||||||
|
.unwrap_or_else(|_| target.clone());
|
||||||
|
if source_canon == target_canon {
|
||||||
|
tracing::debug!(path = %track.file_path, "already in place, skipping");
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let final_target = resolve_target(&target);
|
||||||
|
|
||||||
|
if config.dry_run {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let source_dir = source.parent().map(Path::to_owned);
|
||||||
|
move_or_copy(source, &final_target, config.copy)?;
|
||||||
|
|
||||||
|
// Update file_path in DB
|
||||||
|
let new_path = final_target.to_string_lossy().to_string();
|
||||||
|
let active = shanty_db::entities::track::ActiveModel {
|
||||||
|
id: Set(track.id),
|
||||||
|
file_path: Set(new_path),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
queries::tracks::update_metadata(conn, track.id, active).await?;
|
||||||
|
|
||||||
|
// Cleanup empty source directories up to the library root
|
||||||
|
if !config.copy
|
||||||
|
&& let Some(dir) = source_dir
|
||||||
|
{
|
||||||
|
cleanup_empty_dirs(&dir, &config.target_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
/// Organize all tracks from the database.
|
/// Organize all tracks from the database.
|
||||||
pub async fn organize_from_db(
|
pub async fn organize_from_db(
|
||||||
conn: &DatabaseConnection,
|
conn: &DatabaseConnection,
|
||||||
|
|||||||
Reference in New Issue
Block a user