artifacts: implement musicbrainz tooling

This commit is contained in:
2026-06-16 11:32:15 +02:00
parent ac6cb425ac
commit d69ba43a6b
7 changed files with 177 additions and 56 deletions
+33 -10
View File
@@ -1,13 +1,17 @@
use std::collections::{HashMap, HashSet};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
pub mod bandcamp;
pub mod mixxx;
pub mod beets;
pub mod musicbrainz;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SourceID {
Bandcamp(u64),
Musicbrainz(String),
Mixxx,
Beets
}
@@ -20,7 +24,7 @@ pub struct Artist {
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<String>,
pub sources: Vec<SourceID>
pub sources: HashSet<SourceID>
}
impl PartialEq for Artist {
@@ -48,7 +52,7 @@ pub struct Album {
#[serde(skip_serializing_if = "Option::is_none")]
pub release_date: Option<DateTime<Utc>>,
pub sources: Vec<SourceID>
pub sources: HashSet<SourceID>
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -68,7 +72,7 @@ pub struct Track {
#[serde(skip_serializing_if = "Option::is_none")]
pub bpm: Option<f64>,
pub sources: Vec<SourceID>
pub sources: HashSet<SourceID>
}
impl PartialEq for Track {
@@ -109,28 +113,47 @@ macro_rules! merge_fields {
($this:tt, $that:tt, $($fields:tt),+) => {
$(
merge_fields!($this, $that, $fields);
for src in &$that.sources {
$this.sources.insert(src.clone());
}
)+
}
}
impl Artifact {
pub fn merge(&mut self, other: Artifact) {
impl Merge for Artifact {
fn merge(&mut self, other: Self) {
if *self != other {
return;
}
match (self, other) {
(Artifact::Track(this_track), Artifact::Track(that_track)) => {
(Self::Track(this_track), Self::Track(that_track)) => {
merge_fields!(this_track, that_track, album, label, year, artist, bpm);
// FIXME: genres
},
(Artifact::Album(this_album), Artifact::Album(that_album)) => {
(Self::Album(this_album), Self::Album(that_album)) => {
merge_fields!(this_album, that_album, about, credits, release_date);
},
(Artifact::Artist(this_artist), Artifact::Artist(that_artist)) => {
(Self::Artist(this_artist), Self::Artist(that_artist)) => {
merge_fields!(this_artist, that_artist, bio, location);
},
_ => ()
}
}
}
impl<M: Merge + PartialEq> Merge for Vec<M> {
fn merge(&mut self, other: Self) {
for artifact in other {
if let Some(merge_target) = self.iter_mut().find(|a| { **a == artifact }) {
merge_target.merge(artifact);
} else {
self.push(artifact);
}
}
}
}
pub trait Merge {
fn merge(&mut self, other: Self);
}