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).
|
/// Filter for a specifying subsets of the entire collection (e.g., for display).
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct CollectionFilter {
|
pub struct CollectionFilter {
|
||||||
pub include: Vec<AlbumField>,
|
pub include: Vec<Vec<AlbumField>>,
|
||||||
pub except: Vec<AlbumField>,
|
pub except: Vec<Vec<AlbumField>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -16,11 +16,19 @@ pub enum AlbumField {
|
|||||||
|
|
||||||
impl CollectionFilter {
|
impl CollectionFilter {
|
||||||
pub fn filter_album(&self, album: &Album) -> bool {
|
pub fn filter_album(&self, album: &Album) -> bool {
|
||||||
let include = Self::filter_or(&self.include, album);
|
let include = Self::filter_and(true, &self.include, album);
|
||||||
let except = Self::filter_or(&self.except, album);
|
let except = Self::filter_and(false, &self.except, album);
|
||||||
include && !except
|
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 {
|
fn filter_or(group: &Vec<AlbumField>, album: &Album) -> bool {
|
||||||
let mut filter = false;
|
let mut filter = false;
|
||||||
for field in group.iter() {
|
for field in group.iter() {
|
||||||
@ -52,18 +60,22 @@ mod tests {
|
|||||||
|
|
||||||
fn test_filter() -> CollectionFilter {
|
fn test_filter() -> CollectionFilter {
|
||||||
CollectionFilter {
|
CollectionFilter {
|
||||||
include: vec![
|
include: vec![vec![
|
||||||
AlbumField::PrimaryType(None),
|
AlbumField::PrimaryType(None),
|
||||||
AlbumField::PrimaryType(Some(AlbumPrimaryType::Ep)),
|
AlbumField::PrimaryType(Some(AlbumPrimaryType::Ep)),
|
||||||
AlbumField::PrimaryType(Some(AlbumPrimaryType::Album)),
|
AlbumField::PrimaryType(Some(AlbumPrimaryType::Album)),
|
||||||
],
|
AlbumField::Ownership(AlbumOwnership::Owned(TrackFormat::Mp3)),
|
||||||
|
AlbumField::Ownership(AlbumOwnership::Owned(TrackFormat::Flac)),
|
||||||
|
]],
|
||||||
except: vec![
|
except: vec![
|
||||||
AlbumField::Ownership(AlbumOwnership::None),
|
vec![AlbumField::Ownership(AlbumOwnership::None)],
|
||||||
|
vec![
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
|
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
|
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::Live),
|
AlbumField::SecondaryType(AlbumSecondaryType::Live),
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
|
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
|
||||||
],
|
],
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +105,9 @@ mod tests {
|
|||||||
let filter = test_filter();
|
let filter = test_filter();
|
||||||
let mut album = test_album();
|
let mut album = test_album();
|
||||||
|
|
||||||
|
// Drop ownership so that filtering is truly only on type.
|
||||||
|
album.tracks.clear();
|
||||||
|
|
||||||
album.meta.info.primary_type = None;
|
album.meta.info.primary_type = None;
|
||||||
assert!(filter.filter_album(&album));
|
assert!(filter.filter_album(&album));
|
||||||
|
|
||||||
@ -121,29 +136,42 @@ mod tests {
|
|||||||
types.push(AlbumSecondaryType::AudioDrama);
|
types.push(AlbumSecondaryType::AudioDrama);
|
||||||
assert!(filter.filter_album(&album));
|
assert!(filter.filter_album(&album));
|
||||||
|
|
||||||
// Filtered type.
|
// Filtered type. But album is owned so it remains.
|
||||||
let types = &mut album.meta.info.secondary_types;
|
let types = &mut album.meta.info.secondary_types;
|
||||||
types.push(AlbumSecondaryType::Live);
|
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.
|
// Remove non-filtered type.
|
||||||
album.meta.info.secondary_types.remove(0);
|
album.meta.info.secondary_types.remove(0);
|
||||||
assert!(!filter.filter_album(&album));
|
assert!(filter.filter_album(&album));
|
||||||
|
|
||||||
// Add another filtered type.
|
// Add another filtered type.
|
||||||
let types = &mut album.meta.info.secondary_types;
|
let types = &mut album.meta.info.secondary_types;
|
||||||
types.push(AlbumSecondaryType::Soundtrack);
|
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));
|
assert!(!filter.filter_album(&album));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn filter_ownership() {
|
fn filter_ownership() {
|
||||||
let filter = test_filter();
|
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();
|
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));
|
assert!(!filter.filter_album(&album));
|
||||||
|
|
||||||
|
// Changing ownership should make it go back to being included.
|
||||||
album.tracks.push(test_track());
|
album.tracks.push(test_track());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
album.get_ownership(),
|
album.get_ownership(),
|
||||||
|
17
src/main.rs
17
src/main.rs
@ -6,7 +6,10 @@ use ratatui::{backend::CrosstermBackend, Terminal};
|
|||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use musichoard::{
|
use musichoard::{
|
||||||
collection::album::{AlbumOwnership, AlbumPrimaryType, AlbumSecondaryType},
|
collection::{
|
||||||
|
album::{AlbumOwnership, AlbumPrimaryType, AlbumSecondaryType},
|
||||||
|
track::TrackFormat,
|
||||||
|
},
|
||||||
external::{
|
external::{
|
||||||
database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
|
database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
|
||||||
library::beets::{
|
library::beets::{
|
||||||
@ -73,12 +76,16 @@ struct DbOpt {
|
|||||||
|
|
||||||
fn default_filter() -> CollectionFilter {
|
fn default_filter() -> CollectionFilter {
|
||||||
CollectionFilter {
|
CollectionFilter {
|
||||||
include: vec![
|
include: vec![vec![
|
||||||
AlbumField::PrimaryType(None),
|
AlbumField::PrimaryType(None),
|
||||||
AlbumField::PrimaryType(Some(AlbumPrimaryType::Ep)),
|
AlbumField::PrimaryType(Some(AlbumPrimaryType::Ep)),
|
||||||
AlbumField::PrimaryType(Some(AlbumPrimaryType::Album)),
|
AlbumField::PrimaryType(Some(AlbumPrimaryType::Album)),
|
||||||
],
|
AlbumField::Ownership(AlbumOwnership::Owned(TrackFormat::Mp3)),
|
||||||
|
AlbumField::Ownership(AlbumOwnership::Owned(TrackFormat::Flac)),
|
||||||
|
]],
|
||||||
except: vec![
|
except: vec![
|
||||||
|
vec![AlbumField::Ownership(AlbumOwnership::None)],
|
||||||
|
vec![
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
|
AlbumField::SecondaryType(AlbumSecondaryType::Compilation),
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
|
AlbumField::SecondaryType(AlbumSecondaryType::Soundtrack),
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::Spokenword),
|
AlbumField::SecondaryType(AlbumSecondaryType::Spokenword),
|
||||||
@ -91,7 +98,7 @@ fn default_filter() -> CollectionFilter {
|
|||||||
AlbumField::SecondaryType(AlbumSecondaryType::MixtapeStreet),
|
AlbumField::SecondaryType(AlbumSecondaryType::MixtapeStreet),
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
|
AlbumField::SecondaryType(AlbumSecondaryType::Demo),
|
||||||
AlbumField::SecondaryType(AlbumSecondaryType::FieldRecording),
|
AlbumField::SecondaryType(AlbumSecondaryType::FieldRecording),
|
||||||
AlbumField::Ownership(AlbumOwnership::None),
|
],
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,7 +107,7 @@ fn with<Database: IDatabase + 'static, Library: ILibrary + 'static>(
|
|||||||
builder: MusicHoardBuilder<Database, Library>,
|
builder: MusicHoardBuilder<Database, Library>,
|
||||||
) {
|
) {
|
||||||
let mut music_hoard = builder.build().expect("failed to initialise MusicHoard");
|
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.
|
// Initialize the terminal user interface.
|
||||||
let backend = CrosstermBackend::new(io::stdout());
|
let backend = CrosstermBackend::new(io::stdout());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user