Fix filtering of unowned albums

This commit is contained in:
Wojciech Kozlowski 2025-01-04 21:34:06 +01:00
parent b7779905bc
commit f3f3582601
2 changed files with 67 additions and 32 deletions

View File

@ -3,8 +3,8 @@ use crate::core::collection::album::{Album, AlbumOwnership, AlbumPrimaryType, Al
/// Filter for a specifying subsets of the entire collection (e.g., for display).
#[derive(Debug, Default)]
pub struct CollectionFilter {
pub include: Vec<AlbumField>,
pub except: Vec<AlbumField>,
pub include: Vec<Vec<AlbumField>>,
pub except: Vec<Vec<AlbumField>>,
}
#[derive(Debug)]
@ -16,11 +16,19 @@ pub enum AlbumField {
impl CollectionFilter {
pub fn filter_album(&self, album: &Album) -> bool {
let include = Self::filter_or(&self.include, album);
let except = Self::filter_or(&self.except, album);
let include = Self::filter_and(true, &self.include, album);
let except = Self::filter_and(false, &self.except, album);
include && !except
}
fn filter_and(empty: bool, group: &Vec<Vec<AlbumField>>, album: &Album) -> bool {
let mut filter = !group.is_empty() || empty;
for field in group.iter() {
filter = filter && Self::filter_or(field, album);
}
filter
}
fn filter_or(group: &Vec<AlbumField>, album: &Album) -> bool {
let mut filter = false;
for field in group.iter() {
@ -52,18 +60,22 @@ mod tests {
fn test_filter() -> CollectionFilter {
CollectionFilter {
include: vec![
include: vec![vec![
AlbumField::PrimaryType(None),
AlbumField::PrimaryType(Some(AlbumPrimaryType::Ep)),
AlbumField::PrimaryType(Some(AlbumPrimaryType::Album)),
],
AlbumField::Ownership(AlbumOwnership::Owned(TrackFormat::Mp3)),
AlbumField::Ownership(AlbumOwnership::Owned(TrackFormat::Flac)),
]],
except: vec![
AlbumField::Ownership(AlbumOwnership::None),
vec![AlbumField::Ownership(AlbumOwnership::None)],
vec![
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
AlbumField::SecondaryType(AlbumSecondaryType::Live),
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
],
],
}
}
@ -93,6 +105,9 @@ mod tests {
let filter = test_filter();
let mut album = test_album();
// Drop ownership so that filtering is truly only on type.
album.tracks.clear();
album.meta.info.primary_type = None;
assert!(filter.filter_album(&album));
@ -121,29 +136,42 @@ mod tests {
types.push(AlbumSecondaryType::AudioDrama);
assert!(filter.filter_album(&album));
// Filtered type.
// Filtered type. But album is owned so it remains.
let types = &mut album.meta.info.secondary_types;
types.push(AlbumSecondaryType::Live);
assert!(!filter.filter_album(&album));
assert_ne!(album.get_ownership(), AlbumOwnership::None);
assert!(filter.filter_album(&album));
// Remove non-filtered type.
album.meta.info.secondary_types.remove(0);
assert!(!filter.filter_album(&album));
assert!(filter.filter_album(&album));
// Add another filtered type.
let types = &mut album.meta.info.secondary_types;
types.push(AlbumSecondaryType::Soundtrack);
assert!(filter.filter_album(&album));
// Drop ownership and this should be now excluded.
album.tracks.clear();
assert_eq!(album.get_ownership(), AlbumOwnership::None);
assert!(!filter.filter_album(&album));
}
#[test]
fn filter_ownership() {
let filter = test_filter();
let mut album = Album::new(AlbumId::new("An Album"));
let mut album = test_album();
// It is an album though so it should remain included.
album.tracks.clear();
assert_eq!(album.get_ownership(), AlbumOwnership::None);
assert!(filter.filter_album(&album));
// Change to unincluded primary type.
album.meta.info.primary_type = Some(AlbumPrimaryType::Other);
assert!(!filter.filter_album(&album));
// Changing ownership should make it go back to being included.
album.tracks.push(test_track());
assert_eq!(
album.get_ownership(),

View File

@ -6,7 +6,10 @@ use ratatui::{backend::CrosstermBackend, Terminal};
use structopt::StructOpt;
use musichoard::{
collection::album::{AlbumOwnership, AlbumPrimaryType, AlbumSecondaryType},
collection::{
album::{AlbumOwnership, AlbumPrimaryType, AlbumSecondaryType},
track::TrackFormat,
},
external::{
database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
library::beets::{
@ -73,12 +76,16 @@ struct DbOpt {
fn default_filter() -> CollectionFilter {
CollectionFilter {
include: vec![
include: vec![vec![
AlbumField::PrimaryType(None),
AlbumField::PrimaryType(Some(AlbumPrimaryType::Ep)),
AlbumField::PrimaryType(Some(AlbumPrimaryType::Album)),
],
AlbumField::Ownership(AlbumOwnership::Owned(TrackFormat::Mp3)),
AlbumField::Ownership(AlbumOwnership::Owned(TrackFormat::Flac)),
]],
except: vec![
vec![AlbumField::Ownership(AlbumOwnership::None)],
vec![
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
AlbumField::SecondaryType(AlbumSecondaryType::Spokenword),
@ -91,7 +98,7 @@ fn default_filter() -> CollectionFilter {
AlbumField::SecondaryType(AlbumSecondaryType::MixtapeStreet),
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
AlbumField::SecondaryType(AlbumSecondaryType::FieldRecording),
AlbumField::Ownership(AlbumOwnership::None),
],
],
}
}
@ -100,7 +107,7 @@ fn with<Database: IDatabase + 'static, Library: ILibrary + 'static>(
builder: MusicHoardBuilder<Database, Library>,
) {
let mut music_hoard = builder.build().expect("failed to initialise MusicHoard");
music_hoard.set_filter(default_filter());
// music_hoard.set_filter(default_filter());
// Initialize the terminal user interface.
let backend = CrosstermBackend::new(io::stdout());