Add a filtering tool to only show only certain release group types #252
@ -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,17 +60,21 @@ 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),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Live),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
|
||||
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(),
|
||||
|
41
src/main.rs
41
src/main.rs
@ -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,25 +76,29 @@ 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![
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Spokenword),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Interview),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Audiobook),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::AudioDrama),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Live),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Remix),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::DjMix),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::MixtapeStreet),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::FieldRecording),
|
||||
AlbumField::Ownership(AlbumOwnership::None),
|
||||
vec![AlbumField::Ownership(AlbumOwnership::None)],
|
||||
vec![
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Spokenword),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Interview),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Audiobook),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::AudioDrama),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Live),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Remix),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::DjMix),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::MixtapeStreet),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
|
||||
AlbumField::SecondaryType(AlbumSecondaryType::FieldRecording),
|
||||
],
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user