streamlit-octostar-utils 0.6.5.dev2__tar.gz → 0.6.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/PKG-INFO +2 -1
  2. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/pyproject.toml +2 -1
  3. streamlit_octostar_utils-0.6.6/streamlit_octostar_utils/metadata/__init__.py +17 -0
  4. streamlit_octostar_utils-0.6.6/streamlit_octostar_utils/metadata/parent_meta.py +152 -0
  5. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/LICENSE +0 -0
  6. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/README.md +0 -0
  7. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/__init__.py +0 -0
  8. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/__init__.py +0 -0
  9. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/celery.py +0 -0
  10. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/contents.py +0 -0
  11. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/fastapi.py +0 -0
  12. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/nifi.py +0 -0
  13. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parallelism.py +0 -0
  14. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/__init__.py +0 -0
  15. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/combine_fields.py +0 -0
  16. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/entities_parser.py +0 -0
  17. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/generics.py +0 -0
  18. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/info.py +0 -0
  19. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/linkchart_functions.py +0 -0
  20. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/matches.py +0 -0
  21. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/parameters.py +0 -0
  22. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/rules.py +0 -0
  23. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/api_crafter/parser/signals.py +0 -0
  24. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/core/__init__.py +0 -0
  25. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/core/dict.py +0 -0
  26. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/core/filetypes.py +0 -0
  27. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/core/opensearch_conversion.py +0 -0
  28. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/core/threading/__init__.py +0 -0
  29. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/core/threading/key_queue.py +0 -0
  30. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/core/timestamp.py +0 -0
  31. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/nlp/__init__.py +0 -0
  32. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/nlp/custom_recognizers.py +0 -0
  33. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/nlp/language.py +0 -0
  34. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/nlp/ner.py +0 -0
  35. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/octostar/__init__.py +0 -0
  36. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/octostar/client.py +0 -0
  37. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/octostar/context.py +0 -0
  38. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/octostar/permissions.py +0 -0
  39. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/ontology/__init__.py +0 -0
  40. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/ontology/inheritance.py +0 -0
  41. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/ontology/relationships.py +0 -0
  42. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/ontology/validation.py +0 -0
  43. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/style/__init__.py +0 -0
  44. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/style/common.py +0 -0
  45. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/threading/__init__.py +0 -0
  46. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/threading/async_task_manager.py +0 -0
  47. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/threading/session_callback_manager.py +0 -0
  48. {streamlit_octostar_utils-0.6.5.dev2 → streamlit_octostar_utils-0.6.6}/streamlit_octostar_utils/threading/session_state_hot_swapper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: streamlit-octostar-utils
3
- Version: 0.6.5.dev2
3
+ Version: 0.6.6
4
4
  Summary:
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -24,6 +24,7 @@ Requires-Dist: iso639-lang (>=2.0.0,<3.0.0) ; extra == "nlp"
24
24
  Requires-Dist: nltk (>=3.8.0,<4.0.0) ; extra == "nlp"
25
25
  Requires-Dist: numpy (>=1.20.0)
26
26
  Requires-Dist: octostar-streamlit (>=0.1.25,<0.2.0)
27
+ Requires-Dist: pillow (>=10.0.0)
27
28
  Requires-Dist: pottery (>=3.0.0,<4.0.0)
28
29
  Requires-Dist: presidio-analyzer (>=2.2.0,<3.0.0) ; extra == "nlp"
29
30
  Requires-Dist: psutil (>=5.9.0)
@@ -5,7 +5,7 @@ include = '\.pyi?$'
5
5
 
6
6
  [tool.poetry]
7
7
  name = "streamlit-octostar-utils"
8
- version = "0.6.5-dev.2"
8
+ version = "0.6.6"
9
9
  description = ""
10
10
  license = "MIT"
11
11
  authors = ["Octostar"]
@@ -43,6 +43,7 @@ pottery = "^3.0.0"
43
43
  slowapi = "^0.1.9"
44
44
  filetype = "^1.2.0"
45
45
  PyJWT = "^2.5.0"
46
+ pillow = ">=10.0.0"
46
47
  #octostar-python-client = "^1.0.0"
47
48
  py3langid = { version = ">=0.2.0,<0.3.0", optional = true }
