diff --git a/beets/dbcore/db.py b/beets/dbcore/db.py index b38ba1daa..8e11e4d1c 100755 --- a/beets/dbcore/db.py +++ b/beets/dbcore/db.py @@ -30,6 +30,7 @@ from collections.abc import Generator, Iterable, Iterator, Mapping, Sequence from sqlite3 import Connection from typing import TYPE_CHECKING, Any, AnyStr, Callable, Generic, TypeVar, cast +from mediafile import MediaFile from rich import print from rich_tables.generic import flexitable from unidecode import unidecode @@ -356,6 +357,10 @@ class Model(ABC, Generic[D]): """Fields in the related table.""" return cls._relation._fields.keys() - cls.shared_db_fields + @cached_classproperty + def writable_db_fields(cls) -> set[str]: + return MediaFile.fields() & cls._relation._fields.keys() + @cached_classproperty def table_with_flex_attrs(cls) -> str: """Return a SQL for entity table which includes aggregated flexible attributes. diff --git a/beets/library.py b/beets/library.py index 5976291d4..d7d08e1f1 100644 --- a/beets/library.py +++ b/beets/library.py @@ -406,6 +406,17 @@ class LibModel(dbcore.Model["Library"]): ] ) + @classmethod + def any_writable_field_query( + cls, query_class: Type[dbcore.FieldQuery], pattern: str + ) -> dbcore.OrQuery: + return dbcore.OrQuery( + [ + cls.field_query(f, pattern, query_class) + for f in cls.writable_db_fields + ] + ) + @classmethod def match_all_query( cls, pattern_by_field: Mapping[str, str] diff --git a/beetsplug/bpd/__init__.py b/beetsplug/bpd/__init__.py index da6c2eb46..15d0b4113 100644 --- a/beetsplug/bpd/__init__.py +++ b/beetsplug/bpd/__init__.py @@ -27,8 +27,6 @@ import time import traceback from string import Template -from mediafile import MediaFile - import beets import beets.ui from beets import dbcore, vfs @@ -91,8 +89,6 @@ SUBSYSTEMS = [ "partition", ] -ITEM_KEYS_WRITABLE = set(MediaFile.fields()).intersection(Item._fields.keys()) - # Gstreamer import error. class NoGstreamerError(Exception): @@ -1399,7 +1395,7 @@ class Server(BaseServer): return test_tag, key raise BPDError(ERROR_UNKNOWN, "no such tagtype") - def _metadata_query(self, query_type, any_query_type, kv): + def _metadata_query(self, query_type, kv, allow_any_query: bool = False): """Helper function returns a query object that will find items according to the library query type provided and the key-value pairs specified. The any_query_type is used for queries of @@ -1411,11 +1407,9 @@ class Server(BaseServer): it = iter(kv) for tag, value in zip(it, it): if tag.lower() == "any": - if any_query_type: + if allow_any_query: queries.append( - any_query_type( - value, ITEM_KEYS_WRITABLE, query_type - ) + Item.any_writable_field_query(query_type, value) ) else: raise BPDError(ERROR_UNKNOWN, "no such tagtype") @@ -1429,14 +1423,14 @@ class Server(BaseServer): def cmd_search(self, conn, *kv): """Perform a substring match for items.""" query = self._metadata_query( - dbcore.query.SubstringQuery, dbcore.query.AnyFieldQuery, kv + dbcore.query.SubstringQuery, kv, allow_any_query=True ) for item in self.lib.items(query): yield self._item_info(item) def cmd_find(self, conn, *kv): """Perform an exact match for items.""" - query = self._metadata_query(dbcore.query.MatchQuery, None, kv) + query = self._metadata_query(dbcore.query.MatchQuery, kv) for item in self.lib.items(query): yield self._item_info(item) @@ -1456,7 +1450,7 @@ class Server(BaseServer): raise BPDError(ERROR_ARG, 'should be "Album" for 3 arguments') elif len(kv) % 2 != 0: raise BPDError(ERROR_ARG, "Incorrect number of filter arguments") - query = self._metadata_query(dbcore.query.MatchQuery, None, kv) + query = self._metadata_query(dbcore.query.MatchQuery, kv) clause, subvals = query.clause() statement = (