Source code for google_music_utils.filter

__all__ = [
	'exclude_items',
	'include_items',
]

import functools
import re
from itertools import filterfalse


from .utils import (
	get_field,
	get_item_tags,
	normalize_value,
)


def _match_field(
	field_value,
	pattern,
	*,
	ignore_case=False,
	normalize_values=False
):
	"""Match an item metadata field value by pattern.

	Note:
		Metadata values are lowercased when ``normalized_values`` is ``True``,
		so ``ignore_case`` is automatically set to ``True``.

	Parameters:
		field_value (list or str): A metadata field value to check.
		pattern (str): A regex pattern to check the field value(s) against.
		ignore_case (bool, Optional):
			Perform case-insensitive matching.
			Default: ``False``
		normalize_values (bool, Optional):
			Normalize metadata values to remove common differences between sources.
			Default: ``False``

	Returns:
		bool: True if matched, False if not.
	"""

	if normalize_values:
		ignore_case = True

	normalize = normalize_value if normalize_values else lambda x: str(x)
	search = functools.partial(re.search, flags=re.I) if ignore_case else re.search

	# audio_metadata fields contain a list of values.
	if isinstance(field_value, list):
		return any(search(pattern, normalize(value)) for value in field_value)
	else:
		return search(pattern, normalize(field_value))


def _match_item(
	item,
	*,
	any_all=any,
	ignore_case=False,
	normalize_values=False,
	**kwargs
):
	"""Match items by metadata.

	Note:
		Metadata values are lowercased when ``normalized_values`` is ``True``,
		so ``ignore_case`` is automatically set to ``True``.

	Parameters:
		item (~collections.abc.Mapping, str, os.PathLike): Item dict or filepath.
		any_all (callable, Optional):
			A callable to determine if any or all filters must match to match item.
			Expected values :obj:`any` (default) or :obj:`all`.
		ignore_case (bool, Optional):
			Perform case-insensitive matching.
			Default: ``False``
		normalize_values (bool, Optional):
			Normalize metadata values to remove common differences between sources.
			Default: ``False``
		kwargs (list, Optional): Lists of values to match the given metadata field.

	Returns:
		bool: True if matched, False if not.
	"""

	tags = get_item_tags(item)

	if tags is not None:
		return any_all(
			_match_field(
				get_field(tags, field),
				pattern,
				ignore_case=ignore_case,
				normalize_values=normalize_values,
			)
			for field, patterns in kwargs.items()
			for pattern in patterns
		)


[docs]def exclude_items( items, *, any_all=any, ignore_case=False, normalize_values=False, **kwargs ): """Exclude items by matching metadata. Note: Metadata values are lowercased when ``normalized_values`` is ``True``, so ``ignore_case`` is automatically set to ``True``. Parameters: items (list): A list of item dicts or filepaths. any_all (callable, Optional): A callable to determine if any or all filters must match to exclude items. Expected values :obj:`any` (default) or :obj:`all`. ignore_case (bool, Optional): Perform case-insensitive matching. Default: ``False`` normalize_values (bool, Optional): Normalize metadata values to remove common differences between sources. Default: ``False`` kwargs (list, Optional): Lists of values to match the given metadata field. Yields: dict: The next item to be included. Example: >>> from google_music_utils import exclude_items >>> list(exclude_items(song_list, any_all=all, ignore_case=True, normalize_values=True, artist=['Beck'], album=['Golden Feelings'])) """ if kwargs: match = functools.partial( _match_item, any_all=any_all, ignore_case=ignore_case, normalize_values=normalize_values, **kwargs ) return filterfalse(match, items) else: return iter(items)
[docs]def include_items( items, *, any_all=any, ignore_case=False, normalize_values=False, **kwargs ): """Include items by matching metadata. Note: Metadata values are lowercased when ``normalized_values`` is ``True``, so ``ignore_case`` is automatically set to ``True``. Parameters: items (list): A list of item dicts or filepaths. any_all (callable, Optional): A callable to determine if any or all filters must match to include items. Expected values :obj:`any` (default) or :obj:`all`. ignore_case (bool, Optional): Perform case-insensitive matching. Default: ``False`` normalize_values (bool, Optional): Normalize metadata values to remove common differences between sources. Default: ``False`` kwargs (list, Optional): Lists of values to match the given metadata field. Yields: dict: The next item to be included. Example: >>> from google_music_utils import exclude_items >>> list(include_items(song_list, any_all=all, ignore_case=True, normalize_values=True, artist=['Beck'], album=['Odelay'])) """ if kwargs: match = functools.partial( _match_item, any_all=any_all, ignore_case=ignore_case, normalize_values=normalize_values, **kwargs ) return filter(match, items) else: return iter(items)