decipher.catalog
Query a curated cetacean audio corpus over HTTPS and materialise matching clips as listenable AudioSource objects. The round-trip invariant holds: every fetched clip lands on disk as a playable WAV.
The catalog is a parquet index served by a small HTTP front door over a Modal volume. The toolkit caches the parquet locally with a short TTL; clips are cached content-addressably by sha256 and are never re-downloaded once present.
Configuration
DECIPHER_CATALOG_URL— base URL of the server. Defaults to the public endpoint.DECIPHER_CACHE_DIR— where to stash cached parquet + clips. Defaults to~/.cache/decipher.
decipher.catalog.catalog def catalog(
*,
paper_id: str | Sequence[str] | None = None,
species: str | Sequence[str] | None = None,
substrate: str | Sequence[str] | None = None,
clan: str | Sequence[str] | None = None,
min_duration_s: float | None = None,
max_duration_s: float | None = None,
label_eq: dict[str, Any] | None = None,
limit: int | None = None,
refresh: bool = False,
) -> pandas.DataFrame Return filtered catalog rows as a pandas DataFrame. Filters AND together;
string filters accept a single value or a sequence (membership). label_eq matches paper-specific JSON labels exactly.
Set refresh=True to bypass the local parquet cache.
decipher.catalog.fetch def fetch(
*,
paper_id: str | Sequence[str] | None = None,
species: str | Sequence[str] | None = None,
substrate: str | Sequence[str] | None = None,
clan: str | Sequence[str] | None = None,
min_duration_s: float | None = None,
max_duration_s: float | None = None,
label_eq: dict[str, Any] | None = None,
limit: int | None = None,
refresh: bool = False,
) -> list[AudioSource] Same filter semantics as catalog, but materialises matching
clips as AudioSource objects. Each returned object's path points into the local content-addressable cache, so
writing the samples back via write_wav yields a playable WAV.
decipher.catalog.healthcheck def healthcheck() -> dict Hit the server's /healthz and return the parsed JSON.
decipher.catalog.clear_cache def clear_cache(*, catalog_only: bool = False) -> None Delete the local parquet cache. When catalog_only=False (the default), also remove every cached clip.
Example
from decipher import catalog, fetch
# Browse without downloading audio
df = catalog(species="physeter_macrocephalus", min_duration_s=1.0, limit=20)
print(df[["paper_id", "clip_id", "duration_s"]].head())
# Download + load a handful as AudioSources
clips = fetch(paper_id="005_sharma_2024", limit=5)
for c in clips:
print(c.path, c.sr, c.duration_s)
What fetch returns
Each of these was produced by one fetch(...) call — the
clip is cached content-addressably by sha256 at ~/.cache/decipher/clips/<sha>.wav, so a second call hits
the local cache.
Catalog at a glance