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, provider-neutral wrapper over the Amazon S3 API
(SigV4), built on boto3/botocore — the reference client every S3-compatible
server is tested against. It never depends on a particular implementation: the
validated backends are SeaweedFS 4.x (default reference implementation),
Garage 2.x (validated fallback), and any other S3-compatible provider,
including MinIO. It is constructed from an explicit ObjectStorageConfig (also
exported as S3StorageConfig; 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="storage: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,
stream_object, list_object_keys, put_object, put_object_stream,
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.
stream_object and put_object_stream are the unbuffered pair. The first
yields an object's bytes in chunks; the second writes length bytes from an
open file-like object, for a payload the caller has already assembled on disk
(an archive export, say) that put_object's bytes argument would force
resident in memory.
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 storage host (e.g. storage:9000
in a container stack), set the two optional endpoint fields:
config = ObjectStorageConfig(
endpoint="storage: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 the
storage backend 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 }ExportArchiveEntry— one validated, traversal-safe source-object → ZIP-entry mapping;ExportArchiveJobPayloadcarries the authorized manifest, entries, target, chunk size and presign lifetime for delegated archive assembly.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.
Metadata
Release files for media-sdk-m8 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| media_sdk_m8-1.0.0.tar.gz | 105.9 kB | With provenance | Details for media_sdk_m8-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| media_sdk_m8-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for media_sdk_m8-1.0.0-py3-none-any.whl |
Total release size: 129.0 kB
Release files / media_sdk_m8-1.0.0.tar.gz
| Download URL | media_sdk_m8-1.0.0.tar.gz |
|---|---|
| Size | 105.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8f5cf98bfda102670087aeb96e9932ff38a7dc763c00572f920f3210f793de81
|
|
BLAKE2b-256 checksum How to use checksums |
3121cad78d58c82b207ebbc2fad6b49d9dc027e9ed6c727040e11e4c4fadc97e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logRelease files / media_sdk_m8-1.0.0-py3-none-any.whl
| Download URL | media_sdk_m8-1.0.0-py3-none-any.whl |
|---|---|
| Size | 23.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ed34182d36773e6ad9790ed7d2c9719414c0660dd6d109fae4c7c11838d7824b
|
|
BLAKE2b-256 checksum How to use checksums |
1dfc6bf4bf99df2901395328c43d9157df6eaa39f9ca1f760c4698824c4262a2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logSecurity for media-sdk-m8 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for media_sdk_m8-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.