mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-01-31 23:29:10 +00:00
feat: add filetype and mediatype searches (#575)
* feat: add filetype and mediatype searches * style: fix some style issues * fix: parametrize mediatype and filetype tests * style: fix remaining unordered import * style: fix pytest parametrize calls * feat: add human-readable names to mediacategories * feat: use human-readable names in mediacategory: search * feat: add human-readable name to open document * fix: fix returning multiple filetypes issue and add regression test
This commit is contained in:
@@ -77,6 +77,9 @@ class FilterState:
|
||||
path: Path | str | None = None
|
||||
# file name
|
||||
name: str | None = None
|
||||
# file type
|
||||
filetype: str | None = None
|
||||
mediatype: str | None = None
|
||||
|
||||
# a generic query to be parsed
|
||||
query: str | None = None
|
||||
@@ -87,6 +90,7 @@ class FilterState:
|
||||
# parse the value
|
||||
if ":" in query:
|
||||
kind, _, value = query.partition(":")
|
||||
value = value.replace('"', "")
|
||||
else:
|
||||
# default to tag search
|
||||
kind, value = "tag", query
|
||||
@@ -101,6 +105,10 @@ class FilterState:
|
||||
self.name = value
|
||||
elif kind == "id":
|
||||
self.id = int(self.id) if str(self.id).isnumeric() else self.id
|
||||
elif kind == "filetype":
|
||||
self.filetype = value
|
||||
elif kind == "mediatype":
|
||||
self.mediatype = value
|
||||
|
||||
else:
|
||||
self.tag = self.tag and self.tag.strip()
|
||||
|
||||
@@ -36,6 +36,7 @@ from ...constants import (
|
||||
TS_FOLDER_NAME,
|
||||
)
|
||||
from ...enums import LibraryPrefs
|
||||
from ...media_types import MediaCategories
|
||||
from .db import make_tables
|
||||
from .enums import FieldTypeEnum, FilterState, TagColor
|
||||
from .fields import (
|
||||
@@ -438,6 +439,18 @@ class Library:
|
||||
)
|
||||
elif search.path:
|
||||
statement = statement.where(Entry.path.ilike(f"%{search.path}%"))
|
||||
elif search.filetype:
|
||||
statement = statement.where(Entry.suffix.ilike(f"{search.filetype}"))
|
||||
elif search.mediatype:
|
||||
extensions: set[str] = set[str]()
|
||||
for media_cat in MediaCategories.ALL_CATEGORIES:
|
||||
if search.mediatype == media_cat.name:
|
||||
extensions = extensions | media_cat.extensions
|
||||
break
|
||||
# just need to map it to search db - suffixes do not have '.'
|
||||
statement = statement.where(
|
||||
Entry.suffix.in_(map(lambda x: x.replace(".", ""), extensions))
|
||||
)
|
||||
|
||||
extensions = self.prefs(LibraryPrefs.EXTENSION_LIST)
|
||||
is_exclude_list = self.prefs(LibraryPrefs.IS_EXCLUDE_LIST)
|
||||
|
||||
@@ -62,6 +62,7 @@ class MediaCategory:
|
||||
|
||||
media_type: MediaType
|
||||
extensions: set[str]
|
||||
name: str
|
||||
is_iana: bool = False
|
||||
|
||||
|
||||
@@ -338,151 +339,181 @@ class MediaCategories:
|
||||
media_type=MediaType.ADOBE_PHOTOSHOP,
|
||||
extensions=_ADOBE_PHOTOSHOP_SET,
|
||||
is_iana=False,
|
||||
name="photoshop",
|
||||
)
|
||||
AFFINITY_PHOTO_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.AFFINITY_PHOTO,
|
||||
extensions=_AFFINITY_PHOTO_SET,
|
||||
is_iana=False,
|
||||
name="affinity photo",
|
||||
)
|
||||
ARCHIVE_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.ARCHIVE,
|
||||
extensions=_ARCHIVE_SET,
|
||||
is_iana=False,
|
||||
name="archive",
|
||||
)
|
||||
AUDIO_MIDI_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.AUDIO_MIDI,
|
||||
extensions=_AUDIO_MIDI_SET,
|
||||
is_iana=False,
|
||||
name="audio midi",
|
||||
)
|
||||
AUDIO_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.AUDIO,
|
||||
extensions=_AUDIO_SET | _AUDIO_MIDI_SET,
|
||||
is_iana=True,
|
||||
name="audio",
|
||||
)
|
||||
BLENDER_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.BLENDER,
|
||||
extensions=_BLENDER_SET,
|
||||
is_iana=False,
|
||||
name="blender",
|
||||
)
|
||||
DATABASE_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.DATABASE,
|
||||
extensions=_DATABASE_SET,
|
||||
is_iana=False,
|
||||
name="database",
|
||||
)
|
||||
DISK_IMAGE_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.DISK_IMAGE,
|
||||
extensions=_DISK_IMAGE_SET,
|
||||
is_iana=False,
|
||||
name="disk image",
|
||||
)
|
||||
DOCUMENT_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.DOCUMENT,
|
||||
extensions=_DOCUMENT_SET,
|
||||
is_iana=False,
|
||||
name="document",
|
||||
)
|
||||
EBOOK_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.EBOOK,
|
||||
extensions=_EBOOK_SET,
|
||||
is_iana=False,
|
||||
name="ebook",
|
||||
)
|
||||
FONT_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.FONT,
|
||||
extensions=_FONT_SET,
|
||||
is_iana=True,
|
||||
name="font",
|
||||
)
|
||||
IMAGE_ANIMATED_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.IMAGE_ANIMATED,
|
||||
extensions=_IMAGE_ANIMATED_SET,
|
||||
is_iana=False,
|
||||
name="animated image",
|
||||
)
|
||||
IMAGE_RAW_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.IMAGE_RAW,
|
||||
extensions=_IMAGE_RAW_SET,
|
||||
is_iana=False,
|
||||
name="raw image",
|
||||
)
|
||||
IMAGE_VECTOR_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.IMAGE_VECTOR,
|
||||
extensions=_IMAGE_VECTOR_SET,
|
||||
is_iana=False,
|
||||
name="vector image",
|
||||
)
|
||||
IMAGE_RASTER_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.IMAGE,
|
||||
extensions=_IMAGE_RASTER_SET,
|
||||
is_iana=False,
|
||||
name="raster image",
|
||||
)
|
||||
IMAGE_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.IMAGE,
|
||||
extensions=_IMAGE_RASTER_SET | _IMAGE_RAW_SET | _IMAGE_VECTOR_SET,
|
||||
is_iana=True,
|
||||
name="image",
|
||||
)
|
||||
INSTALLER_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.INSTALLER,
|
||||
extensions=_INSTALLER_SET,
|
||||
is_iana=False,
|
||||
name="installer",
|
||||
)
|
||||
MATERIAL_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.MATERIAL,
|
||||
extensions=_MATERIAL_SET,
|
||||
is_iana=False,
|
||||
name="material",
|
||||
)
|
||||
MODEL_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.MODEL,
|
||||
extensions=_MODEL_SET,
|
||||
is_iana=True,
|
||||
name="model",
|
||||
)
|
||||
OPEN_DOCUMENT_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.OPEN_DOCUMENT,
|
||||
extensions=_OPEN_DOCUMENT_SET,
|
||||
is_iana=False,
|
||||
name="open document",
|
||||
)
|
||||
PACKAGE_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.PACKAGE,
|
||||
extensions=_PACKAGE_SET,
|
||||
is_iana=False,
|
||||
name="package",
|
||||
)
|
||||
PDF_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.PDF,
|
||||
extensions=_PDF_SET,
|
||||
is_iana=False,
|
||||
name="pdf",
|
||||
)
|
||||
PLAINTEXT_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.PLAINTEXT,
|
||||
extensions=_PLAINTEXT_SET,
|
||||
is_iana=False,
|
||||
name="plaintext",
|
||||
)
|
||||
PRESENTATION_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.PRESENTATION,
|
||||
extensions=_PRESENTATION_SET,
|
||||
is_iana=False,
|
||||
name="presentation",
|
||||
)
|
||||
PROGRAM_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.PROGRAM,
|
||||
extensions=_PROGRAM_SET,
|
||||
is_iana=False,
|
||||
name="program",
|
||||
)
|
||||
SHORTCUT_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.SHORTCUT,
|
||||
extensions=_SHORTCUT_SET,
|
||||
is_iana=False,
|
||||
name="shortcut",
|
||||
)
|
||||
SOURCE_ENGINE_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.SOURCE_ENGINE,
|
||||
extensions=_SOURCE_ENGINE_SET,
|
||||
is_iana=False,
|
||||
name="source engine",
|
||||
)
|
||||
SPREADSHEET_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.SPREADSHEET,
|
||||
extensions=_SPREADSHEET_SET,
|
||||
is_iana=False,
|
||||
name="spreadsheet",
|
||||
)
|
||||
TEXT_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.TEXT,
|
||||
extensions=_DOCUMENT_SET | _PLAINTEXT_SET,
|
||||
is_iana=True,
|
||||
name="text",
|
||||
)
|
||||
VIDEO_TYPES: MediaCategory = MediaCategory(
|
||||
media_type=MediaType.VIDEO,
|
||||
extensions=_VIDEO_SET,
|
||||
is_iana=True,
|
||||
name="video",
|
||||
)
|
||||
|
||||
ALL_CATEGORIES: list[MediaCategory] = [
|
||||
|
||||
@@ -21,6 +21,37 @@ def cwd():
|
||||
return CWD
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def file_mediatypes_library():
|
||||
lib = Library()
|
||||
|
||||
status = lib.open_library(pathlib.Path(""), ":memory:")
|
||||
assert status.success
|
||||
|
||||
entry1 = Entry(
|
||||
folder=lib.folder,
|
||||
path=pathlib.Path("foo.png"),
|
||||
fields=lib.default_fields,
|
||||
)
|
||||
|
||||
entry2 = Entry(
|
||||
folder=lib.folder,
|
||||
path=pathlib.Path("bar.png"),
|
||||
fields=lib.default_fields,
|
||||
)
|
||||
|
||||
entry3 = Entry(
|
||||
folder=lib.folder,
|
||||
path=pathlib.Path("baz.apng"),
|
||||
fields=lib.default_fields,
|
||||
)
|
||||
|
||||
assert lib.add_entries([entry1, entry2, entry3])
|
||||
assert len(lib.tags) == 2
|
||||
|
||||
return lib
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def library(request):
|
||||
# when no param is passed, use the default
|
||||
|
||||
@@ -401,3 +401,21 @@ def test_library_prefs_multiple_identical_vals():
|
||||
# accessing .value should raise exception
|
||||
with pytest.raises(AttributeError):
|
||||
assert TestPrefs.BAR.value
|
||||
|
||||
|
||||
@pytest.mark.parametrize(["filetype", "num_of_filetype"], [("md", 1), ("txt", 1), ("png", 0)])
|
||||
def test_filetype_search(library, filetype, num_of_filetype):
|
||||
results = library.search_library(FilterState(filetype=filetype))
|
||||
assert len(results.items) == num_of_filetype
|
||||
|
||||
|
||||
@pytest.mark.parametrize(["filetype", "num_of_filetype"], [("png", 2), ("apng", 1), ("ng", 0)])
|
||||
def test_filetype_return_one_filetype(file_mediatypes_library, filetype, num_of_filetype):
|
||||
results = file_mediatypes_library.search_library(FilterState(filetype=filetype))
|
||||
assert len(results.items) == num_of_filetype
|
||||
|
||||
|
||||
@pytest.mark.parametrize(["mediatype", "num_of_mediatype"], [("plaintext", 2), ("image", 0)])
|
||||
def test_mediatype_search(library, mediatype, num_of_mediatype):
|
||||
results = library.search_library(FilterState(mediatype=mediatype))
|
||||
assert len(results.items) == num_of_mediatype
|
||||
|
||||
Reference in New Issue
Block a user