Shared, settings-agnostic media primitives (object storage + job contracts) for m8 media services.
Project description
media-sdk-m8
Shared, settings-agnostic media primitives for the m8 media stack. Consumed by:
- media-service-m8 — the live API (job producer)
- media-worker-m8 — the async ARQ worker (job consumer)
The SDK is framework-agnostic (no FastAPI, no auth-sdk, no pydantic-settings) and imgtools-free. It owns no business logic, no database, and no preset knowledge — callers pass an explicit config object; the SDK never reads settings or env.
Contents
Object storage — media_sdk_m8.storage
ObjectStorage is a thin wrapper over the MinIO SDK. It is constructed from an
explicit ObjectStorageConfig (endpoint, credentials, region, TLS, and the default
presigned-URL lifetime), so it has no dependency on any service's settings module.
from media_sdk_m8 import ObjectStorage, ObjectStorageConfig
config = ObjectStorageConfig(
endpoint="minio:9000",
access_key="...",
secret_key="...",
secure=False,
region="us-east-1",
presigned_expire_seconds=300,
)
storage = ObjectStorage(config)
data = storage.get_object(bucket="private-media", object_key="key")
storage.put_object(
bucket="public-media",
object_key="key/variants/thumb_webp/thumb.webp",
data=variant_bytes,
content_type="image/webp",
)
Methods: stat_object, remove_object, get_object_head, get_object,
list_object_keys, put_object, set_object_content_type, copy_object,
post_upload_url, presigned_post_object, presigned_get_object.
list_object_keys(*, bucket, prefix="") recursively yields stored keys — the
read primitive an orphan reconciler uses to find bytes that have no DB row.
Presigned-URL expiry defaults to config.presigned_expire_seconds and can be
overridden per call via expires_seconds.
Browser-direct presigned URLs (public endpoint)
When the browser cannot resolve the internal MinIO host (e.g. minio:9000 in a
container stack), set the two optional endpoint fields:
config = ObjectStorageConfig(
endpoint="minio:9000", # internal — service and worker only
access_key="...",
secret_key="...",
secure=False,
region="us-east-1",
public_endpoint="127.0.0.1:9005", # host:port, no scheme — browser-reachable
public_secure=False, # scheme for public URLs; falls back to `secure` when None
)
public_endpoint— the host the browser hits, inhost:portform (no scheme).post_upload_urlreturns a URL built from this host/scheme;presigned_get_objectis signed by a client bound to this endpoint (SigV4 GET signatures bind the Host, so the signing client must match the endpoint the browser sends the request to).public_secure— TLS flag for the public endpoint. Falls back tosecurewhen omitted.
All internal ops (stat_object, get_object, copy_object, etc.) always use
endpoint, not public_endpoint. When public_endpoint is None (default),
post_upload_url and presigned_get_object behave identically to before —
compatible with proxy-through deployments and media-worker-m8.
Reverse-proxy requirement: a proxy (e.g. Traefik) forwarding requests to MinIO
must preserve the Host header (passHostHeader: true in Traefik, which is its
default) so the SigV4 signature validates on arrival.
Job contracts — media_sdk_m8.contracts
Self-contained Pydantic v2 models that form the producer↔consumer contract. The service builds and enqueues them; the worker deserializes and acts on them.
ScanJobPayload—{ object_id, bucket, object_key, owner_user_id }VariantSpec—{ variant_name, output_options, target_bucket, target_key };output_optionsis the imgtools-shaped dict (one format +name) built by the service, so the worker needs no preset or key knowledge.VariantJobPayload—{ job_id, media_object_id, source_bucket, source_object_key, specs }OutboxEventPayload—{ event_id, event_type, object_id, payload, created_at }; the outbound webhook contract. media-service-m8 writes one per state change to its transactional outbox and POSTs this HMAC-signed body to subscriber URLs, so a subscriber needs only this shape to verify and consume an event.
Development
pip install -e ".[dev]"
ruff format . && ruff check .
mypy media_sdk_m8 --ignore-missing-imports
bandit -r media_sdk_m8 --severity-level medium
pytest --cov=media_sdk_m8 --cov-report=term-missing --cov-fail-under=100
Published to PyPI on tagged release.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file media_sdk_m8-0.5.1.tar.gz.
File metadata
- Download URL: media_sdk_m8-0.5.1.tar.gz
- Upload date:
- Size: 52.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0d396402ca5daf48d855d39e5510c2faf9f30aa277a2c2921723027e4571aee
|
|
| MD5 |
9860e54e17e3696dd2fcbc6cfd486643
|
|
| BLAKE2b-256 |
dbe7ae6c91da47c49fad82530211ed91688c13dafc3876c31c0399ad38cb7f96
|
Provenance
The following attestation bundles were made for media_sdk_m8-0.5.1.tar.gz:
Publisher:
PiPy.yml on mano8/media-sdk-m8
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
media_sdk_m8-0.5.1.tar.gz -
Subject digest:
f0d396402ca5daf48d855d39e5510c2faf9f30aa277a2c2921723027e4571aee - Sigstore transparency entry: 2057201421
- Sigstore integration time:
-
Permalink:
mano8/media-sdk-m8@8dccfdf819f29f3253043797d3880cada087e6b0 -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/mano8
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
PiPy.yml@8dccfdf819f29f3253043797d3880cada087e6b0 -
Trigger Event:
release
-
Statement type:
File details
Details for the file media_sdk_m8-0.5.1-py3-none-any.whl.
File metadata
- Download URL: media_sdk_m8-0.5.1-py3-none-any.whl
- Upload date:
- Size: 17.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c888bb364fcaab1f851193d0fe8755b3cd8009981b6d9532cf3f93f15aa78e7f
|
|
| MD5 |
51b0462b29f901f0ccf68f4bae4ff18d
|
|
| BLAKE2b-256 |
973f98208cea3ccc01a7eec532aaf9c8cad6bc89deed8bb11d891e634db01739
|
Provenance
The following attestation bundles were made for media_sdk_m8-0.5.1-py3-none-any.whl:
Publisher:
PiPy.yml on mano8/media-sdk-m8
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
media_sdk_m8-0.5.1-py3-none-any.whl -
Subject digest:
c888bb364fcaab1f851193d0fe8755b3cd8009981b6d9532cf3f93f15aa78e7f - Sigstore transparency entry: 2057201742
- Sigstore integration time:
-
Permalink:
mano8/media-sdk-m8@8dccfdf819f29f3253043797d3880cada087e6b0 -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/mano8
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
PiPy.yml@8dccfdf819f29f3253043797d3880cada087e6b0 -
Trigger Event:
release
-
Statement type: