aboutsummaryrefslogtreecommitdiff
path: root/import
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-04-25 21:34:59 +0200
committermetamuffin <metamuffin@disroot.org>2025-04-25 21:34:59 +0200
commit2c6f753797b8e223c5982c4790f1be8b08fe63d6 (patch)
tree32b77bae2fd83927bc37dbbf3fa666e10beb4703 /import
parentb84ac0b04c45aa353ba1a302f96fa5f0f5895d59 (diff)
downloadjellything-2c6f753797b8e223c5982c4790f1be8b08fe63d6.tar
jellything-2c6f753797b8e223c5982c4790f1be8b08fe63d6.tar.bz2
jellything-2c6f753797b8e223c5982c4790f1be8b08fe63d6.tar.zst
images from wikidata works
Diffstat (limited to 'import')
-rw-r--r--import/src/lib.rs72
-rw-r--r--import/src/musicbrainz.rs107
2 files changed, 155 insertions, 24 deletions
diff --git a/import/src/lib.rs b/import/src/lib.rs
index 0f89ab0..fa74b9c 100644
--- a/import/src/lib.rs
+++ b/import/src/lib.rs
@@ -700,26 +700,60 @@ fn apply_musicbrainz_recording(
for rel in &rec.relations {
use musicbrainz::reltypes::*;
- match rel.type_id.as_str() {
- INSTRUMENT => {
- node.people
- .entry(PeopleGroup::Instrument)
- .or_default()
- .push(Appearance {
- jobs: rel.attributes.clone(),
- characters: vec![],
- person: Person {
- name: rel
- .artist
- .as_ref()
- .map(|a| a.name.clone())
- .unwrap_or_default(),
- headshot: None,
- ids: ObjectIds::default(),
- },
- });
+ let a = match rel.type_id.as_str() {
+ INSTRUMENT => Some(("", PeopleGroup::Instrument)),
+ VOCAL => Some(("", PeopleGroup::Vocal)),
+ PRODUCER => Some(("", PeopleGroup::Producer)),
+ MIX => Some(("mix ", PeopleGroup::Engineer)),
+ PHONOGRAPHIC_COPYRIGHT => Some(("phonographic copyright ", PeopleGroup::Engineer)),
+ PROGRAMMING => Some(("programming ", PeopleGroup::Engineer)),
+ _ => None,
+ };
+
+ if let Some((note, group)) = a {
+ let artist = rel.artist.as_ref().unwrap();
+
+ let artist =
+ rthandle.block_on(apis.musicbrainz.lookup_artist(artist.id.clone()))?;
+
+ let mut image = None;
+
+ for rel in &artist.relations {
+ match rel.type_id.as_str() {
+ WIKIDATA => {
+ let url = rel.url.as_ref().unwrap().resource.clone();
+ if let Some(id) = url.strip_prefix("https://www.wikidata.org/wiki/") {
+ if let Some(filename) = rthandle
+ .block_on(apis.wikidata.query_image_path(id.to_owned()))?
+ {
+ let path = rthandle.block_on(
+ apis.wikimedia_commons.image_by_filename(filename),
+ )?;
+ image = Some(AssetInner::Cache(path).ser());
+ }
+ }
+ }
+ _ => (),
+ }
}
- _ => (),
+ let mut jobs = vec![];
+ if !note.is_empty() {
+ jobs.push(note.to_string());
+ }
+ jobs.extend(rel.attributes.clone());
+ node.people.entry(group).or_default().push(Appearance {
+ jobs,
+ characters: vec![],
+ person: Person {
+ name: if rel.target_credit.is_empty() {
+ artist.name.clone()
+ } else {
+ rel.target_credit.clone()
+ },
+ headshot: image,
+ ids: ObjectIds::default(),
+ },
+ });
}
}
diff --git a/import/src/musicbrainz.rs b/import/src/musicbrainz.rs
index 0fe839b..704807b 100644
--- a/import/src/musicbrainz.rs
+++ b/import/src/musicbrainz.rs
@@ -55,6 +55,24 @@ pub struct MbRecordingRel {
#[derive(Debug, Deserialize, Encode, Decode)]
#[serde(rename_all = "kebab-case")]
+pub struct MbArtistRel {
+ pub id: String,
+ pub isnis: Vec<String>,
+ pub ipis: Vec<String>,
+ pub name: String,
+ pub disambiguation: String,
+ pub country: Option<String>,
+ pub sort_name: String,
+ pub gender_id: Option<String>,
+ pub area: Option<MbArea>,
+ pub begin_area: Option<MbArea>,
+ pub end_area: Option<MbArea>,
+ pub life_span: MbTimespan,
+ pub relations: Vec<MbRelation>,
+}
+
+#[derive(Debug, Deserialize, Encode, Decode)]
+#[serde(rename_all = "kebab-case")]
pub struct MbArtistCredit {
pub name: String,
pub artist: MbArtist,
@@ -81,6 +99,7 @@ pub struct MbRelation {
pub url: Option<MbUrl>,
pub recording: Option<MbRecording>,
pub series: Option<MbSeries>,
+ pub event: Option<MbEvent>,
}
#[derive(Debug, Deserialize, Encode, Decode)]
@@ -98,10 +117,12 @@ pub struct MbSeries {
pub struct MbRecording {
pub id: String,
pub title: String,
+ #[serde(default)]
pub isrcs: Vec<String>,
pub video: bool,
pub disambiguation: String,
- pub length: u32,
+ pub length: Option<u32>,
+ #[serde(default)]
pub artist_credit: Vec<MbArtistCredit>,
}
@@ -109,8 +130,8 @@ pub struct MbRecording {
#[serde(rename_all = "kebab-case")]
pub struct MbWork {
pub id: String,
- pub r#type: String,
- pub type_id: String,
+ pub r#type: Option<String>,
+ pub type_id: Option<String>,
pub languages: Vec<String>,
pub iswcs: Vec<String>,
pub language: Option<String>,
@@ -121,6 +142,19 @@ pub struct MbWork {
#[derive(Debug, Deserialize, Encode, Decode)]
#[serde(rename_all = "kebab-case")]
+pub struct MbEvent {
+ pub id: String,
+ pub r#type: String,
+ pub type_id: String,
+ pub name: String,
+ pub time: String,
+ pub cancelled: bool,
+ pub setlist: String,
+ pub life_span: MbTimespan,
+}
+
+#[derive(Debug, Deserialize, Encode, Decode)]
+#[serde(rename_all = "kebab-case")]
pub struct MbArtist {
pub id: String,
pub r#type: Option<String>,
@@ -133,9 +167,28 @@ pub struct MbArtist {
#[derive(Debug, Deserialize, Encode, Decode)]
#[serde(rename_all = "kebab-case")]
+pub struct MbTimespan {
+ pub begin: Option<String>,
+ pub end: Option<String>,
+ pub ended: bool,
+}
+
+#[derive(Debug, Deserialize, Encode, Decode)]
+#[serde(rename_all = "kebab-case")]
+pub struct MbArea {
+ pub name: String,
+ pub sort_name: String,
+ #[serde(default)]
+ pub iso_3166_1_codes: Vec<String>,
+ pub id: String,
+ pub disambiguation: String,
+}
+
+#[derive(Debug, Deserialize, Encode, Decode)]
+#[serde(rename_all = "kebab-case")]
pub struct MbUrl {
- id: String,
- resource: String,
+ pub id: String,
+ pub resource: String,
}
impl MusicBrainz {
@@ -206,4 +259,48 @@ impl MusicBrainz {
})
.await
}
+
+ pub async fn lookup_artist(&self, id: String) -> Result<Arc<MbArtistRel>> {
+ async_cache_memory("api-musicbrainz-artist", id.clone(), || async move {
+ let _permit = self.rate_limit.clone().acquire_owned().await?;
+ let permit_drop_ts = Instant::now() + Duration::from_secs(10);
+ info!("artist lookup: {id}");
+
+ let inc = [
+ "area-rels",
+ "artist-rels",
+ "event-rels",
+ "genre-rels",
+ "instrument-rels",
+ "label-rels",
+ "place-rels",
+ "recording-rels",
+ "release-rels",
+ "release-group-rels",
+ "series-rels",
+ "url-rels",
+ "work-rels",
+ ]
+ .join("+");
+
+ let resp = self
+ .client
+ .get(format!(
+ "https://musicbrainz.org/ws/2/artist/{id}?inc={inc}"
+ ))
+ .send()
+ .await?
+ .error_for_status()?
+ .json::<MbArtistRel>()
+ .await?;
+
+ tokio::task::spawn(async move {
+ sleep_until(permit_drop_ts).await;
+ drop(_permit);
+ });
+
+ Ok(resp)
+ })
+ .await
+ }
}