# `orchestrator.pkg.media_extract`
<!-- generated by `orchestrator understand`; the PKG is source of truth, edits here are advisory -->

[← Episteme](../README.md) · [Architecture](../architecture.md)

**Source:** [`src/orchestrator/pkg/media_extract.py`](../../src/orchestrator/pkg/media_extract.py)

3 types · 10 functions · python

## Changing this safely

**Tested by** (1): `tests.pkg.test_media_extract`

**Most depended-upon here** — a change to these reaches the most code (call graph, ≤4 hops):

- [`MediaExtractorUnavailableError`](../../src/orchestrator/pkg/media_extract.py#L55) — reaches **12** symbols
- [`_load_pytesseract`](../../src/orchestrator/pkg/media_extract.py#L74) — reaches **5** symbols
- [`ExtractResult`](../../src/orchestrator/pkg/media_extract.py#L64) — reaches **3** symbols
- [`_iter_files`](../../src/orchestrator/pkg/media_extract.py#L219) — reaches **3** symbols
- [`labels_from_ocr`](../../src/orchestrator/pkg/media_extract.py#L120) — reaches **3** symbols
- [`tesseract_version`](../../src/orchestrator/pkg/media_extract.py#L85) — reaches **3** symbols
- [`write_artifact`](../../src/orchestrator/pkg/media_extract.py#L174) — reaches **3** symbols
- [`build_image_artifact`](../../src/orchestrator/pkg/media_extract.py#L140) — reaches **2** symbols

_1 of the symbols other code depends on here have no test path the graph can see — worth a second look before changing them. Absence of a path is not proof of absence of a test: calls through an attribute chain (`obj.method()`) are skipped rather than guessed at, so indirect coverage is invisible._

## Types

### `ExtractResult`

[`src/orchestrator/pkg/media_extract.py:64`](../../src/orchestrator/pkg/media_extract.py#L64)

- **Called by** (2): [`extract_image`](../../src/orchestrator/pkg/media_extract.py#L186), [`extract_media`](../../src/orchestrator/pkg/media_asr.py#L227)
- **Fields**: `artifact`, `media`, `segments`, `status`, `truncated`

### `MediaExtractorUnavailableError`

[`src/orchestrator/pkg/media_extract.py:55`](../../src/orchestrator/pkg/media_extract.py#L55)

- **Extends** (1): `RuntimeError`
- **Called by** (10 production · 1 test): [`_load_pytesseract`](../../src/orchestrator/pkg/media_extract.py#L74), [`_load_whisper`](../../src/orchestrator/pkg/media_asr.py#L140), [`_tesseract_ocr`](../../src/orchestrator/pkg/media_extract.py#L102), [`build_image_artifact`](../../src/orchestrator/pkg/media_extract.py#L140), [`build_media_artifact`](../../src/orchestrator/pkg/media_asr.py#L192), [`extract_image`](../../src/orchestrator/pkg/media_extract.py#L186), [`extract_media`](../../src/orchestrator/pkg/media_asr.py#L227), [`tesseract_version`](../../src/orchestrator/pkg/media_extract.py#L85), [`transcribe`](../../src/orchestrator/pkg/media_asr.py#L116), [`transcribe`](../../src/orchestrator/pkg/media_asr.py#L86), [`broken_ocr`](../../tests/pkg/test_media_extract.py#L184)

### `OcrFn`

[`src/orchestrator/pkg/media_extract.py:49`](../../src/orchestrator/pkg/media_extract.py#L49)

- **Extends** (1): `typing.Protocol`

## Functions

### `_iter_files`

[`src/orchestrator/pkg/media_extract.py:219`](../../src/orchestrator/pkg/media_extract.py#L219)

- **Called by** (2): [`iter_image_files`](../../src/orchestrator/pkg/media_extract.py#L240), [`iter_media_files`](../../src/orchestrator/pkg/media_extract.py#L245)

### `_load_pytesseract`

[`src/orchestrator/pkg/media_extract.py:74`](../../src/orchestrator/pkg/media_extract.py#L74)

- **Called by** (2): [`_tesseract_ocr`](../../src/orchestrator/pkg/media_extract.py#L102), [`tesseract_version`](../../src/orchestrator/pkg/media_extract.py#L85)
- **Calls** (1): [`MediaExtractorUnavailableError`](../../src/orchestrator/pkg/media_extract.py#L55)

### `_tesseract_ocr`

[`src/orchestrator/pkg/media_extract.py:102`](../../src/orchestrator/pkg/media_extract.py#L102)

- **Calls** (4): [`MediaExtractorUnavailableError`](../../src/orchestrator/pkg/media_extract.py#L55), [`_load_pytesseract`](../../src/orchestrator/pkg/media_extract.py#L74), `image_to_string`, `open`

### `build_image_artifact`

[`src/orchestrator/pkg/media_extract.py:140`](../../src/orchestrator/pkg/media_extract.py#L140)

- **Called by** (1 production · 3 test): [`extract_image`](../../src/orchestrator/pkg/media_extract.py#L186), [`test_build_image_artifact_is_schema_conformant`](../../tests/pkg/test_media_extract.py#L87), [`test_build_records_hash_matching_the_file`](../../tests/pkg/test_media_extract.py#L99), [`test_too_many_labels_are_truncated_and_recorded`](../../tests/pkg/test_media_extract.py#L157)
- **Calls** (4): [`MediaExtractorUnavailableError`](../../src/orchestrator/pkg/media_extract.py#L55), [`_file_sha256`](../../src/orchestrator/pkg/media.py#L52), [`labels_from_ocr`](../../src/orchestrator/pkg/media_extract.py#L120), [`tesseract_version`](../../src/orchestrator/pkg/media_extract.py#L85)

### `extract_image`

[`src/orchestrator/pkg/media_extract.py:186`](../../src/orchestrator/pkg/media_extract.py#L186)

- **Called by** (1 production · 7 test): [`media_extract`](../../src/orchestrator/cli.py#L3652), [`test_extract_image_is_noop_when_artifact_exists`](../../tests/pkg/test_media_extract.py#L120), [`test_extract_image_writes_artifact_the_reader_round_trips`](../../tests/pkg/test_media_extract.py#L110), [`test_extract_is_deterministic_for_a_pinned_version`](../../tests/pkg/test_media_extract.py#L137), [`test_extracted_labels_bind_to_real_symbols`](../../tests/pkg/test_media_extract.py#L195), [`test_extractor_unavailable_propagates`](../../tests/pkg/test_media_extract.py#L183), [`test_oversized_image_is_skipped_not_extracted`](../../tests/pkg/test_media_extract.py#L150), [`test_real_tesseract_produces_a_valid_artifact`](../../tests/pkg/test_media_extract.py#L211)
- **Calls** (6): [`ExtractResult`](../../src/orchestrator/pkg/media_extract.py#L64), [`MediaExtractorUnavailableError`](../../src/orchestrator/pkg/media_extract.py#L55), [`_file_sha256`](../../src/orchestrator/pkg/media.py#L52), [`artifact_path`](../../src/orchestrator/pkg/media.py#L64), [`build_image_artifact`](../../src/orchestrator/pkg/media_extract.py#L140), [`write_artifact`](../../src/orchestrator/pkg/media_extract.py#L174)

### `iter_image_files`

[`src/orchestrator/pkg/media_extract.py:240`](../../src/orchestrator/pkg/media_extract.py#L240)

- **Called by** (0 production · 1 test): [`test_iter_image_files_expands_dirs_images_only`](../../tests/pkg/test_media_extract.py#L170)
- **Calls** (1): [`_iter_files`](../../src/orchestrator/pkg/media_extract.py#L219)

### `iter_media_files`

[`src/orchestrator/pkg/media_extract.py:245`](../../src/orchestrator/pkg/media_extract.py#L245)

- **Called by** (1): [`media_extract`](../../src/orchestrator/cli.py#L3652)
- **Calls** (1): [`_iter_files`](../../src/orchestrator/pkg/media_extract.py#L219)

### `labels_from_ocr`

[`src/orchestrator/pkg/media_extract.py:120`](../../src/orchestrator/pkg/media_extract.py#L120)

- **Called by** (1 production · 1 test): [`build_image_artifact`](../../src/orchestrator/pkg/media_extract.py#L140), [`test_labels_from_ocr_keeps_labels_drops_noise_and_dedupes`](../../tests/pkg/test_media_extract.py#L78)

### `tesseract_version`

[`src/orchestrator/pkg/media_extract.py:85`](../../src/orchestrator/pkg/media_extract.py#L85)

- **Called by** (1 production · 1 test): [`build_image_artifact`](../../src/orchestrator/pkg/media_extract.py#L140), [`test_real_tesseract_produces_a_valid_artifact`](../../tests/pkg/test_media_extract.py#L211)
- **Calls** (3): [`MediaExtractorUnavailableError`](../../src/orchestrator/pkg/media_extract.py#L55), [`_load_pytesseract`](../../src/orchestrator/pkg/media_extract.py#L74), `get_tesseract_version`

### `write_artifact`

[`src/orchestrator/pkg/media_extract.py:174`](../../src/orchestrator/pkg/media_extract.py#L174)

- **Called by** (2): [`extract_image`](../../src/orchestrator/pkg/media_extract.py#L186), [`extract_media`](../../src/orchestrator/pkg/media_asr.py#L227)
- **Calls** (2): [`artifact_path`](../../src/orchestrator/pkg/media.py#L64), `dumps`

## Imports

`PIL.Image`, `__future__.annotations`, `dataclasses.dataclass`, `json`, [`orchestrator.pkg.media`](../../src/orchestrator/pkg/media.py#L1), `pathlib.Path`, `pytesseract`, `typing.Protocol`

## Imported by

[`orchestrator.cli`](orchestrator.cli.md), [`orchestrator.pkg.media_asr`](orchestrator.pkg.media_asr.md), [`tests.pkg.test_media_extract`](../../tests/pkg/test_media_extract.py#L1)
