diff --git a/src/library.rs b/src/library.rs index 5329b69..6a6736e 100644 --- a/src/library.rs +++ b/src/library.rs @@ -60,26 +60,22 @@ impl fmt::Display for LibrarySummary { } /// Add an artist to the watchlist by expanding into individual track wanted items. -/// -/// Fetches the artist's discography from the provider, then for each release -/// fetches the tracklist and adds each track as a separate Wanted item. pub async fn add_artist( conn: &DatabaseConnection, name: Option<&str>, musicbrainz_id: Option<&str>, provider: &impl MetadataProvider, + user_id: Option, ) -> WatchResult { let (resolved_name, resolved_mbid) = resolve_artist_info(name, musicbrainz_id, provider).await?; let artist = queries::artists::upsert(conn, &resolved_name, resolved_mbid.as_deref()).await?; - // Get artist MBID — either provided or from the search let artist_mbid = resolved_mbid.or_else(|| artist.musicbrainz_id.clone()); let artist_mbid = match artist_mbid { Some(mbid) => mbid, None => { - // Search for the artist to get MBID let results = provider .search_artist(&resolved_name, 1) .await @@ -95,7 +91,6 @@ pub async fn add_artist( tracing::info!(name = %resolved_name, mbid = %artist_mbid, "fetching discography"); - // Fetch all releases let releases = provider .get_artist_releases(&artist_mbid, 100) .await @@ -123,6 +118,7 @@ pub async fn add_artist( &resolved_name, &track.title, Some(&track.recording_mbid), + user_id, ) .await { @@ -141,19 +137,17 @@ pub async fn add_artist( } /// Add an album to the watchlist by expanding into individual track wanted items. -/// -/// Fetches the album's tracklist and adds each track as a separate Wanted item. pub async fn add_album( conn: &DatabaseConnection, artist_name: Option<&str>, album_name: Option<&str>, musicbrainz_id: Option<&str>, provider: &impl MetadataProvider, + user_id: Option, ) -> WatchResult { let (resolved_album, resolved_artist, resolved_mbid) = resolve_album_info(artist_name, album_name, musicbrainz_id, provider).await?; - // Get release MBID let release_mbid = match resolved_mbid { Some(mbid) => mbid, None => { @@ -185,6 +179,7 @@ pub async fn add_album( &resolved_artist, &track.title, Some(&track.recording_mbid), + user_id, ) .await { @@ -208,6 +203,7 @@ pub async fn add_track( title: Option<&str>, musicbrainz_id: Option<&str>, provider: &impl MetadataProvider, + user_id: Option, ) -> WatchResult { let (resolved_title, resolved_artist, resolved_mbid) = resolve_track_info(artist_name, title, musicbrainz_id, provider).await?; @@ -217,12 +213,15 @@ pub async fn add_track( let item = queries::wanted::add( conn, - ItemType::Track, - &resolved_title, - resolved_mbid.as_deref(), - Some(artist.id), - None, - None, + queries::wanted::AddWantedItem { + item_type: ItemType::Track, + name: &resolved_title, + musicbrainz_id: resolved_mbid.as_deref(), + artist_id: Some(artist.id), + album_id: None, + track_id: None, + user_id, + }, ) .await?; @@ -250,18 +249,22 @@ async fn add_track_inner( artist_name: &str, title: &str, recording_mbid: Option<&str>, + user_id: Option, ) -> WatchResult { let artist = queries::artists::upsert(conn, artist_name, None).await?; let is_owned = matching::track_is_owned(conn, artist_name, title).await?; let item = queries::wanted::add( conn, - ItemType::Track, - title, - recording_mbid, - Some(artist.id), - None, - None, + queries::wanted::AddWantedItem { + item_type: ItemType::Track, + name: title, + musicbrainz_id: recording_mbid, + artist_id: Some(artist.id), + album_id: None, + track_id: None, + user_id, + }, ) .await?; @@ -288,7 +291,6 @@ async fn resolve_artist_info( let mbid = mbid.ok_or_else(|| WatchError::Other("either a name or --mbid is required".into()))?; - // Search for artist by MBID to get the name let results = provider .search_artist(mbid, 1) .await @@ -385,12 +387,12 @@ pub async fn list_items( conn: &DatabaseConnection, status_filter: Option, artist_filter: Option<&str>, + user_id: Option, ) -> WatchResult> { - let items = queries::wanted::list(conn, status_filter).await?; + let items = queries::wanted::list(conn, status_filter, user_id).await?; let mut entries = Vec::new(); for item in items { - // Use the name field directly now let artist_name = if let Some(id) = item.artist_id { queries::artists::get_by_id(conn, id) .await @@ -400,7 +402,6 @@ pub async fn list_items( None }; - // Apply artist filter if provided if let Some(filter) = artist_filter { let filter_norm = matching::normalize(filter); let matches = artist_name @@ -434,7 +435,7 @@ pub async fn remove_item(conn: &DatabaseConnection, id: i32) -> WatchResult<()> /// Get a summary of the library state. pub async fn library_summary(conn: &DatabaseConnection) -> WatchResult { - let all = queries::wanted::list(conn, None).await?; + let all = queries::wanted::list(conn, None, None).await?; let mut summary = LibrarySummary::default(); for item in &all { diff --git a/src/main.rs b/src/main.rs index b2c0c58..ccec803 100644 --- a/src/main.rs +++ b/src/main.rs @@ -131,7 +131,7 @@ async fn main() -> anyhow::Result<()> { anyhow::bail!("provide either a name or --mbid"); } let summary = - add_artist(db.conn(), name.as_deref(), mbid.as_deref(), &mb_client).await?; + add_artist(db.conn(), name.as_deref(), mbid.as_deref(), &mb_client, None).await?; println!("Artist watch: {summary}"); } AddCommand::Album { @@ -148,6 +148,7 @@ async fn main() -> anyhow::Result<()> { album.as_deref(), mbid.as_deref(), &mb_client, + None, ) .await?; println!("Album watch: {summary}"); @@ -166,6 +167,7 @@ async fn main() -> anyhow::Result<()> { title.as_deref(), mbid.as_deref(), &mb_client, + None, ) .await?; println!( @@ -179,7 +181,7 @@ async fn main() -> anyhow::Result<()> { }, Commands::List { status, artist } => { let status_filter = status.as_deref().map(parse_status).transpose()?; - let entries = list_items(db.conn(), status_filter, artist.as_deref()).await?; + let entries = list_items(db.conn(), status_filter, artist.as_deref(), None).await?; if entries.is_empty() { println!("Watchlist is empty."); diff --git a/tests/integration.rs b/tests/integration.rs index 70c06b7..ac60df6 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -118,7 +118,7 @@ async fn test_add_track_wanted() { let db = test_db().await; let provider = MockProvider; - let entry = add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider) + let entry = add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider, None) .await .unwrap(); assert_eq!(entry.item_type, ItemType::Track); @@ -133,7 +133,7 @@ async fn test_add_track_auto_owned() { insert_track(&db, "Pink Floyd", "Time", "DSOTM").await; - let entry = add_track(db.conn(), Some("Pink Floyd"), Some("Time"), None, &provider) + let entry = add_track(db.conn(), Some("Pink Floyd"), Some("Time"), None, &provider, None) .await .unwrap(); assert_eq!(entry.status, WantedStatus::Owned); @@ -150,12 +150,13 @@ async fn test_add_album_expands_to_tracks() { Some("Test Album"), None, &provider, + None, ) .await .unwrap(); assert_eq!(summary.tracks_added, 2); - let items = list_items(db.conn(), Some(WantedStatus::Wanted), None) + let items = list_items(db.conn(), Some(WantedStatus::Wanted), None, None) .await .unwrap(); assert_eq!(items.len(), 2); @@ -167,12 +168,12 @@ async fn test_add_artist_expands_to_tracks() { let db = test_db().await; let provider = MockProvider; - let summary = add_artist(db.conn(), Some("Test Artist"), None, &provider) + let summary = add_artist(db.conn(), Some("Test Artist"), None, &provider, None) .await .unwrap(); assert_eq!(summary.tracks_added, 2); - let items = list_items(db.conn(), None, None).await.unwrap(); + let items = list_items(db.conn(), None, None, None).await.unwrap(); assert_eq!(items.len(), 2); } @@ -181,22 +182,22 @@ async fn test_list_items_with_filters() { let db = test_db().await; let provider = MockProvider; - add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider) + add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider, None) .await .unwrap(); - add_track(db.conn(), Some("Tool"), Some("Lateralus"), None, &provider) + add_track(db.conn(), Some("Tool"), Some("Lateralus"), None, &provider, None) .await .unwrap(); - let all = list_items(db.conn(), None, None).await.unwrap(); + let all = list_items(db.conn(), None, None, None).await.unwrap(); assert_eq!(all.len(), 2); - let wanted = list_items(db.conn(), Some(WantedStatus::Wanted), None) + let wanted = list_items(db.conn(), Some(WantedStatus::Wanted), None, None) .await .unwrap(); assert_eq!(wanted.len(), 2); - let radiohead = list_items(db.conn(), None, Some("Radiohead")) + let radiohead = list_items(db.conn(), None, Some("Radiohead"), None) .await .unwrap(); assert_eq!(radiohead.len(), 1); @@ -207,11 +208,11 @@ async fn test_remove_item() { let db = test_db().await; let provider = MockProvider; - let entry = add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider) + let entry = add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider, None) .await .unwrap(); remove_item(db.conn(), entry.id).await.unwrap(); - let all = list_items(db.conn(), None, None).await.unwrap(); + let all = list_items(db.conn(), None, None, None).await.unwrap(); assert!(all.is_empty()); } @@ -222,10 +223,10 @@ async fn test_library_summary() { insert_track(&db, "Pink Floyd", "Time", "DSOTM").await; - add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider) + add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider, None) .await .unwrap(); - add_track(db.conn(), Some("Pink Floyd"), Some("Time"), None, &provider) + add_track(db.conn(), Some("Pink Floyd"), Some("Time"), None, &provider, None) .await .unwrap();