Compare commits

..

2 Commits

Author SHA1 Message Date
Connor Johnstone
aef4708439 Sped up artist enrichment at least somewhat 2026-03-21 15:08:28 -04:00
Connor Johnstone
85e24671a3 Added the watch and scheduler systems 2026-03-20 16:28:15 -04:00
3 changed files with 56 additions and 19 deletions

View File

@@ -24,4 +24,5 @@ chrono = { version = "0.4", features = ["serde"] }
dirs = "6" dirs = "6"
[dev-dependencies] [dev-dependencies]
shanty-data = { path = "../shanty-data" }
tokio = { version = "1", features = ["full", "test-util"] } tokio = { version = "1", features = ["full", "test-util"] }

View File

@@ -89,24 +89,39 @@ pub async fn add_artist(
} }
}; };
tracing::info!(name = %resolved_name, mbid = %artist_mbid, "fetching discography"); tracing::info!(name = %resolved_name, mbid = %artist_mbid, "fetching discography (release groups)");
let releases = provider let release_groups = provider
.get_artist_releases(&artist_mbid, 100) .get_artist_release_groups(&artist_mbid)
.await .await
.map_err(|e| WatchError::Other(format!("failed to fetch discography: {e}")))?; .map_err(|e| WatchError::Other(format!("failed to fetch release groups: {e}")))?;
tracing::info!(count = releases.len(), "found releases"); tracing::info!(count = release_groups.len(), "found release groups");
let mut summary = AddSummary::default(); let mut summary = AddSummary::default();
for release in &releases { for rg in &release_groups {
tracing::info!(title = %release.title, mbid = %release.mbid, "fetching tracks"); // Resolve a concrete release MBID from the release group
let release_mbid = if let Some(ref rid) = rg.first_release_mbid {
rid.clone()
} else {
// Browse releases for this release group to find a concrete release
match provider.resolve_release_from_group(&rg.mbid).await {
Ok(rid) => rid,
Err(e) => {
tracing::warn!(rg = %rg.title, rg_id = %rg.mbid, error = %e, "failed to resolve release from group");
summary.errors += 1;
continue;
}
}
};
let tracks = match provider.get_release_tracks(&release.mbid).await { tracing::info!(title = %rg.title, release_mbid = %release_mbid, "fetching tracks for release group");
let tracks = match provider.get_release_tracks(&release_mbid).await {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
tracing::warn!(release = %release.title, error = %e, "failed to fetch tracks"); tracing::warn!(rg = %rg.title, error = %e, "failed to fetch tracks");
summary.errors += 1; summary.errors += 1;
continue; continue;
} }

View File

@@ -1,9 +1,9 @@
use chrono::Utc; use chrono::Utc;
use sea_orm::ActiveValue::Set; use sea_orm::ActiveValue::Set;
use shanty_data::DataResult;
use shanty_db::entities::wanted_item::{ItemType, WantedStatus}; use shanty_db::entities::wanted_item::{ItemType, WantedStatus};
use shanty_db::{Database, queries}; use shanty_db::{Database, queries};
use shanty_tag::error::TagResult;
use shanty_tag::provider::*; use shanty_tag::provider::*;
use shanty_watch::{add_album, add_artist, add_track, library_summary, list_items, remove_item}; use shanty_watch::{add_album, add_artist, add_track, library_summary, list_items, remove_item};
@@ -46,10 +46,10 @@ impl MetadataProvider for MockProvider {
&self, &self,
_artist: &str, _artist: &str,
_title: &str, _title: &str,
) -> TagResult<Vec<RecordingMatch>> { ) -> DataResult<Vec<RecordingMatch>> {
Ok(vec![]) Ok(vec![])
} }
async fn search_release(&self, _artist: &str, _album: &str) -> TagResult<Vec<ReleaseMatch>> { async fn search_release(&self, _artist: &str, _album: &str) -> DataResult<Vec<ReleaseMatch>> {
Ok(vec![ReleaseMatch { Ok(vec![ReleaseMatch {
mbid: "release-123".into(), mbid: "release-123".into(),
title: "Test Album".into(), title: "Test Album".into(),
@@ -60,10 +60,14 @@ impl MetadataProvider for MockProvider {
score: 100, score: 100,
}]) }])
} }
async fn get_recording(&self, _mbid: &str) -> TagResult<RecordingDetails> { async fn get_recording(&self, _mbid: &str) -> DataResult<RecordingDetails> {
Err(shanty_tag::TagError::Other("not found".into())) Err(shanty_data::DataError::Other("not found".into()))
} }
async fn search_artist(&self, _query: &str, _limit: u32) -> TagResult<Vec<ArtistSearchResult>> { async fn search_artist(
&self,
_query: &str,
_limit: u32,
) -> DataResult<Vec<ArtistSearchResult>> {
Ok(vec![ArtistSearchResult { Ok(vec![ArtistSearchResult {
mbid: "artist-456".into(), mbid: "artist-456".into(),
name: "Test Artist".into(), name: "Test Artist".into(),
@@ -77,7 +81,7 @@ impl MetadataProvider for MockProvider {
&self, &self,
_mbid: &str, _mbid: &str,
_limit: u32, _limit: u32,
) -> TagResult<Vec<DiscographyEntry>> { ) -> DataResult<Vec<DiscographyEntry>> {
Ok(vec![DiscographyEntry { Ok(vec![DiscographyEntry {
mbid: "release-123".into(), mbid: "release-123".into(),
title: "Test Album".into(), title: "Test Album".into(),
@@ -86,7 +90,7 @@ impl MetadataProvider for MockProvider {
track_count: Some(2), track_count: Some(2),
}]) }])
} }
async fn get_release_tracks(&self, _release_mbid: &str) -> TagResult<Vec<ReleaseTrack>> { async fn get_release_tracks(&self, _release_mbid: &str) -> DataResult<Vec<ReleaseTrack>> {
Ok(vec![ Ok(vec![
ReleaseTrack { ReleaseTrack {
recording_mbid: "rec-1".into(), recording_mbid: "rec-1".into(),
@@ -108,8 +112,25 @@ impl MetadataProvider for MockProvider {
async fn get_artist_release_groups( async fn get_artist_release_groups(
&self, &self,
_artist_mbid: &str, _artist_mbid: &str,
) -> TagResult<Vec<shanty_tag::provider::ReleaseGroupEntry>> { ) -> DataResult<Vec<shanty_tag::provider::ReleaseGroupEntry>> {
Ok(vec![]) Ok(vec![ReleaseGroupEntry {
mbid: "rg-123".into(),
title: "Test Album".into(),
primary_type: Some("Album".into()),
secondary_types: vec![],
first_release_date: Some("2024".into()),
first_release_mbid: Some("release-123".into()),
}])
}
async fn resolve_release_from_group(&self, release_group_mbid: &str) -> DataResult<String> {
if release_group_mbid == "rg-123" {
Ok("release-123".into())
} else {
Err(shanty_data::DataError::Other(format!(
"no releases for release-group {release_group_mbid}"
)))
}
} }
} }