48
49
  spacy-download = { version = "==1.1.0", optional = true }
@@ -0,0 +1,17 @@
1
+ from streamlit_octostar_utils.metadata.parent_meta import (
2
+ EXIF_PASSTHROUGH_FIELDS,
3
+ OS_PARENT_META_PREFIX,
4
+ embed_png_parent_meta,
5
+ ffmpeg_metadata_args,
6
+ resolve_with_aliases,
7
+ select_parent_meta_passthrough,
8
+ )
9
+
10
+ __all__ = [
11
+ "EXIF_PASSTHROUGH_FIELDS",
12
+ "OS_PARENT_META_PREFIX",
13
+ "embed_png_parent_meta",
14
+ "ffmpeg_metadata_args",
15
+ "resolve_with_aliases",
16
+ "select_parent_meta_passthrough",
17
+ ]
@@ -0,0 +1,152 @@
1
+ """Parent->child EXIF passthrough protocol.
2
+
3
+ Producer routes that create new derived files (face crops, video keyframes,
4
+ extracted audio) carry the parent's EXIF date/GPS tags through to downstream
5
+ extract-metadata by embedding ``os_parent_meta_<canonical>`` keys into the
6
+ child file's metadata container:
7
+
8
+ * PNG: ``tEXt`` chunks via :func:`embed_png_parent_meta`.
9
+ * Audio/video containers: ffmpeg ``-metadata`` flags via
10
+ :func:`ffmpeg_metadata_args`.
11
+
12
+ The reader recovers them through :func:`resolve_with_aliases`, which is
13
+ prefix-aware so it works whether exiftool surfaces the embedded key directly
14
+ or with a format-specific group prefix (``PNG:``, ``ID3:``, ``RIFF:``,
15
+ ``pdf:docinfo:custom:``, ...).
16
+
17
+ Keep producer and reader on the same version of this module: the suffix-match
18
+ in :func:`resolve_with_aliases` is the only piece that knows what prefixes
19
+ the various containers slap on, and a new container format means changes here
20
+ that both sides must see.
21
+ """
22
+
23
+ from io import BytesIO
24
+ from typing import Any, Dict, List
25
+
26
+
27
+ OS_PARENT_META_PREFIX = "os_parent_meta_"
28
+
29
+ _PDF_CUSTOM_PREFIX = "pdf:docinfo:custom:"
30
+
31
+ EXIF_PASSTHROUGH_FIELDS = (
32
+ "EXIF:DateTimeOriginal",
33
+ "Composite:SubSecDateTimeOriginal",
34
+ "Composite:DateTimeOriginal",
35
+ "EXIF:CreateDate",
36
+ "Composite:SubSecCreateDate",
37
+ "QuickTime:CreateDate",
38
+ "QuickTime:MediaCreateDate",
39
+ "QuickTime:TrackCreateDate",
40
+ "PDF:CreateDate",
41
+ "XMP:CreateDate",
42
+ "XMP:DateCreated",
43
+ "IPTC:DateCreated",
44
+ "Composite:DateTimeCreated",
45
+ "EXIF:DateTime",
46
+ "EXIF:ModifyDate",
47
+ "Composite:SubSecModifyDate",
48
+ "PDF:ModDate",
49
+ "XMP:ModifyDate",
50
+ "Composite:GPSLatitude",
51
+ "EXIF:GPSLatitude",
52
+ "XMP:GPSLatitude",
53
+ "Composite:GPSLatitudeRef",
54
+ "EXIF:GPSLatitudeRef",
55
+ "XMP:GPSLatitudeRef",
56
+ "Composite:GPSLongitude",
57
+ "EXIF:GPSLongitude",
58
+ "XMP:GPSLongitude",
59
+ "Composite:GPSLongitudeRef",
60
+ "EXIF:GPSLongitudeRef",
61
+ "XMP:GPSLongitudeRef",
62
+ "Composite:GPSPosition",
63
+ )
64
+
65
+
66
+ def resolve_with_aliases(metadata: Dict[str, Any], key: str) -> Any:
67
+ """Return ``metadata[key]`` respecting ``os_parent_meta_<key>`` passthrough.
68
+
69
+ Lookup order:
70
+
71
+ 1. ``os_parent_meta_<key>`` -- written directly by producer routes that
72
+ can place a key into the child container under an exact name.
73
+ 2. ``pdf:docinfo:custom:os_parent_meta_<key>`` -- PDF custom property
74
+ container (mirrors the ``os_ContextBefore`` pattern used elsewhere).
75
+ 3. Any key ending with ``:os_parent_meta_<key>`` -- catches the
76
+ format-specific group prefix that exiftool prepends when reading
77
+ container-native tags (``PNG:`` for PNG tEXt, ``ID3:`` for MP3 TXXX,
78
+ ``RIFF:`` for WAV INFO, etc.).
79
+ 4. ``<key>`` -- canonical fallback (the value as it would appear if the
80
+ child carried real EXIF natively).
81
+ """
82
+ aliased = f"{OS_PARENT_META_PREFIX}{key}"
83
+ value = metadata.get(aliased)
84
+ if value is not None:
85
+ return value
86
+ value = metadata.get(f"{_PDF_CUSTOM_PREFIX}{aliased}")
87
+ if value is not None:
88
+ return value
89
+ suffix = f":{aliased}"
90
+ for k, v in metadata.items():
91
+ if v is not None and k.endswith(suffix):
92
+ return v
93
+ return metadata.get(key)
94
+
95
+
96
+ def select_parent_meta_passthrough(metadata: Any) -> Dict[str, str]:
97
+ """Pick EXIF date/GPS tags worth embedding into derived children.
98
+
99
+ Input is a parent's raw exiftool metadata dict (as stored on the parent
100
+ entity's ``extract:metadata`` annotation). Output is keyed by
101
+ ``os_parent_meta_<canonical>`` with stringified values, ready to feed
102
+ into a container-specific embedder.
103
+
104
+ Uses :func:`resolve_with_aliases` so a parent that itself inherited tags
105
+ from *its* parent (e.g. a keyframe of an inherited-meta video) still
106
+ propagates the original values forward.
107
+ """
108
+ if not isinstance(metadata, dict):
109
+ return {}
110
+ out: Dict[str, str] = {}
111
+ for key in EXIF_PASSTHROUGH_FIELDS:
112
+ value = resolve_with_aliases(metadata, key)
113
+ if value is None:
114
+ continue
115
+ if isinstance(value, (list, tuple)):
116
+ value = ",".join(str(v) for v in value)
117
+ out[f"{OS_PARENT_META_PREFIX}{key}"] = str(value)
118
+ return out
119
+
120
+
121
+ def embed_png_parent_meta(png_bytes: bytes, parent_meta: Dict[str, str]) -> bytes:
122
+ """Add ``parent_meta`` keys as PNG ``tEXt`` chunks. No-op if empty."""
123
+ if not parent_meta:
124
+ return png_bytes
125
+ from PIL import Image
126
+ from PIL.PngImagePlugin import PngInfo
127
+
128
+ img = Image.open(BytesIO(png_bytes))
129
+ info = PngInfo()
130
+ for k, v in (img.info or {}).items():
131
+ if isinstance(v, str):
132
+ info.add_text(k, v)
133
+ for k, v in parent_meta.items():
134
+ info.add_text(k, v)
135
+ out = BytesIO()
136
+ img.save(out, format="PNG", pnginfo=info)
137
+ return out.getvalue()
138
+
139
+
140
+ def ffmpeg_metadata_args(parent_meta: Dict[str, str]) -> List[str]:
141
+ """Return ``-metadata key=value`` ffmpeg flag pairs for ``parent_meta``.
142
+
143
+ ffmpeg writes these into whatever metadata container the output format
144
+ supports (ID3v2 TXXX for MP3, INFO chunk for WAV, custom atoms for
145
+ MP4/M4A, vorbis comments for OGG). exiftool surfaces them with the
146
+ container's group prefix (``ID3:``, ``RIFF:``, ...), which
147
+ :func:`resolve_with_aliases` handles via its suffix match.
148
+ """
149
+ args: List[str] = []
150
+ for k, v in (parent_meta or {}).items():
151
+ args.extend(["-metadata", f"{k}={v}"])
152
+ return args