Fileboy for Python
The official Python client for Fileboy: upload files and videos, build image
URLs that resize on the fly, and read video data. Its only dependency is requests.
Install
pip install fileboy
Python 3.8 or newer.
Quick start
import os
import fileboy
client = fileboy.Client(public_key=os.environ["FILEBOY_PUBLIC_KEY"])
file = client.upload_file("report.pdf") # a path, a binary file object or bytes
print(file.id) # store this
client.build_file_url(file.id, download=True)
client.build_image_url(file.id, transforms=[{"type": "resize", "width": 300}, {"type": "format", "format": "webp"}])
# https://api.fileboy.io/images/get/<id>?transform=resize:300x_;format:webp
Django and other frameworks
Configure once, then call fileboy.client() anywhere:
# settings.py (or an AppConfig.ready())
import os
import fileboy
fileboy.configure(
public_key=os.environ["FILEBOY_PUBLIC_KEY"],
# private_key=..., only if your app uses signed requests
# api_url="https://api.fileboy.io",
)
# views.py
import fileboy
def upload(request):
file = fileboy.client().upload_file(request.FILES["file"], name=request.FILES["file"].name)
...
FILEBOY_PUBLIC_KEY, FILEBOY_PRIVATE_KEY and FILEBOY_API_URL are read from the environment when you do not set them.
What you can do
| Task | Method |
|---|---|
| Upload a path, file object or bytes | upload_file(file, name=, mime=, metadata=) |
| Upload from a path | upload_file_from_path(path, ...) |
| Upload from a URL | upload_file_from_url(url, name=, metadata=, expect=) |
| File details | fetch_file_data(id_or_label) |
| File URL | build_file_url(id_or_label, filename=, download=) |
| Image URL with transforms | build_image_url(id_or_label, transforms=, filename=, download=) |
| Download bytes | download_file(id_or_label, range=), download_image(id_or_label, transforms=, range=) |
| Upload a video | upload_video(file, name=, mime=) |
| Video details | fetch_video_data(id), fetch_video_info(id) |
| Manifest and poster URLs | build_video_manifest_url(id, type="hls"), build_video_poster_url(id) |
| Custom poster | upload_video_poster(video_id, image), download_video_poster(video_id) |
| Transcripts | upload_video_transcript(id, file, language="English"), fetch_video_transcript(id, transcript_id) |
| List, update and delete files (private key) | list_files(...), iter_files(...), update_file(id, ...), delete_file(id), delete_files(ids) |
| Manage videos, labels, usage (private key) | list_videos, update_video, delete_video, list_labels, create_label, delete_label, get_usage |
| Signed access token | create_access_token(read=, write=, upload=, expires_in=) |
An id that is a UUID is used as is. Anything else is treated as a label in your app, and a value
that already contains a + is passed through. Labels are created in the dashboard.
Uploads return a FileData dataclass (id, name, original_filename, mime, metadata,
file_size, content_hash, signed, dies_at, created_at). upload_video returns a Video
(id, name, raw_file_id). range is a (first_byte, last_byte) tuple.
Managing files
Listing, updating and deleting files, videos and labels needs your app's private key. With only
the public key these methods raise ConfigurationError before any request is made.
client = fileboy.Client(public_key="PUBLIC", private_key="PRIVATE")
# One page, or every match across all pages
page = client.list_files(mime="image/", limit=50)
for file in page.items:
print(file.name, file.labels)
more = client.list_files(cursor=page.next_cursor) # next_cursor is None on the last page
for file in client.iter_files(metadata={"orderId": "42"}):
print(file.id)
# Metadata is merged (None removes a key). Pass replace_metadata=True to replace it all.
client.update_file(file_id, name="new.png", metadata={"keep": "1", "drop": None},
add_labels=["hero"], remove_labels=["old"])
client.update_file("hero", name="renamed.png") # an id without a hyphen is a label
client.delete_file(file_id) # NotFoundError if it is already gone
result = client.delete_files([id_a, id_b]) # up to 100; result.deleted, .not_found, .failed
client.list_videos(status="ready")
client.update_video(video_id, name="launch.mp4")
client.delete_video(video_id)
client.list_labels()
client.create_label("hero", file_id=file_id)
client.delete_label("hero")
usage = client.get_usage() # usage.files, usage.videos, usage.storage_bytes, usage.limits.files
Errors keep the API's code and status on the exception, and api_message holds the API's own
message. NotFoundError, FileboyPermissionError/AuthenticationError and ValidationError tell
you what went wrong without reading the code.
Image transforms
transforms= takes a list of dicts applied in order. The image service supports resize,
format, quality, background and invert:
[
{"type": "resize", "width": 200, "height": 200, "fit": "cover"}, # fit: contain cover fill inside outside
{"type": "resize", "width": 400, "fit": "contain", "position": "top-left", "background": "#ffffff"},
{"type": "format", "format": "webp"}, # jpeg png webp tiff bmp
{"type": "quality", "value": 75},
{"type": "background", "color": "#fff"},
{"type": "invert"},
]
You can also pass a dict of name: value, such as {"resize": "300x_", "format": "webp", "quality": 75}, or a ready-made string.
Errors
Every error is a FileboyError with status, code (the API's error code) and body.
| Class | When |
|---|---|
AuthenticationError |
401 |
FileboyPermissionError |
403, including plan limits |
NotFoundError |
404 |
RateLimitError |
429 |
BadRequestError |
other 4xx, such as a file that is too large |
ValidationError |
the management API rejected your values (a BadRequestError) |
ServerError |
5xx |
FileboyConnectionError |
timeouts and network failures |
InvalidArgumentError, ConfigurationError |
bad arguments, before any request |
FileboyPermissionError and FileboyConnectionError are prefixed so they do not shadow the
Python built-ins PermissionError and ConnectionError.
Notes
- File, image and video reads need no authorization, so URL builders never sign anything.
- The shared
demokey only accepts small images and cannot upload video. - The client uses a
requests.Session, so connections are reused between calls.
Development
pip install -e ".[dev]"
pytest
The tests run against a local fake server. The transform builder is also checked against
packages/sdk-fixtures/transforms.json, the same cases the Go image parser is tested with.
MIT licensed.
Webhooks
Verify the requests Fileboy sends to your endpoints. Use the raw body, exactly as received:
# Django
import fileboy
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def fileboy_webhook(request):
try:
event = fileboy.verify_webhook(request.body, request.headers, settings.FILEBOY_WEBHOOK_SECRET)
except fileboy.WebhookVerificationError as error:
return HttpResponseBadRequest(error.code) # "invalid_signature", "timestamp_out_of_tolerance", ...
if event["type"] == "video.ready":
... # event["resource"]["urls"]["hls"], event["resource"]["duration"]
return HttpResponse(status=200)
verify_webhook returns the parsed event (a dict) or raises WebhookVerificationError. The timestamp
must be within 5 minutes (tolerance_seconds= changes that). Events carry a unique id that is the same
on every retry, so use it to ignore duplicates. fileboy.sign_webhook(body, timestamp, secret) builds a
signature for your own tests.
Publishing (maintainers)
You need a PyPI account that owns fileboy (the first upload claims the name) and an API token (account settings, scope: that project once it exists). Try TestPyPI first.
- Bump the version in
src/fileboy/_version.pyandpyproject.toml(keep them equal) and add a section toCHANGELOG.md. pip install -e '.[dev]' && pytestmust pass.python -m pip install build twine && rm -rf dist && python -m build && twine check dist/*- Rehearse:
twine upload --repository testpypi dist/*. - Publish:
twine upload dist/*with username__token__and the API token as the password. - Tag the commit:
git tag python-vX.Y.Z && git push origin python-vX.Y.Z.
PyPI never lets you reuse a version number, so bump it for every fix.
Metadata
Release files for fileboy 0.2.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 |
|---|---|---|---|---|
| fileboy-0.2.0.tar.gz | 31.3 kB | Without provenance | Details for fileboy-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fileboy-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for fileboy-0.2.0-py3-none-any.whl |
Total release size: 54.9 kB
Release files / fileboy-0.2.0.tar.gz
| Download URL | fileboy-0.2.0.tar.gz |
|---|---|
| Size | 31.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
657d6bfbed80d4f2d16995fd85dd428bf4f380f47badc28049695f2e3c1be406
|
|
BLAKE2b-256 checksum How to use checksums |
d06317e9fcf8316ccb7dcce934955fd8e68450d05531095c1ed0d750e6a4265b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Release files / fileboy-0.2.0-py3-none-any.whl
| Download URL | fileboy-0.2.0-py3-none-any.whl |
|---|---|
| Size | 23.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ce903748b2d5749df054e4a24d077475385890f62f5a8abe52f8557299d2849f
|
|
BLAKE2b-256 checksum How to use checksums |
6f17fe714798326409e7d8c07c979ba4903b5100588d8a7f609787f7a984572b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.6
|
Security for fileboy 0.2.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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.