diff --git a/CHANGELOG.md b/CHANGELOG.md index 7161d43..960e0cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Added support for scene based granular video code processing - Added ffprobe video metadata extraction (duration, fps, width, height, language) - Added support str and Path objects for all file inpunts +- Handle unsupported SVG files gracefully ## 0.6.2 - 2024-06-13 - Update and relax dependencies diff --git a/docs/changelog.md b/docs/changelog.md index 7161d43..960e0cd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,7 @@ - Added support for scene based granular video code processing - Added ffprobe video metadata extraction (duration, fps, width, height, language) - Added support str and Path objects for all file inpunts +- Handle unsupported SVG files gracefully ## 0.6.2 - 2024-06-13 - Update and relax dependencies diff --git a/iscc_sdk/cli.py b/iscc_sdk/cli.py index 8b48085..c81efb9 100644 --- a/iscc_sdk/cli.py +++ b/iscc_sdk/cli.py @@ -95,8 +95,12 @@ def create(file: Path): """Create ISCC-CODE for single FILE.""" log.remove() if file.is_file() and file.exists(): - result = idk.code_iscc(file.as_posix()) - typer.echo(result.json(indent=2)) + try: + result = idk.code_iscc(file.as_posix()) + typer.echo(result.json(indent=2)) + except idk.IsccUnsupportedMediatype as e: + typer.echo(e) + raise typer.Exit(code=1) else: typer.echo(f"Invalid file path {file}") raise typer.Exit(code=1) diff --git a/iscc_sdk/image.py b/iscc_sdk/image.py index 11ce36c..fb35612 100644 --- a/iscc_sdk/image.py +++ b/iscc_sdk/image.py @@ -189,6 +189,8 @@ def image_meta_embed(fp, meta): cmdf += f"set Xmp.dc.creator {meta.creator}\n" if meta.rights: cmdf += f"set Xmp.dc.rights {meta.rights}\n" + if meta.identifier: + cmdf += f"set Xmp.dc.identifier {meta.identifier}\n" # Create temp filepaths tempdir = Path(tempfile.mkdtemp()) diff --git a/iscc_sdk/mediatype.py b/iscc_sdk/mediatype.py index c09fa72..b58b25f 100644 --- a/iscc_sdk/mediatype.py +++ b/iscc_sdk/mediatype.py @@ -169,9 +169,9 @@ def mediatype_to_mode(mime_type): # type: (str) -> str """Get perceptual processing mode from mimetype. - :param str mime_type: RFC-6838 mediatype string - :return str: Processing mode ("text", "image", "audio", "video") - :raise ValueError: if no matching processing mode was found. + :param mime_type: RFC-6838 mediatype string + :return Processing mode ("text", "image", "audio", "video") + :raise IsccUnsupportedMediatype: if no matching processing mode was found. """ mime_type = mediatype_clean(mime_type) @@ -181,7 +181,7 @@ def mediatype_to_mode(mime_type): # Fallback to guess mode by top-level type mode = mime_type.split("/")[0] - if mode in ["text", "image", "audio", "video"]: + if mode in ["text", "image", "audio", "video"] and mime_type not in UNSUPPORTED_MEDIATYPES: log.warning(f"Guessing perceptual mode from {mime_type}") return mode @@ -202,6 +202,7 @@ def mediatype_to_mode(mime_type): mimetypes.add_type("image/heic", ".heic") mimetypes.add_type("image/avif", ".avif") + SUPPORTED_MEDIATYPES = { # Text Formats "application/rtf": {"mode": "text", "ext": "rtf"}, @@ -278,6 +279,12 @@ def mediatype_to_mode(mime_type): "video/x-ms-wmv": {"mode": "video", "ext": "wmv"}, } +# Signals eplixitly unsupported mediatypes that fail processing +UNSUPPORTED_MEDIATYPES = { + "image/svg+xml": {"mode": "image", "ext": "svg"}, +} + + MEDIATYPE_NORM = { "audio/x-aiff": "audio/aiff", "audio/x-wav": "audio/wav", diff --git a/iscc_sdk/metadata.py b/iscc_sdk/metadata.py index 481da75..30feb6d 100644 --- a/iscc_sdk/metadata.py +++ b/iscc_sdk/metadata.py @@ -1,5 +1,6 @@ """*Metadata handling functions*""" +from loguru import logger as log from pathlib import Path from typing import Optional diff --git a/tests/conftest.py b/tests/conftest.py index 509271b..5a48b03 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -115,6 +115,18 @@ def epub_file(tmp_path_factory): return dst.as_posix() +@pytest.fixture(scope="module") +def svg_file(tmp_path_factory) -> str: + dst = tmp_path_factory.mktemp("data") / "image.svg" + svg_content = """ + + + + """ + dst.write_text(svg_content) + return dst.as_posix() + + @pytest.fixture(scope="module") def asset_tree(tmp_path_factory): dst = Path(tmp_path_factory.mktemp("data")) diff --git a/tests/test_cli.py b/tests/test_cli.py index d5472a9..5248ee7 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -68,6 +68,12 @@ def test_cli_create(): } +def test_cli_create_unsupported(svg_file): + result = runner.invoke(app, ["create", svg_file]) + assert result.exit_code == 1 + assert "Unsupported mediatype image/svg+xml" in result.stdout + + def test_cli_batch_no_arg(): result = runner.invoke(app, ["batch"]) assert result.exit_code == 2 diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 6bf56de..eef11d9 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -1,3 +1,5 @@ +import pytest + import iscc_sdk as idk @@ -10,6 +12,11 @@ def test_extract_metadata(jpg_file): } +def test_extract_metadata_unsupported(svg_file): + with pytest.raises(idk.IsccUnsupportedMediatype): + idk.extract_metadata(svg_file) + + def test_embed_metadata(jpg_file): meta = idk.IsccMeta(name="Some Title", description="Some Description") new_file = idk.embed_metadata(jpg_file, meta) @@ -22,6 +29,19 @@ def test_embed_metadata(jpg_file): } +def test_metadata_identifier_field_image(jpg_file): + meta = idk.IsccMeta(name="Some Title", description="Some Description", identifier="abcdefghijk") + new_file = idk.embed_metadata(jpg_file, meta) + assert idk.extract_metadata(new_file).dict() == { + "name": "Some Title", + "description": "Some Description", + "creator": "Some Cat Lover", + "height": 133, + "width": 200, + "identifier": "abcdefghijk", + } + + def test_embed_metadata_unsupported(doc_file): meta = idk.IsccMeta(name="Some Title", description="Some Description") new_file = idk.embed_metadata(doc_file, meta)