Compare commits
1 Commits
0b336789da
...
0f066d5708
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f066d5708 |
@@ -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<i32>,
|
||||
) -> WatchResult<AddSummary> {
|
||||
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<i32>,
|
||||
) -> WatchResult<AddSummary> {
|
||||
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<i32>,
|
||||
) -> WatchResult<WatchListEntry> {
|
||||
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<i32>,
|
||||
) -> WatchResult<bool> {
|
||||
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<WantedStatus>,
|
||||
artist_filter: Option<&str>,
|
||||
user_id: Option<i32>,
|
||||
) -> WatchResult<Vec<WatchListEntry>> {
|
||||
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<LibrarySummary> {
|
||||
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 {
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user