Added merge
This commit is contained in:
@@ -714,11 +714,6 @@ pub async fn set_album_artist(pool: &PgPool, album_id: i64, artist_id: i64) -> R
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_albums_to_artist(pool: &PgPool, from_artist_id: i64, to_artist_id: i64) -> Result<(), sqlx::Error> {
|
||||
sqlx::query("UPDATE albums SET artist_id = $2 WHERE artist_id = $1")
|
||||
.bind(from_artist_id).bind(to_artist_id).execute(pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn move_track_artists(pool: &PgPool, from_artist_id: i64, to_artist_id: i64) -> Result<(), sqlx::Error> {
|
||||
// Update, but avoid duplicate (track_id, artist_id, role) - delete first any conflicting rows
|
||||
@@ -772,3 +767,14 @@ pub async fn update_track_storage_path(pool: &PgPool, track_id: i64, new_path: &
|
||||
.bind(track_id).bind(new_path).execute(pool).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_albums_for_artist(pool: &PgPool, artist_id: i64) -> Result<Vec<Album>, sqlx::Error> {
|
||||
sqlx::query_as::<_, Album>("SELECT * FROM albums WHERE artist_id = $1 ORDER BY year NULLS LAST, name")
|
||||
.bind(artist_id).fetch_all(pool).await
|
||||
}
|
||||
|
||||
pub async fn find_album_by_artist_id_and_name(pool: &PgPool, artist_id: i64, name: &str) -> Result<Option<i64>, sqlx::Error> {
|
||||
let row: Option<(i64,)> = sqlx::query_as("SELECT id FROM albums WHERE artist_id = $1 AND name = $2")
|
||||
.bind(artist_id).bind(name).fetch_optional(pool).await?;
|
||||
Ok(row.map(|r| r.0))
|
||||
}
|
||||
|
||||
@@ -102,30 +102,35 @@ pub async fn execute_merge(state: &Arc<AppState>, merge_id: Uuid) -> anyhow::Res
|
||||
// 2. Process album mappings
|
||||
for mapping in &proposal.album_mappings {
|
||||
if let Some(target_id) = mapping.merge_into_album_id {
|
||||
// Remove duplicate tracks (same file_hash in both albums)
|
||||
let dup_ids = db::get_duplicate_track_ids_in_albums(&state.pool, mapping.source_album_id, target_id).await?;
|
||||
for dup_id in dup_ids {
|
||||
if let Ok(Some(path)) = db::get_track_storage_path(&state.pool, dup_id).await {
|
||||
let p = std::path::Path::new(&path);
|
||||
if p.exists() {
|
||||
let _ = tokio::fs::remove_file(p).await;
|
||||
}
|
||||
}
|
||||
db::delete_track(&state.pool, dup_id).await?;
|
||||
}
|
||||
// Move remaining tracks to target album
|
||||
db::move_tracks_to_album(&state.pool, mapping.source_album_id, target_id).await?;
|
||||
db::delete_album(&state.pool, mapping.source_album_id).await?;
|
||||
merge_albums_into(&state.pool, mapping.source_album_id, target_id).await?;
|
||||
} else {
|
||||
// Rename album and move to winner artist
|
||||
// Rename album, then move to winner — merging if winner already has same name
|
||||
db::rename_album(&state.pool, mapping.source_album_id, &mapping.canonical_name).await?;
|
||||
db::set_album_artist(&state.pool, mapping.source_album_id, proposal.winner_artist_id).await?;
|
||||
if let Some(existing_id) = db::find_album_by_artist_id_and_name(
|
||||
&state.pool, proposal.winner_artist_id, &mapping.canonical_name,
|
||||
).await? {
|
||||
if existing_id != mapping.source_album_id {
|
||||
merge_albums_into(&state.pool, mapping.source_album_id, existing_id).await?;
|
||||
}
|
||||
// else: source_album already belongs to winner with that name — nothing to do
|
||||
} else {
|
||||
db::set_album_artist(&state.pool, mapping.source_album_id, proposal.winner_artist_id).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Move remaining albums from losers to winner
|
||||
// 3. Move remaining albums from losers to winner, merging name conflicts
|
||||
for &loser_id in &loser_ids {
|
||||
db::move_albums_to_artist(&state.pool, loser_id, proposal.winner_artist_id).await?;
|
||||
let albums = db::get_albums_for_artist(&state.pool, loser_id).await?;
|
||||
for album in albums {
|
||||
if let Some(existing_id) = db::find_album_by_artist_id_and_name(
|
||||
&state.pool, proposal.winner_artist_id, &album.name,
|
||||
).await? {
|
||||
merge_albums_into(&state.pool, album.id, existing_id).await?;
|
||||
} else {
|
||||
db::set_album_artist(&state.pool, album.id, proposal.winner_artist_id).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Move track_artists from losers to winner
|
||||
@@ -174,6 +179,23 @@ pub async fn execute_merge(state: &Arc<AppState>, merge_id: Uuid) -> anyhow::Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Merge source album into target: deduplicate tracks, move the rest, delete source.
|
||||
async fn merge_albums_into(pool: &sqlx::PgPool, source_id: i64, target_id: i64) -> anyhow::Result<()> {
|
||||
let dup_ids = db::get_duplicate_track_ids_in_albums(pool, source_id, target_id).await?;
|
||||
for dup_id in dup_ids {
|
||||
if let Ok(Some(path)) = db::get_track_storage_path(pool, dup_id).await {
|
||||
let p = std::path::Path::new(&path);
|
||||
if p.exists() {
|
||||
let _ = tokio::fs::remove_file(p).await;
|
||||
}
|
||||
}
|
||||
db::delete_track(pool, dup_id).await?;
|
||||
}
|
||||
db::move_tracks_to_album(pool, source_id, target_id).await?;
|
||||
db::delete_album(pool, source_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sanitize(name: &str) -> String {
|
||||
name.chars()
|
||||
.map(|c| match c {
|
||||
|
||||
Reference in New Issue
Block a user