mirror of
https://github.com/blw1138/Zordon.git
synced 2026-06-09 13:39:24 -05:00
More API cleanup
This commit is contained in:
+514
@@ -0,0 +1,514 @@
|
|||||||
|
# Zordon API Reference
|
||||||
|
|
||||||
|
Zordon exposes a Flask API from `src/api/api_server.py`. The server is started by
|
||||||
|
`start_api_server()` and listens on `Config.port_number` with all application
|
||||||
|
routes mounted under `/api`.
|
||||||
|
|
||||||
|
The in-repo client wrapper is `src/api/server_proxy.py`. Most UI and distributed
|
||||||
|
rendering code should prefer `RenderServerProxy` instead of constructing request
|
||||||
|
URLs directly.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
- Current API version: `0.1`
|
||||||
|
- `RenderServerProxy.request()` sends `X-API-Version` with the current
|
||||||
|
`API_VERSION`, but the server does not currently validate this header.
|
||||||
|
|
||||||
|
## Response Conventions
|
||||||
|
|
||||||
|
- JSON endpoints return Flask-serialized dictionaries or lists.
|
||||||
|
- File endpoints return `send_file()` responses.
|
||||||
|
- Most error responses are plain text with HTTP `400`, `404`, `500`, or `503`.
|
||||||
|
- `JobNotFoundError` is mapped to HTTP `400`.
|
||||||
|
- Unhandled exceptions are mapped to HTTP `500` with a plain-text message.
|
||||||
|
|
||||||
|
## Jobs
|
||||||
|
|
||||||
|
### `GET /api/jobs`
|
||||||
|
|
||||||
|
Returns all render jobs and a cache token.
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"jobs": [
|
||||||
|
{
|
||||||
|
"id": "job-id",
|
||||||
|
"name": "job name",
|
||||||
|
"status": "running"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"token": "cache-token"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_all_jobs()`
|
||||||
|
- `src/ui/main_window.py`
|
||||||
|
|
||||||
|
### `GET /api/jobs_long_poll`
|
||||||
|
|
||||||
|
Long-polls the job list until the supplied cache token changes or 30 seconds
|
||||||
|
elapse.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `token` | No | Cache token returned by `/api/jobs`. |
|
||||||
|
|
||||||
|
Responses:
|
||||||
|
|
||||||
|
- `200` with the same shape as `/api/jobs` when jobs changed.
|
||||||
|
- `204` with an empty body when no changes arrive before timeout.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_all_jobs()` through the background cache updater.
|
||||||
|
|
||||||
|
### `GET /api/jobs/<status_val>`
|
||||||
|
|
||||||
|
Returns jobs matching a render status.
|
||||||
|
|
||||||
|
Path parameters:
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `status_val` | Status string converted by `string_to_status()`. |
|
||||||
|
|
||||||
|
Responses:
|
||||||
|
|
||||||
|
- `200` with a list of job JSON objects when matches exist.
|
||||||
|
- `400` when no jobs match the requested status.
|
||||||
|
|
||||||
|
Review note: this route is not currently wrapped by `RenderServerProxy` and no
|
||||||
|
in-repo callers were found.
|
||||||
|
|
||||||
|
### `GET /api/job/<job_id>`
|
||||||
|
|
||||||
|
Returns one job as JSON.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_job_info()`
|
||||||
|
- `add_job.py`
|
||||||
|
- `src/ui/main_window.py`
|
||||||
|
- `src/distributed_job_manager.py`
|
||||||
|
- `tests/job_creation_tests.py`
|
||||||
|
|
||||||
|
### `GET /api/job/<job_id>/logs`
|
||||||
|
|
||||||
|
Returns the job log file as `text/plain`.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `src/ui/main_window.py` opens this URL directly.
|
||||||
|
|
||||||
|
### `GET /api/job/<job_id>/file_list`
|
||||||
|
|
||||||
|
Returns a list of output filenames for the job.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_job_files_list()`
|
||||||
|
- `src/utilities/server_helper.py`
|
||||||
|
|
||||||
|
### `GET /api/job/<job_id>/download`
|
||||||
|
|
||||||
|
Downloads one output file.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `filename` | Yes | Case-insensitive filename from the job file list. |
|
||||||
|
|
||||||
|
Responses:
|
||||||
|
|
||||||
|
- `200` with the requested file as an attachment.
|
||||||
|
- `400` when `filename` is missing.
|
||||||
|
- `404` when the file is not found.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.download_job_file()`
|
||||||
|
- `src/utilities/server_helper.py`
|
||||||
|
|
||||||
|
### `GET /api/job/<job_id>/download_all`
|
||||||
|
|
||||||
|
Creates a temporary zip of the job output directory and downloads it.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.download_all_job_files()`
|
||||||
|
- `src/ui/main_window.py`
|
||||||
|
- `src/utilities/server_helper.py`
|
||||||
|
|
||||||
|
### `GET /api/job/<job_id>/thumbnail`
|
||||||
|
|
||||||
|
Returns a generated preview image or video for a job.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `size` | No | `big` selects the larger preview path. Currently parsed but not applied. |
|
||||||
|
| `video_ok` | No | If truthy and a video preview exists, video can be returned. |
|
||||||
|
|
||||||
|
Responses:
|
||||||
|
|
||||||
|
- `200` with `image/jpeg` or `video/mp4`.
|
||||||
|
- `404` when no thumbnail is available.
|
||||||
|
- `500` on preview generation errors.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `src/ui/main_window.py`
|
||||||
|
|
||||||
|
Review note: `size=big` is parsed into `big_thumb` but not used.
|
||||||
|
|
||||||
|
## Job Lifecycle
|
||||||
|
|
||||||
|
### `POST /api/add_job`
|
||||||
|
|
||||||
|
Adds one or more render jobs.
|
||||||
|
|
||||||
|
Request formats:
|
||||||
|
|
||||||
|
- JSON request body.
|
||||||
|
- Multipart form with a `json` field and optional `file` upload.
|
||||||
|
|
||||||
|
Common job fields include:
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `name` | Display name for the render job. |
|
||||||
|
| `renderer` | Render engine name such as `blender` or `ffmpeg`. |
|
||||||
|
| `start_frame` | First frame to render. |
|
||||||
|
| `end_frame` | Last frame to render. |
|
||||||
|
| `args` | Engine-specific render arguments. |
|
||||||
|
| `enable_split_jobs` | Whether distributed subjobs may be created. |
|
||||||
|
| `child_jobs` | Optional subjob definitions. |
|
||||||
|
| `local_path` | Local file path used when posting to localhost. |
|
||||||
|
|
||||||
|
Responses:
|
||||||
|
|
||||||
|
- `200` with created job data.
|
||||||
|
- `400` for invalid or missing job data.
|
||||||
|
- `500` for unexpected processing or creation errors.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.post_job_to_server()`
|
||||||
|
- `add_job.py`
|
||||||
|
- `src/ui/add_job_window.py`
|
||||||
|
- `src/distributed_job_manager.py`
|
||||||
|
- `tests/job_creation_tests.py`
|
||||||
|
|
||||||
|
### `POST /api/job/<job_id>/cancel`
|
||||||
|
|
||||||
|
Cancels a job.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `confirm` | Yes | Must be truthy or the request is rejected. |
|
||||||
|
| `redirect` | No | If truthy, redirects to `index`. |
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.cancel_job()`
|
||||||
|
- `src/ui/main_window.py`
|
||||||
|
- `src/distributed_job_manager.py`
|
||||||
|
|
||||||
|
### `POST /api/job/<job_id>/delete`
|
||||||
|
|
||||||
|
Deletes a job, stops it first, deletes previews, and removes owned upload/output
|
||||||
|
directories when safe.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `confirm` | Yes | Must be truthy or the request is rejected. |
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.delete_job()`
|
||||||
|
- `src/ui/main_window.py`
|
||||||
|
|
||||||
|
### `POST /api/job/<job_id>/send_subjob_update_notification`
|
||||||
|
|
||||||
|
Notifies a parent job that a child/subjob changed state.
|
||||||
|
|
||||||
|
Request body:
|
||||||
|
|
||||||
|
- JSON representation of a subjob.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.send_subjob_update_notification()`
|
||||||
|
- `src/distributed_job_manager.py`
|
||||||
|
|
||||||
|
## Status and Environment
|
||||||
|
|
||||||
|
### `GET /api/heartbeat`
|
||||||
|
|
||||||
|
Returns the current timestamp as plain text. Used for fast connectivity checks.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.check_connection()`
|
||||||
|
|
||||||
|
### `GET /api/status`
|
||||||
|
|
||||||
|
Returns local system and queue status.
|
||||||
|
|
||||||
|
Response includes:
|
||||||
|
|
||||||
|
- timestamp
|
||||||
|
- operating system and version
|
||||||
|
- CPU brand, count, and current utilization
|
||||||
|
- memory totals and current utilization
|
||||||
|
- job counts
|
||||||
|
- hostname and port
|
||||||
|
- app and API versions
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_status()`
|
||||||
|
|
||||||
|
### `GET /api/snapshot`
|
||||||
|
|
||||||
|
Returns local status, all jobs, and a timestamp.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `full_status()` internally.
|
||||||
|
|
||||||
|
Review note: no direct in-repo HTTP callers were found. It overlaps with
|
||||||
|
`/api/status` plus `/api/jobs`.
|
||||||
|
|
||||||
|
### `GET /api/full_status`
|
||||||
|
|
||||||
|
Returns a multi-server shaped status payload with the local server populated.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_data()`
|
||||||
|
- `dashboard.py`
|
||||||
|
|
||||||
|
Review note: this currently reports only the local server. The response shape
|
||||||
|
suggests an intended future aggregation point.
|
||||||
|
|
||||||
|
### `GET /api/presets`
|
||||||
|
|
||||||
|
Returns `config/presets.yaml`.
|
||||||
|
|
||||||
|
Review note: no in-repo callers were found.
|
||||||
|
|
||||||
|
### `GET /api/cpu_benchmark`
|
||||||
|
|
||||||
|
Runs a CPU benchmark for 10 seconds and returns the score as plain text.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `src/utilities/server_helper.py`
|
||||||
|
|
||||||
|
### `GET /api/disk_benchmark`
|
||||||
|
|
||||||
|
Runs a disk I/O benchmark and returns write/read speeds.
|
||||||
|
|
||||||
|
Review note: no in-repo callers were found.
|
||||||
|
|
||||||
|
## Engines
|
||||||
|
|
||||||
|
### `GET /api/engines/for_filename`
|
||||||
|
|
||||||
|
Returns the engine name suitable for a project filename.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `filename` | Yes | Project filename or path. The client currently sends only the basename. |
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_engine_for_filename()`
|
||||||
|
- `src/ui/add_job_window.py`
|
||||||
|
|
||||||
|
### `GET /api/engines`
|
||||||
|
|
||||||
|
Returns installed engine data keyed by engine name.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `response_type` | No | `standard` or `full`; defaults to `standard`. |
|
||||||
|
|
||||||
|
`full` responses also include supported extensions, supported export formats,
|
||||||
|
system info, and UI options.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_engines()`
|
||||||
|
- `src/ui/settings_window.py`
|
||||||
|
- `src/ui/engine_browser.py`
|
||||||
|
|
||||||
|
### `GET /api/engines/names`
|
||||||
|
|
||||||
|
Returns installed engine names as a list without instantiating engine classes.
|
||||||
|
Use this for lightweight selection UIs that only need engine names.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_engine_names()`
|
||||||
|
- `src/ui/add_job_window.py`
|
||||||
|
|
||||||
|
### `GET /api/engines/<engine_name>`
|
||||||
|
|
||||||
|
Returns installed version data for a single engine.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `response_type` | No | `standard` or `full`; defaults to `standard`. |
|
||||||
|
|
||||||
|
`full` responses also include supported extensions, supported export formats,
|
||||||
|
system info, and UI options.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_engine()`
|
||||||
|
- `src/ui/add_job_window.py`
|
||||||
|
|
||||||
|
### `GET /api/engines/<engine_name>/availability`
|
||||||
|
|
||||||
|
Returns whether an engine can accept jobs on this server, plus CPU count,
|
||||||
|
installed versions, and hostname.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.get_engine_availability()`
|
||||||
|
- `src/distributed_job_manager.py`
|
||||||
|
|
||||||
|
### `GET /api/engines/<engine_name>/args`
|
||||||
|
|
||||||
|
Returns engine arguments.
|
||||||
|
|
||||||
|
Review note: no in-repo callers were found.
|
||||||
|
|
||||||
|
### `GET /api/engines/<engine_name>/help`
|
||||||
|
|
||||||
|
Returns engine help text.
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `src/ui/add_job_window.py` opens this URL directly.
|
||||||
|
|
||||||
|
### `GET /api/engines/download_available`
|
||||||
|
|
||||||
|
Checks whether a managed engine version is available to download.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `engine` | Yes | Engine name. |
|
||||||
|
| `version` | Yes | Engine version. |
|
||||||
|
| `system_os` | No | Target OS. |
|
||||||
|
| `cpu` | No | Target CPU architecture. |
|
||||||
|
|
||||||
|
Review note: no in-repo callers were found.
|
||||||
|
|
||||||
|
### `GET /api/engines/most_recent_version`
|
||||||
|
|
||||||
|
Finds the most recent downloadable version for an engine.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `engine` | Yes | Engine name. |
|
||||||
|
| `system_os` | No | Target OS. |
|
||||||
|
| `cpu` | No | Target CPU architecture. |
|
||||||
|
|
||||||
|
Review note: no in-repo callers were found.
|
||||||
|
|
||||||
|
### `POST /api/engines/download`
|
||||||
|
|
||||||
|
Downloads a managed engine version.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `engine` | Yes | Engine name. |
|
||||||
|
| `version` | Yes | Engine version. |
|
||||||
|
| `system_os` | No | Target OS. |
|
||||||
|
| `cpu` | No | Target CPU architecture. |
|
||||||
|
|
||||||
|
Review note: no in-repo callers were found. Settings currently calls
|
||||||
|
`EngineManager.download_engine()` directly instead of this API route.
|
||||||
|
|
||||||
|
### `POST /api/engines/delete`
|
||||||
|
|
||||||
|
Deletes a managed engine download.
|
||||||
|
|
||||||
|
JSON body:
|
||||||
|
|
||||||
|
| Name | Required | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `engine` | Yes | Engine name. |
|
||||||
|
| `version` | Yes | Engine version. |
|
||||||
|
| `system_os` | No | Target OS. |
|
||||||
|
| `cpu` | No | Target CPU architecture. |
|
||||||
|
|
||||||
|
Known callers:
|
||||||
|
|
||||||
|
- `RenderServerProxy.delete_engine_download()`
|
||||||
|
- `src/ui/engine_browser.py`
|
||||||
|
|
||||||
|
## Debug
|
||||||
|
|
||||||
|
### `GET /api/_debug/detected_clients`
|
||||||
|
|
||||||
|
Returns hostnames detected by Zeroconf.
|
||||||
|
|
||||||
|
Review note: development/debug only, with an inline comment saying it probably
|
||||||
|
should not ship.
|
||||||
|
|
||||||
|
### `POST /api/_debug/clear_history`
|
||||||
|
|
||||||
|
Clears render queue history and returns `success`.
|
||||||
|
|
||||||
|
Review note: development/debug only.
|
||||||
|
|
||||||
|
## Redundancy and Cleanup Review
|
||||||
|
|
||||||
|
Likely redundant or overlapping routes:
|
||||||
|
|
||||||
|
- `/api/snapshot` overlaps with `/api/status` and `/api/jobs`. It is currently
|
||||||
|
used internally by `/api/full_status`.
|
||||||
|
Routes with no in-repo callers found:
|
||||||
|
|
||||||
|
- `GET /api/jobs/<status_val>`
|
||||||
|
- `GET /api/presets`
|
||||||
|
- `GET /api/disk_benchmark`
|
||||||
|
- `GET /api/engines/<engine_name>/args`
|
||||||
|
- `GET /api/engines/download_available`
|
||||||
|
- `GET /api/engines/most_recent_version`
|
||||||
|
- `POST /api/engines/download`
|
||||||
|
- `GET /api/_debug/detected_clients`
|
||||||
|
- `POST /api/_debug/clear_history`
|
||||||
|
|
||||||
|
Routes or methods with cleanup risks:
|
||||||
|
|
||||||
|
- `job_thumbnail()` parses `size=big` but never uses the resulting `big_thumb`
|
||||||
|
value.
|
||||||
+596
@@ -0,0 +1,596 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Zordon API Reference</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #0d1117;
|
||||||
|
--surface: #161b22;
|
||||||
|
--surface2: #1c2333;
|
||||||
|
--border: #30363d;
|
||||||
|
--text: #e6edf3;
|
||||||
|
--text-muted: #8b949e;
|
||||||
|
--blue: #58a6ff;
|
||||||
|
--green: #3fb950;
|
||||||
|
--orange: #d29922;
|
||||||
|
--red: #f85149;
|
||||||
|
--purple: #bc8cff;
|
||||||
|
--cyan: #39d2c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
padding: 56px 24px 36px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
font-size: 2.7rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
header p {
|
||||||
|
max-width: 760px;
|
||||||
|
margin: 0 auto;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 250px minmax(0, 1fr);
|
||||||
|
gap: 32px;
|
||||||
|
max-width: 1180px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 36px 24px 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
position: sticky;
|
||||||
|
top: 18px;
|
||||||
|
align-self: start;
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
display: block;
|
||||||
|
padding: 5px 0;
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: .92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover { color: var(--blue); }
|
||||||
|
|
||||||
|
section { margin-bottom: 44px; }
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0 18px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 2px solid var(--blue);
|
||||||
|
font-size: 1.45rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
color: var(--cyan);
|
||||||
|
font-size: 1.08rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { color: var(--text-muted); }
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||||||
|
background: var(--surface2);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
color: var(--cyan);
|
||||||
|
font-size: .9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin: 12px 0;
|
||||||
|
padding: 14px;
|
||||||
|
overflow-x: auto;
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 12px 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: .92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 9px 10px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: var(--surface2);
|
||||||
|
color: var(--text);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
td { color: var(--text-muted); }
|
||||||
|
|
||||||
|
.meta-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel, .endpoint {
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel h3 { color: var(--text); }
|
||||||
|
.panel ul { margin: 8px 0 0; padding-left: 20px; }
|
||||||
|
|
||||||
|
.endpoint {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.endpoint-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 52px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: .76rem;
|
||||||
|
letter-spacing: .02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.get { background: rgba(88, 166, 255, .16); color: var(--blue); border: 1px solid rgba(88, 166, 255, .35); }
|
||||||
|
.post { background: rgba(63, 185, 80, .16); color: var(--green); border: 1px solid rgba(63, 185, 80, .35); }
|
||||||
|
.multi { background: rgba(210, 153, 34, .16); color: var(--orange); border: 1px solid rgba(210, 153, 34, .35); }
|
||||||
|
|
||||||
|
.path {
|
||||||
|
color: var(--text);
|
||||||
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||||||
|
font-weight: 650;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-left: 3px solid var(--orange);
|
||||||
|
background: rgba(210, 153, 34, .08);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.danger {
|
||||||
|
border-left-color: var(--red);
|
||||||
|
background: rgba(248, 81, 73, .08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.callers {
|
||||||
|
margin: 10px 0 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callers li { margin: 2px 0; }
|
||||||
|
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
main { grid-template-columns: 1fr; }
|
||||||
|
nav { position: static; }
|
||||||
|
header h1 { font-size: 2.1rem; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Zordon API Reference</h1>
|
||||||
|
<p>Flask endpoints exposed by <code>src/api/api_server.py</code>, with the in-repo client wrapper in <code>src/api/server_proxy.py</code>.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<nav aria-label="API sections">
|
||||||
|
<strong>Sections</strong>
|
||||||
|
<a href="#overview">Overview</a>
|
||||||
|
<a href="#jobs">Jobs</a>
|
||||||
|
<a href="#job-lifecycle">Job Lifecycle</a>
|
||||||
|
<a href="#status">Status and Environment</a>
|
||||||
|
<a href="#engines">Engines</a>
|
||||||
|
<a href="#debug">Debug</a>
|
||||||
|
<a href="#cleanup">Cleanup Review</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<section id="overview">
|
||||||
|
<h2>Overview</h2>
|
||||||
|
<div class="meta-grid">
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Versioning</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Current API version: <code>0.1</code></li>
|
||||||
|
<li><code>RenderServerProxy.request()</code> sends <code>X-API-Version</code>.</li>
|
||||||
|
<li>The server does not currently validate that header.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Response Conventions</h3>
|
||||||
|
<ul>
|
||||||
|
<li>JSON endpoints return Flask-serialized dictionaries or lists.</li>
|
||||||
|
<li>File endpoints return <code>send_file()</code> responses.</li>
|
||||||
|
<li>Most errors are plain text with <code>400</code>, <code>404</code>, <code>500</code>, or <code>503</code>.</li>
|
||||||
|
<li><code>JobNotFoundError</code> maps to HTTP <code>400</code>.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="jobs">
|
||||||
|
<h2>Jobs</h2>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/jobs</span></div>
|
||||||
|
<p>Returns all render jobs and a cache token.</p>
|
||||||
|
<pre><code>{
|
||||||
|
"jobs": [{"id": "job-id", "name": "job name", "status": "running"}],
|
||||||
|
"token": "cache-token"
|
||||||
|
}</code></pre>
|
||||||
|
<p>Known callers:</p>
|
||||||
|
<ul class="callers">
|
||||||
|
<li><code>RenderServerProxy.get_all_jobs()</code></li>
|
||||||
|
<li><code>src/ui/main_window.py</code></li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/jobs_long_poll</span></div>
|
||||||
|
<p>Long-polls the job list until the supplied cache token changes or 30 seconds elapse.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>token</code></td><td>No</td><td>Cache token returned by <code>/api/jobs</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p>Returns <code>200</code> with job data when changed, or <code>204</code> when no change arrives before timeout.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/jobs/<status_val></span></div>
|
||||||
|
<p>Returns jobs matching a render status converted by <code>string_to_status()</code>.</p>
|
||||||
|
<div class="note">No <code>RenderServerProxy</code> wrapper or in-repo caller was found.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/job/<job_id></span></div>
|
||||||
|
<p>Returns one job as JSON.</p>
|
||||||
|
<p>Known callers include <code>RenderServerProxy.get_job_info()</code>, <code>add_job.py</code>, <code>src/ui/main_window.py</code>, <code>src/distributed_job_manager.py</code>, and <code>tests/job_creation_tests.py</code>.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/job/<job_id>/logs</span></div>
|
||||||
|
<p>Returns the job log file as <code>text/plain</code>. The main window opens this URL directly.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/job/<job_id>/file_list</span></div>
|
||||||
|
<p>Returns a list of output filenames for the job.</p>
|
||||||
|
<p>Known callers: <code>RenderServerProxy.get_job_files_list()</code> and <code>src/utilities/server_helper.py</code>.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/job/<job_id>/download</span></div>
|
||||||
|
<p>Downloads one output file.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>filename</code></td><td>Yes</td><td>Case-insensitive filename from the job file list.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p>Returns <code>200</code> with an attachment, <code>400</code> when <code>filename</code> is missing, or <code>404</code> when the file is not found.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/job/<job_id>/download_all</span></div>
|
||||||
|
<p>Creates a temporary zip of the job output directory and downloads it.</p>
|
||||||
|
<p>Known callers: <code>RenderServerProxy.download_all_job_files()</code>, <code>src/ui/main_window.py</code>, and <code>src/utilities/server_helper.py</code>.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/job/<job_id>/thumbnail</span></div>
|
||||||
|
<p>Returns a generated preview image or video for a job.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>size</code></td><td>No</td><td><code>big</code> is parsed but not currently applied.</td></tr>
|
||||||
|
<tr><td><code>video_ok</code></td><td>No</td><td>If truthy and a video preview exists, video can be returned.</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">Cleanup note: <code>size=big</code> is parsed into <code>big_thumb</code> but not used.</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="job-lifecycle">
|
||||||
|
<h2>Job Lifecycle</h2>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method post">POST</span><span class="path">/api/add_job</span></div>
|
||||||
|
<p>Adds one or more render jobs. Accepts either a JSON request body or multipart form data with a <code>json</code> field and optional <code>file</code> upload.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Common Field</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>name</code></td><td>Display name for the render job.</td></tr>
|
||||||
|
<tr><td><code>renderer</code></td><td>Render engine name such as <code>blender</code> or <code>ffmpeg</code>.</td></tr>
|
||||||
|
<tr><td><code>start_frame</code></td><td>First frame to render.</td></tr>
|
||||||
|
<tr><td><code>end_frame</code></td><td>Last frame to render.</td></tr>
|
||||||
|
<tr><td><code>args</code></td><td>Engine-specific render arguments.</td></tr>
|
||||||
|
<tr><td><code>enable_split_jobs</code></td><td>Whether distributed subjobs may be created.</td></tr>
|
||||||
|
<tr><td><code>child_jobs</code></td><td>Optional subjob definitions.</td></tr>
|
||||||
|
<tr><td><code>local_path</code></td><td>Local file path used when posting to localhost.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p>Known callers include <code>RenderServerProxy.post_job_to_server()</code>, <code>add_job.py</code>, <code>src/ui/add_job_window.py</code>, <code>src/distributed_job_manager.py</code>, and integration tests.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method post">POST</span><span class="path">/api/job/<job_id>/cancel</span></div>
|
||||||
|
<p>Cancels a job. Requires a truthy <code>confirm</code> query parameter.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>confirm</code></td><td>Yes</td><td>Must be truthy or the request is rejected.</td></tr>
|
||||||
|
<tr><td><code>redirect</code></td><td>No</td><td>If truthy, redirects to <code>index</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method post">POST</span><span class="path">/api/job/<job_id>/delete</span></div>
|
||||||
|
<p>Deletes a job, stops it first, deletes previews, and removes owned upload/output directories when safe.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>confirm</code></td><td>Yes</td><td>Must be truthy or the request is rejected.</td></tr>
|
||||||
|
</table>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method post">POST</span><span class="path">/api/job/<job_id>/send_subjob_update_notification</span></div>
|
||||||
|
<p>Notifies a parent job that a child/subjob changed state. The request body is the JSON representation of the subjob.</p>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="status">
|
||||||
|
<h2>Status and Environment</h2>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/heartbeat</span></div>
|
||||||
|
<p>Returns the current timestamp as plain text. Used by <code>RenderServerProxy.check_connection()</code>.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/status</span></div>
|
||||||
|
<p>Returns local system and queue status, including operating system, CPU, memory, job counts, hostname, port, app version, and API version.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/snapshot</span></div>
|
||||||
|
<p>Returns local status, all jobs, and a timestamp.</p>
|
||||||
|
<div class="note">No direct in-repo HTTP callers were found. It overlaps with <code>/api/status</code> plus <code>/api/jobs</code> and is used internally by <code>/api/full_status</code>.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/full_status</span></div>
|
||||||
|
<p>Returns a multi-server shaped status payload with the local server populated. Used by <code>RenderServerProxy.get_data()</code> and <code>dashboard.py</code>.</p>
|
||||||
|
<div class="note">The response shape suggests an intended future aggregation point, but it currently reports only the local server.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/presets</span></div>
|
||||||
|
<p>Returns <code>config/presets.yaml</code>.</p>
|
||||||
|
<div class="note">No in-repo callers were found.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/cpu_benchmark</span></div>
|
||||||
|
<p>Runs a CPU benchmark for 10 seconds and returns the score as plain text. Used by <code>src/utilities/server_helper.py</code>.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/disk_benchmark</span></div>
|
||||||
|
<p>Runs a disk I/O benchmark and returns write/read speeds.</p>
|
||||||
|
<div class="note">No in-repo callers were found.</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="engines">
|
||||||
|
<h2>Engines</h2>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines/for_filename</span></div>
|
||||||
|
<p>Returns the engine name suitable for a project filename.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>filename</code></td><td>Yes</td><td>Project filename or path. The client currently sends only the basename.</td></tr>
|
||||||
|
</table>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines</span></div>
|
||||||
|
<p>Returns installed engine data keyed by engine name.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>response_type</code></td><td>No</td><td><code>standard</code> or <code>full</code>; defaults to <code>standard</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p><code>full</code> responses include supported extensions, supported export formats, system info, and UI options.</p>
|
||||||
|
<p>Known callers: <code>RenderServerProxy.get_engines()</code>, <code>src/ui/settings_window.py</code>, and <code>src/ui/engine_browser.py</code>.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines/names</span></div>
|
||||||
|
<p>Returns installed engine names as a list without instantiating engine classes. Use this for lightweight selection UIs that only need engine names.</p>
|
||||||
|
<p>Known callers: <code>RenderServerProxy.get_engine_names()</code> and <code>src/ui/add_job_window.py</code>.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines/<engine_name></span></div>
|
||||||
|
<p>Returns installed version data for a single engine.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>response_type</code></td><td>No</td><td><code>standard</code> or <code>full</code>; defaults to <code>standard</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p><code>full</code> responses include supported extensions, supported export formats, system info, and UI options.</p>
|
||||||
|
<p>Known caller: <code>RenderServerProxy.get_engine()</code> in the add-job window.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines/<engine_name>/availability</span></div>
|
||||||
|
<p>Returns whether an engine can accept jobs on this server, plus CPU count, installed versions, and hostname.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines/<engine_name>/args</span></div>
|
||||||
|
<p>Returns engine arguments.</p>
|
||||||
|
<div class="note">No in-repo callers were found.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines/<engine_name>/help</span></div>
|
||||||
|
<p>Returns engine help text. The add-job window opens this URL directly.</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines/download_available</span></div>
|
||||||
|
<p>Checks whether a managed engine version is available to download.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>engine</code></td><td>Yes</td><td>Engine name.</td></tr>
|
||||||
|
<tr><td><code>version</code></td><td>Yes</td><td>Engine version.</td></tr>
|
||||||
|
<tr><td><code>system_os</code></td><td>No</td><td>Target OS.</td></tr>
|
||||||
|
<tr><td><code>cpu</code></td><td>No</td><td>Target CPU architecture.</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">No in-repo callers were found.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/engines/most_recent_version</span></div>
|
||||||
|
<p>Finds the most recent downloadable version for an engine.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>engine</code></td><td>Yes</td><td>Engine name.</td></tr>
|
||||||
|
<tr><td><code>system_os</code></td><td>No</td><td>Target OS.</td></tr>
|
||||||
|
<tr><td><code>cpu</code></td><td>No</td><td>Target CPU architecture.</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">No in-repo callers were found.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method post">POST</span><span class="path">/api/engines/download</span></div>
|
||||||
|
<p>Downloads a managed engine version.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Query Parameter</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>engine</code></td><td>Yes</td><td>Engine name.</td></tr>
|
||||||
|
<tr><td><code>version</code></td><td>Yes</td><td>Engine version.</td></tr>
|
||||||
|
<tr><td><code>system_os</code></td><td>No</td><td>Target OS.</td></tr>
|
||||||
|
<tr><td><code>cpu</code></td><td>No</td><td>Target CPU architecture.</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">Settings currently calls <code>EngineManager.download_engine()</code> directly instead of this API route.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method post">POST</span><span class="path">/api/engines/delete</span></div>
|
||||||
|
<p>Deletes a managed engine download.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>JSON Field</th><th>Required</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>engine</code></td><td>Yes</td><td>Engine name.</td></tr>
|
||||||
|
<tr><td><code>version</code></td><td>Yes</td><td>Engine version.</td></tr>
|
||||||
|
<tr><td><code>system_os</code></td><td>No</td><td>Target OS.</td></tr>
|
||||||
|
<tr><td><code>cpu</code></td><td>No</td><td>Target CPU architecture.</td></tr>
|
||||||
|
</table>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="debug">
|
||||||
|
<h2>Debug</h2>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method get">GET</span><span class="path">/api/_debug/detected_clients</span></div>
|
||||||
|
<p>Returns hostnames detected by Zeroconf.</p>
|
||||||
|
<div class="note">Development/debug only, with an inline comment saying it probably should not ship.</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="endpoint">
|
||||||
|
<div class="endpoint-header"><span class="method post">POST</span><span class="path">/api/_debug/clear_history</span></div>
|
||||||
|
<p>Clears render queue history and returns <code>success</code>.</p>
|
||||||
|
<div class="note">Development/debug only.</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="cleanup">
|
||||||
|
<h2>Cleanup Review</h2>
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Likely Redundant Or Overlapping Routes</h3>
|
||||||
|
<ul>
|
||||||
|
<li><code>/api/snapshot</code> overlaps with <code>/api/status</code> and <code>/api/jobs</code>. It is currently used internally by <code>/api/full_status</code>.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel" style="margin-top: 14px;">
|
||||||
|
<h3>Routes With No In-Repo Callers Found</h3>
|
||||||
|
<ul>
|
||||||
|
<li><code>GET /api/jobs/<status_val></code></li>
|
||||||
|
<li><code>GET /api/presets</code></li>
|
||||||
|
<li><code>GET /api/disk_benchmark</code></li>
|
||||||
|
<li><code>GET /api/engines/<engine_name>/args</code></li>
|
||||||
|
<li><code>GET /api/engines/download_available</code></li>
|
||||||
|
<li><code>GET /api/engines/most_recent_version</code></li>
|
||||||
|
<li><code>POST /api/engines/download</code></li>
|
||||||
|
<li><code>GET /api/_debug/detected_clients</code></li>
|
||||||
|
<li><code>POST /api/_debug/clear_history</code></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel" style="margin-top: 14px;">
|
||||||
|
<h3>Cleanup Risks</h3>
|
||||||
|
<ul>
|
||||||
|
<li><code>job_thumbnail()</code> parses <code>size=big</code> but never uses the resulting <code>big_thumb</code> value.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+13
-9
@@ -370,7 +370,7 @@ def delete_job(job_id):
|
|||||||
# Engine Info and Management:
|
# Engine Info and Management:
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
|
|
||||||
@server.get('/api/engine_for_filename')
|
@server.get('/api/engines/for_filename')
|
||||||
def get_engine_for_filename():
|
def get_engine_for_filename():
|
||||||
filename = request.args.get("filename")
|
filename = request.args.get("filename")
|
||||||
if not filename:
|
if not filename:
|
||||||
@@ -463,27 +463,31 @@ def get_engine(engine_name):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
@server.get('/api/<engine_name>/is_available')
|
@server.get('/api/engines/<engine_name>/availability')
|
||||||
def get_engine_availability(engine_name):
|
def get_engine_availability(engine_name):
|
||||||
return {'engine': engine_name, 'available': RenderQueue.is_available_for_job(engine_name),
|
return {'engine': engine_name, 'available': RenderQueue.is_available_for_job(engine_name),
|
||||||
'cpu_count': int(psutil.cpu_count(logical=False)),
|
'cpu_count': int(psutil.cpu_count(logical=False)),
|
||||||
'versions': EngineManager.all_version_data_for_engine(engine_name),
|
'versions': EngineManager.all_version_data_for_engine(engine_name),
|
||||||
'hostname': server.config['HOSTNAME']}
|
'hostname': server.config.get('HOSTNAME', socket.gethostname())}
|
||||||
|
|
||||||
|
|
||||||
@server.get('/api/engine/<engine_name>/args')
|
@server.get('/api/engines/<engine_name>/args')
|
||||||
def get_engine_args(engine_name):
|
def get_engine_args(engine_name):
|
||||||
try:
|
try:
|
||||||
engine_class = EngineManager.engine_class_with_name(engine_name)
|
engine_class = EngineManager.engine_class_with_name(engine_name)
|
||||||
|
if not engine_class:
|
||||||
|
return f"Cannot find engine '{engine_name}'", 400
|
||||||
return engine_class().get_arguments()
|
return engine_class().get_arguments()
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return f"Cannot find engine '{engine_name}'", 400
|
return f"Cannot find engine '{engine_name}'", 400
|
||||||
|
|
||||||
|
|
||||||
@server.get('/api/engine/<engine_name>/help')
|
@server.get('/api/engines/<engine_name>/help')
|
||||||
def get_engine_help(engine_name):
|
def get_engine_help(engine_name):
|
||||||
try:
|
try:
|
||||||
engine_class = EngineManager.engine_class_with_name(engine_name)
|
engine_class = EngineManager.engine_class_with_name(engine_name)
|
||||||
|
if not engine_class:
|
||||||
|
return f"Cannot find engine '{engine_name}'", 400
|
||||||
return engine_class().get_help()
|
return engine_class().get_help()
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return f"Cannot find engine '{engine_name}'", 400
|
return f"Cannot find engine '{engine_name}'", 400
|
||||||
@@ -492,7 +496,7 @@ def get_engine_help(engine_name):
|
|||||||
# Engine Downloads and Updates:
|
# Engine Downloads and Updates:
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
|
|
||||||
@server.get('/api/is_engine_available_to_download')
|
@server.get('/api/engines/download_available')
|
||||||
def is_engine_available_to_download():
|
def is_engine_available_to_download():
|
||||||
available_result = EngineManager.version_is_available_to_download(request.args.get('engine'),
|
available_result = EngineManager.version_is_available_to_download(request.args.get('engine'),
|
||||||
request.args.get('version'),
|
request.args.get('version'),
|
||||||
@@ -502,7 +506,7 @@ def is_engine_available_to_download():
|
|||||||
(f"Cannot find available download for {request.args.get('engine')} {request.args.get('version')}", 500)
|
(f"Cannot find available download for {request.args.get('engine')} {request.args.get('version')}", 500)
|
||||||
|
|
||||||
|
|
||||||
@server.get('/api/find_most_recent_version')
|
@server.get('/api/engines/most_recent_version')
|
||||||
def find_most_recent_version():
|
def find_most_recent_version():
|
||||||
most_recent = EngineManager.find_most_recent_version(request.args.get('engine'),
|
most_recent = EngineManager.find_most_recent_version(request.args.get('engine'),
|
||||||
request.args.get('system_os'),
|
request.args.get('system_os'),
|
||||||
@@ -511,7 +515,7 @@ def find_most_recent_version():
|
|||||||
(f"Error finding most recent version of {request.args.get('engine')}", 500)
|
(f"Error finding most recent version of {request.args.get('engine')}", 500)
|
||||||
|
|
||||||
|
|
||||||
@server.post('/api/download_engine')
|
@server.post('/api/engines/download')
|
||||||
def download_engine():
|
def download_engine():
|
||||||
download_result = EngineManager.download_engine(request.args.get('engine'),
|
download_result = EngineManager.download_engine(request.args.get('engine'),
|
||||||
request.args.get('version'),
|
request.args.get('version'),
|
||||||
@@ -521,7 +525,7 @@ def download_engine():
|
|||||||
(f"Error downloading {request.args.get('engine')} {request.args.get('version')}", 500)
|
(f"Error downloading {request.args.get('engine')} {request.args.get('version')}", 500)
|
||||||
|
|
||||||
|
|
||||||
@server.post('/api/delete_engine')
|
@server.post('/api/engines/delete')
|
||||||
def delete_engine_download():
|
def delete_engine_download():
|
||||||
json_data = request.json
|
json_data = request.json
|
||||||
delete_result = EngineManager.delete_engine_download(json_data.get('engine'),
|
delete_result = EngineManager.delete_engine_download(json_data.get('engine'),
|
||||||
|
|||||||
@@ -254,11 +254,11 @@ class RenderServerProxy:
|
|||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
|
|
||||||
def get_engine_for_filename(self, filename:str, timeout=5):
|
def get_engine_for_filename(self, filename:str, timeout=5):
|
||||||
response = self.request(f'engine_for_filename?filename={os.path.basename(filename)}', timeout)
|
response = self.request(f'engines/for_filename?filename={os.path.basename(filename)}', timeout)
|
||||||
return response.text
|
return response.text
|
||||||
|
|
||||||
def get_engine_availability(self, engine_name:str, timeout=5):
|
def get_engine_availability(self, engine_name:str, timeout=5):
|
||||||
return self.request_data(f'{engine_name}/is_available', timeout)
|
return self.request_data(f'engines/{engine_name}/availability', timeout)
|
||||||
|
|
||||||
def get_engine_names(self, timeout=5):
|
def get_engine_names(self, timeout=5):
|
||||||
return self.request_data('engines/names', timeout=timeout)
|
return self.request_data('engines/names', timeout=timeout)
|
||||||
@@ -305,7 +305,7 @@ class RenderServerProxy:
|
|||||||
Response: The response from the server.
|
Response: The response from the server.
|
||||||
"""
|
"""
|
||||||
form_data = {'engine': engine_name, 'version': version, 'system_os': system_os, 'cpu': cpu}
|
form_data = {'engine': engine_name, 'version': version, 'system_os': system_os, 'cpu': cpu}
|
||||||
return self._post('delete_engine', json=form_data)
|
return self._post('engines/delete', json=form_data)
|
||||||
|
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
# Download Files:
|
# Download Files:
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ class NewRenderJobForm(QWidget):
|
|||||||
self.job_name_input.setText(directory)
|
self.job_name_input.setText(directory)
|
||||||
|
|
||||||
def args_help_button_clicked(self):
|
def args_help_button_clicked(self):
|
||||||
url = (f'http://{self.server_proxy.hostname}:{self.server_proxy.port}/api/engine/'
|
url = (f'http://{self.server_proxy.hostname}:{self.server_proxy.port}/api/engines/'
|
||||||
f'{self.engine_type.currentText()}/help')
|
f'{self.engine_type.currentText()}/help')
|
||||||
self.engine_help_viewer = EngineHelpViewer(url)
|
self.engine_help_viewer = EngineHelpViewer(url)
|
||||||
self.engine_help_viewer.show()
|
self.engine_help_viewer.show()
|
||||||
|
|||||||
Reference in New Issue
Block a user