Compare commits
4 Commits
0b336789da
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aef4708439 | ||
|
|
85e24671a3 | ||
|
|
15a4efe1e9 | ||
|
|
0f066d5708 |
@@ -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"] }
|
||||||
|
|||||||
@@ -60,26 +60,22 @@ impl fmt::Display for LibrarySummary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add an artist to the watchlist by expanding into individual track wanted items.
|
/// 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(
|
pub async fn add_artist(
|
||||||
conn: &DatabaseConnection,
|
conn: &DatabaseConnection,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
musicbrainz_id: Option<&str>,
|
musicbrainz_id: Option<&str>,
|
||||||
provider: &impl MetadataProvider,
|
provider: &impl MetadataProvider,
|
||||||
|
user_id: Option<i32>,
|
||||||
) -> WatchResult<AddSummary> {
|
) -> WatchResult<AddSummary> {
|
||||||
let (resolved_name, resolved_mbid) =
|
let (resolved_name, resolved_mbid) =
|
||||||
resolve_artist_info(name, musicbrainz_id, provider).await?;
|
resolve_artist_info(name, musicbrainz_id, provider).await?;
|
||||||
|
|
||||||
let artist = queries::artists::upsert(conn, &resolved_name, resolved_mbid.as_deref()).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 = resolved_mbid.or_else(|| artist.musicbrainz_id.clone());
|
||||||
let artist_mbid = match artist_mbid {
|
let artist_mbid = match artist_mbid {
|
||||||
Some(mbid) => mbid,
|
Some(mbid) => mbid,
|
||||||
None => {
|
None => {
|
||||||
// Search for the artist to get MBID
|
|
||||||
let results = provider
|
let results = provider
|
||||||
.search_artist(&resolved_name, 1)
|
.search_artist(&resolved_name, 1)
|
||||||
.await
|
.await
|
||||||
@@ -93,25 +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)");
|
||||||
|
|
||||||
// Fetch all releases
|
let release_groups = provider
|
||||||
let releases = provider
|
.get_artist_release_groups(&artist_mbid)
|
||||||
.get_artist_releases(&artist_mbid, 100)
|
|
||||||
.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;
|
||||||
}
|
}
|
||||||
@@ -123,6 +133,7 @@ pub async fn add_artist(
|
|||||||
&resolved_name,
|
&resolved_name,
|
||||||
&track.title,
|
&track.title,
|
||||||
Some(&track.recording_mbid),
|
Some(&track.recording_mbid),
|
||||||
|
user_id,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@@ -141,19 +152,17 @@ pub async fn add_artist(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add an album to the watchlist by expanding into individual track wanted items.
|
/// 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(
|
pub async fn add_album(
|
||||||
conn: &DatabaseConnection,
|
conn: &DatabaseConnection,
|
||||||
artist_name: Option<&str>,
|
artist_name: Option<&str>,
|
||||||
album_name: Option<&str>,
|
album_name: Option<&str>,
|
||||||
musicbrainz_id: Option<&str>,
|
musicbrainz_id: Option<&str>,
|
||||||
provider: &impl MetadataProvider,
|
provider: &impl MetadataProvider,
|
||||||
|
user_id: Option<i32>,
|
||||||
) -> WatchResult<AddSummary> {
|
) -> WatchResult<AddSummary> {
|
||||||
let (resolved_album, resolved_artist, resolved_mbid) =
|
let (resolved_album, resolved_artist, resolved_mbid) =
|
||||||
resolve_album_info(artist_name, album_name, musicbrainz_id, provider).await?;
|
resolve_album_info(artist_name, album_name, musicbrainz_id, provider).await?;
|
||||||
|
|
||||||
// Get release MBID
|
|
||||||
let release_mbid = match resolved_mbid {
|
let release_mbid = match resolved_mbid {
|
||||||
Some(mbid) => mbid,
|
Some(mbid) => mbid,
|
||||||
None => {
|
None => {
|
||||||
@@ -185,6 +194,7 @@ pub async fn add_album(
|
|||||||
&resolved_artist,
|
&resolved_artist,
|
||||||
&track.title,
|
&track.title,
|
||||||
Some(&track.recording_mbid),
|
Some(&track.recording_mbid),
|
||||||
|
user_id,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@@ -208,6 +218,7 @@ pub async fn add_track(
|
|||||||
title: Option<&str>,
|
title: Option<&str>,
|
||||||
musicbrainz_id: Option<&str>,
|
musicbrainz_id: Option<&str>,
|
||||||
provider: &impl MetadataProvider,
|
provider: &impl MetadataProvider,
|
||||||
|
user_id: Option<i32>,
|
||||||
) -> WatchResult<WatchListEntry> {
|
) -> WatchResult<WatchListEntry> {
|
||||||
let (resolved_title, resolved_artist, resolved_mbid) =
|
let (resolved_title, resolved_artist, resolved_mbid) =
|
||||||
resolve_track_info(artist_name, title, musicbrainz_id, provider).await?;
|
resolve_track_info(artist_name, title, musicbrainz_id, provider).await?;
|
||||||
@@ -217,12 +228,15 @@ pub async fn add_track(
|
|||||||
|
|
||||||
let item = queries::wanted::add(
|
let item = queries::wanted::add(
|
||||||
conn,
|
conn,
|
||||||
ItemType::Track,
|
queries::wanted::AddWantedItem {
|
||||||
&resolved_title,
|
item_type: ItemType::Track,
|
||||||
resolved_mbid.as_deref(),
|
name: &resolved_title,
|
||||||
Some(artist.id),
|
musicbrainz_id: resolved_mbid.as_deref(),
|
||||||
None,
|
artist_id: Some(artist.id),
|
||||||
None,
|
album_id: None,
|
||||||
|
track_id: None,
|
||||||
|
user_id,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -250,18 +264,22 @@ async fn add_track_inner(
|
|||||||
artist_name: &str,
|
artist_name: &str,
|
||||||
title: &str,
|
title: &str,
|
||||||
recording_mbid: Option<&str>,
|
recording_mbid: Option<&str>,
|
||||||
|
user_id: Option<i32>,
|
||||||
) -> WatchResult<bool> {
|
) -> WatchResult<bool> {
|
||||||
let artist = queries::artists::upsert(conn, artist_name, None).await?;
|
let artist = queries::artists::upsert(conn, artist_name, None).await?;
|
||||||
let is_owned = matching::track_is_owned(conn, artist_name, title).await?;
|
let is_owned = matching::track_is_owned(conn, artist_name, title).await?;
|
||||||
|
|
||||||
let item = queries::wanted::add(
|
let item = queries::wanted::add(
|
||||||
conn,
|
conn,
|
||||||
ItemType::Track,
|
queries::wanted::AddWantedItem {
|
||||||
title,
|
item_type: ItemType::Track,
|
||||||
recording_mbid,
|
name: title,
|
||||||
Some(artist.id),
|
musicbrainz_id: recording_mbid,
|
||||||
None,
|
artist_id: Some(artist.id),
|
||||||
None,
|
album_id: None,
|
||||||
|
track_id: None,
|
||||||
|
user_id,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -288,7 +306,6 @@ async fn resolve_artist_info(
|
|||||||
let mbid =
|
let mbid =
|
||||||
mbid.ok_or_else(|| WatchError::Other("either a name or --mbid is required".into()))?;
|
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
|
let results = provider
|
||||||
.search_artist(mbid, 1)
|
.search_artist(mbid, 1)
|
||||||
.await
|
.await
|
||||||
@@ -385,12 +402,12 @@ pub async fn list_items(
|
|||||||
conn: &DatabaseConnection,
|
conn: &DatabaseConnection,
|
||||||
status_filter: Option<WantedStatus>,
|
status_filter: Option<WantedStatus>,
|
||||||
artist_filter: Option<&str>,
|
artist_filter: Option<&str>,
|
||||||
|
user_id: Option<i32>,
|
||||||
) -> WatchResult<Vec<WatchListEntry>> {
|
) -> 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();
|
let mut entries = Vec::new();
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
// Use the name field directly now
|
|
||||||
let artist_name = if let Some(id) = item.artist_id {
|
let artist_name = if let Some(id) = item.artist_id {
|
||||||
queries::artists::get_by_id(conn, id)
|
queries::artists::get_by_id(conn, id)
|
||||||
.await
|
.await
|
||||||
@@ -400,7 +417,6 @@ pub async fn list_items(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply artist filter if provided
|
|
||||||
if let Some(filter) = artist_filter {
|
if let Some(filter) = artist_filter {
|
||||||
let filter_norm = matching::normalize(filter);
|
let filter_norm = matching::normalize(filter);
|
||||||
let matches = artist_name
|
let matches = artist_name
|
||||||
@@ -434,7 +450,7 @@ pub async fn remove_item(conn: &DatabaseConnection, id: i32) -> WatchResult<()>
|
|||||||
|
|
||||||
/// Get a summary of the library state.
|
/// Get a summary of the library state.
|
||||||
pub async fn library_summary(conn: &DatabaseConnection) -> WatchResult<LibrarySummary> {
|
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();
|
let mut summary = LibrarySummary::default();
|
||||||
for item in &all {
|
for item in &all {
|
||||||
|
|||||||
14
src/main.rs
14
src/main.rs
@@ -130,8 +130,14 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
if name.is_none() && mbid.is_none() {
|
if name.is_none() && mbid.is_none() {
|
||||||
anyhow::bail!("provide either a name or --mbid");
|
anyhow::bail!("provide either a name or --mbid");
|
||||||
}
|
}
|
||||||
let summary =
|
let summary = add_artist(
|
||||||
add_artist(db.conn(), name.as_deref(), mbid.as_deref(), &mb_client).await?;
|
db.conn(),
|
||||||
|
name.as_deref(),
|
||||||
|
mbid.as_deref(),
|
||||||
|
&mb_client,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
println!("Artist watch: {summary}");
|
println!("Artist watch: {summary}");
|
||||||
}
|
}
|
||||||
AddCommand::Album {
|
AddCommand::Album {
|
||||||
@@ -148,6 +154,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
album.as_deref(),
|
album.as_deref(),
|
||||||
mbid.as_deref(),
|
mbid.as_deref(),
|
||||||
&mb_client,
|
&mb_client,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
println!("Album watch: {summary}");
|
println!("Album watch: {summary}");
|
||||||
@@ -166,6 +173,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
title.as_deref(),
|
title.as_deref(),
|
||||||
mbid.as_deref(),
|
mbid.as_deref(),
|
||||||
&mb_client,
|
&mb_client,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
println!(
|
println!(
|
||||||
@@ -179,7 +187,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
},
|
},
|
||||||
Commands::List { status, artist } => {
|
Commands::List { status, artist } => {
|
||||||
let status_filter = status.as_deref().map(parse_status).transpose()?;
|
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() {
|
if entries.is_empty() {
|
||||||
println!("Watchlist is empty.");
|
println!("Watchlist is empty.");
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,9 +139,16 @@ async fn test_add_track_wanted() {
|
|||||||
let db = test_db().await;
|
let db = test_db().await;
|
||||||
let provider = MockProvider;
|
let provider = MockProvider;
|
||||||
|
|
||||||
let entry = add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider)
|
let entry = add_track(
|
||||||
.await
|
db.conn(),
|
||||||
.unwrap();
|
Some("Radiohead"),
|
||||||
|
Some("Creep"),
|
||||||
|
None,
|
||||||
|
&provider,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
assert_eq!(entry.item_type, ItemType::Track);
|
assert_eq!(entry.item_type, ItemType::Track);
|
||||||
assert_eq!(entry.name, "Creep");
|
assert_eq!(entry.name, "Creep");
|
||||||
assert_eq!(entry.status, WantedStatus::Wanted);
|
assert_eq!(entry.status, WantedStatus::Wanted);
|
||||||
@@ -133,9 +161,16 @@ async fn test_add_track_auto_owned() {
|
|||||||
|
|
||||||
insert_track(&db, "Pink Floyd", "Time", "DSOTM").await;
|
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(
|
||||||
.await
|
db.conn(),
|
||||||
.unwrap();
|
Some("Pink Floyd"),
|
||||||
|
Some("Time"),
|
||||||
|
None,
|
||||||
|
&provider,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
assert_eq!(entry.status, WantedStatus::Owned);
|
assert_eq!(entry.status, WantedStatus::Owned);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,12 +185,13 @@ async fn test_add_album_expands_to_tracks() {
|
|||||||
Some("Test Album"),
|
Some("Test Album"),
|
||||||
None,
|
None,
|
||||||
&provider,
|
&provider,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(summary.tracks_added, 2);
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(items.len(), 2);
|
assert_eq!(items.len(), 2);
|
||||||
@@ -167,12 +203,12 @@ async fn test_add_artist_expands_to_tracks() {
|
|||||||
let db = test_db().await;
|
let db = test_db().await;
|
||||||
let provider = MockProvider;
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(summary.tracks_added, 2);
|
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);
|
assert_eq!(items.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,22 +217,36 @@ async fn test_list_items_with_filters() {
|
|||||||
let db = test_db().await;
|
let db = test_db().await;
|
||||||
let provider = MockProvider;
|
let provider = MockProvider;
|
||||||
|
|
||||||
add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider)
|
add_track(
|
||||||
.await
|
db.conn(),
|
||||||
.unwrap();
|
Some("Radiohead"),
|
||||||
add_track(db.conn(), Some("Tool"), Some("Lateralus"), None, &provider)
|
Some("Creep"),
|
||||||
.await
|
None,
|
||||||
.unwrap();
|
&provider,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
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);
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(wanted.len(), 2);
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(radiohead.len(), 1);
|
assert_eq!(radiohead.len(), 1);
|
||||||
@@ -207,11 +257,18 @@ async fn test_remove_item() {
|
|||||||
let db = test_db().await;
|
let db = test_db().await;
|
||||||
let provider = MockProvider;
|
let provider = MockProvider;
|
||||||
|
|
||||||
let entry = add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider)
|
let entry = add_track(
|
||||||
.await
|
db.conn(),
|
||||||
.unwrap();
|
Some("Radiohead"),
|
||||||
|
Some("Creep"),
|
||||||
|
None,
|
||||||
|
&provider,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
remove_item(db.conn(), entry.id).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());
|
assert!(all.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,12 +279,26 @@ async fn test_library_summary() {
|
|||||||
|
|
||||||
insert_track(&db, "Pink Floyd", "Time", "DSOTM").await;
|
insert_track(&db, "Pink Floyd", "Time", "DSOTM").await;
|
||||||
|
|
||||||
add_track(db.conn(), Some("Radiohead"), Some("Creep"), None, &provider)
|
add_track(
|
||||||
.await
|
db.conn(),
|
||||||
.unwrap();
|
Some("Radiohead"),
|
||||||
add_track(db.conn(), Some("Pink Floyd"), Some("Time"), None, &provider)
|
Some("Creep"),
|
||||||
.await
|
None,
|
||||||
.unwrap();
|
&provider,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
add_track(
|
||||||
|
db.conn(),
|
||||||
|
Some("Pink Floyd"),
|
||||||
|
Some("Time"),
|
||||||
|
None,
|
||||||
|
&provider,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let summary = library_summary(db.conn()).await.unwrap();
|
let summary = library_summary(db.conn()).await.unwrap();
|
||||||
assert_eq!(summary.total_items, 2);
|
assert_eq!(summary.total_items, 2);
|
||||||
|
|||||||
Reference in New Issue
Block a user