dazpy 2.8.0__tar.gz → 2.9.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dazpy-2.8.0/dazpy.egg-info → dazpy-2.9.3}/PKG-INFO +202 -13
- dazpy-2.8.0/PKG-INFO → dazpy-2.9.3/README.md +199 -27
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/__init__.py +62 -3
- dazpy-2.9.3/dazpy/_batch.py +219 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_camera.py +46 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_client.py +81 -12
- dazpy-2.9.3/dazpy/_client_aio.py +447 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_element.py +46 -2
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_light.py +2 -2
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_node.py +139 -1
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_pose.py +30 -4
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_property.py +26 -11
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_render.py +384 -6
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_render_api.py +23 -4
- dazpy-2.9.3/dazpy/_render_api_aio.py +150 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_scene.py +24 -3
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_scene_state.py +167 -10
- dazpy-2.9.3/dazpy/_shot_geometry.py +81 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_skeleton.py +131 -0
- dazpy-2.9.3/dazpy/aio.py +20 -0
- dazpy-2.9.3/dazpy/cinematics.py +423 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/exceptions.py +25 -0
- dazpy-2.9.3/dazpy/lighting.py +227 -0
- dazpy-2.9.3/dazpy/materials.py +270 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/math3.py +167 -0
- dazpy-2.9.3/dazpy/poses.py +88 -0
- dazpy-2.8.0/README.md → dazpy-2.9.3/dazpy.egg-info/PKG-INFO +216 -12
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy.egg-info/SOURCES.txt +20 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy.egg-info/requires.txt +3 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/pyproject.toml +4 -1
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_api.py +274 -1
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_comfyui_client.py +38 -2
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_comfyui_enhance_integration.py +7 -1
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_dazpy.py +2657 -113
- dazpy-2.9.3/tests/test_dazpy_aio.py +399 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_file_watcher.py +2 -1
- dazpy-2.9.3/tests/test_math3.py +183 -0
- dazpy-2.9.3/tests/test_render_api.py +77 -0
- dazpy-2.9.3/tests/test_sprite_matrix_canvas_convert.py +110 -0
- dazpy-2.9.3/tests/test_sprite_matrix_integration.py +134 -0
- dazpy-2.9.3/tests/test_sprite_matrix_paths.py +86 -0
- dazpy-2.9.3/tests/test_sprite_matrix_presets.py +86 -0
- dazpy-2.9.3/tests/test_sprite_matrix_render_shot.py +80 -0
- dazpy-2.9.3/tests/test_sprite_matrix_render_util.py +73 -0
- dazpy-2.9.3/tests/test_sprite_matrix_schema.py +234 -0
- dazpy-2.9.3/tests/test_sprite_matrix_stylize_stage.py +121 -0
- dazpy-2.9.3/tests/test_sprite_matrix_workflow_builder.py +383 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_workflow_builder.py +7 -1
- dazpy-2.8.0/dazpy/_batch.py +0 -107
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_animation.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_bone.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_dforce.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_geometry.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_interaction.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_material.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_modifier.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_morph.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_polling.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_result.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_scene_events.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_script_builder.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_timeline.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_undo.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/_viewport.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy/py.typed +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy.egg-info/dependency_links.txt +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/dazpy.egg-info/top_level.txt +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/setup.cfg +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_dazpy_integration.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_dsp_runner.py +0 -0
- {dazpy-2.8.0 → dazpy-2.9.3}/tests/test_performance.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dazpy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.3
|
|
4
4
|
Summary: Python SDK for the DAZ Studio Script Server
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/bluemoonfoundry/daz-script-server
|
|
@@ -12,10 +12,12 @@ Requires-Dist: requests>=2.28
|
|
|
12
12
|
Provides-Extra: docs
|
|
13
13
|
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
14
14
|
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
|
|
15
|
+
Provides-Extra: aio
|
|
16
|
+
Requires-Dist: httpx>=0.24; extra == "aio"
|
|
15
17
|
|
|
16
18
|
# DazScript Server
|
|
17
19
|
|
|
18
|
-
**Version 2.
|
|
20
|
+
**Version 2.9.3** | DAZ Studio 4.5+ | DAZ Studio 6.25+ | Windows & macOS
|
|
19
21
|
|
|
20
22
|
[](https://bluemoonfoundry.github.io/daz-script-server/)
|
|
21
23
|
[](https://bluemoonfoundry.github.io/daz-script-server/api-reference/)
|
|
@@ -78,11 +80,26 @@ print(response.json())
|
|
|
78
80
|
|
|
79
81
|
---
|
|
80
82
|
|
|
83
|
+
## Examples
|
|
84
|
+
|
|
85
|
+
**Complete example collection:** [daz-script-server-examples repository](https://github.com/bluemoonfoundry/daz-script-server-examples)
|
|
86
|
+
|
|
87
|
+
Production-ready examples ranging from beginner tutorials to advanced pipeline implementations:
|
|
88
|
+
- **48 examples** across 9 categories
|
|
89
|
+
- Individual READMEs with skill level classification
|
|
90
|
+
- Requirements.txt per example
|
|
91
|
+
- Comprehensive searchable table
|
|
92
|
+
|
|
93
|
+
Categories: fundamentals, character, animation, rendering, geometry, export, ml_data, ai_vision, bvh
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
81
97
|
## 📋 Table of Contents
|
|
82
98
|
|
|
83
99
|
### Getting Started
|
|
84
100
|
- [Quick Start](#-quick-start)
|
|
85
101
|
- [Why This Exists](#why-this-exists)
|
|
102
|
+
- [What's New in v2.9.0](#whats-new-in-v290)
|
|
86
103
|
- [What's New in v2.8.0](#whats-new-in-v280)
|
|
87
104
|
- [What's New in v2.7.2](#whats-new-in-v272)
|
|
88
105
|
- [What's New in v2.7.1](#whats-new-in-v271)
|
|
@@ -170,6 +187,134 @@ DAZ Studio is powerful for 3D content creation, but automation is limited to man
|
|
|
170
187
|
|
|
171
188
|
---
|
|
172
189
|
|
|
190
|
+
## What's New in v2.9.0
|
|
191
|
+
|
|
192
|
+
### 🎬 dazpy.cinematics — camera shot builders
|
|
193
|
+
|
|
194
|
+
`dazpy.cinematics` packages common camera choreography on top of
|
|
195
|
+
`DazCamera`/`DazScene`: `apply_static_shot()` places/aims/configures a
|
|
196
|
+
camera in one call; `apply_orbit_camera()` sweeps a camera around a target
|
|
197
|
+
across a frame range; `apply_frame_subject()` frames a subject at a named
|
|
198
|
+
shot distance (`close_up` / `medium` / `full_body`); and
|
|
199
|
+
`apply_animated_shot()` writes real, interpolated DAZ Studio keyframes at a
|
|
200
|
+
sequence of waypoints instead of baking a value on every frame.
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from dazpy.cinematics import OrbitCamera, apply_orbit_camera
|
|
204
|
+
|
|
205
|
+
apply_orbit_camera(scene, OrbitCamera(target=figure, radius=250, frame_start=0, frame_end=90))
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 💡 dazpy.lighting — three-point rigs and HDRI environments
|
|
209
|
+
|
|
210
|
+
`apply_three_point_light_setup()` creates and places a key/fill/rim light
|
|
211
|
+
rig around a target in one call, via angle/distance placement or explicit
|
|
212
|
+
world-space positions per light. `apply_hdri_environment()` applies
|
|
213
|
+
image-based dome lighting — map, intensity, rotation, dome/scene mode,
|
|
214
|
+
visibility, and IBL resolution — validating the image path up front and
|
|
215
|
+
verifying a post-apply readback so a silent failure never passes as success.
|
|
216
|
+
|
|
217
|
+
### 🧍 dazpy.poses — apply_pose, reset_transforms, zero_figure
|
|
218
|
+
|
|
219
|
+
Common pose operations without hand-assembling a `DazPose`:
|
|
220
|
+
`apply_pose()` applies a pose object or a JSON file path directly to a
|
|
221
|
+
skeleton; `reset_transforms()` zeroes a node's local position/rotation and
|
|
222
|
+
resets scale to 1.0; `zero_figure()` drives every bone rotation and morph
|
|
223
|
+
to zero while leaving the figure's root transform untouched by default.
|
|
224
|
+
|
|
225
|
+
### 📐 dazpy.math3.AxisRemap — coordinate-space conversion
|
|
226
|
+
|
|
227
|
+
A generic signed-axis-permutation converter for `Vec3`/`Quat`/
|
|
228
|
+
`BoundingBox` between coordinate conventions, with a ready-made
|
|
229
|
+
`Y_UP_TO_Z_UP` preset for moving data from DAZ Studio (Y-up) into
|
|
230
|
+
Blender/glTF-style Z-up pipelines.
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from dazpy.math3 import Y_UP_TO_Z_UP
|
|
234
|
+
|
|
235
|
+
blender_pos = Y_UP_TO_Z_UP.apply_vec3(daz_pos)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 🦴 DazSkeleton.bone_rotations_quat()
|
|
239
|
+
|
|
240
|
+
Returns local-space quaternion rotations for every bone in one HTTP
|
|
241
|
+
round-trip — the composable counterpart to `bone_rotations()` for callers
|
|
242
|
+
feeding the result into another rotation (e.g. `AxisRemap.apply_quat()`),
|
|
243
|
+
sidestepping the per-bone Euler rotation-order tracking that would
|
|
244
|
+
otherwise be required.
|
|
245
|
+
|
|
246
|
+
### 🎞️ Frame-level progress streaming for animation renders
|
|
247
|
+
|
|
248
|
+
The `GET /render/:id/progress` SSE stream now emits a progress event at
|
|
249
|
+
each frame boundary during an animation render, instead of only start/
|
|
250
|
+
terminal events — the DAZ SDK exposes no per-percent render progress
|
|
251
|
+
signal, so this frame-level marker is the most granular progress available.
|
|
252
|
+
|
|
253
|
+
### ⚡ Async client — `dazpy.aio.AsyncDazClient`
|
|
254
|
+
|
|
255
|
+
Async frameworks (FastAPI, FastMCP, ComfyUI custom nodes, asyncio/Temporal
|
|
256
|
+
workflows) previously had to wrap every `DazClient` call in
|
|
257
|
+
`asyncio.to_thread()` or hand-roll an `httpx.AsyncClient` wrapper.
|
|
258
|
+
`dazpy.aio.AsyncDazClient` mirrors `DazClient`'s full method surface —
|
|
259
|
+
`execute`, `execute_file`, inline/file async submit/status/result/list/cancel, render
|
|
260
|
+
submit/batch/animation, USD export, and server-health endpoints — as
|
|
261
|
+
native `async def` methods backed by `httpx.AsyncClient`, including the
|
|
262
|
+
same `retry_on_busy`/`max_wait` backoff semantics (via `asyncio.sleep`
|
|
263
|
+
instead of blocking `time.sleep`).
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from dazpy.aio import AsyncDazClient
|
|
267
|
+
|
|
268
|
+
async def main():
|
|
269
|
+
async with AsyncDazClient() as client:
|
|
270
|
+
request_id = await client.execute_file_async_submit("C:/jobs/pose-probe.dsa")
|
|
271
|
+
result = await client.get_request_result(request_id, wait=True)
|
|
272
|
+
print(result)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Requires the optional `httpx` dependency: `pip install dazpy[aio]`.
|
|
276
|
+
|
|
277
|
+
### `DazClient` connection pooling + `close()`
|
|
278
|
+
|
|
279
|
+
`DazClient` now issues requests through a pooled `requests.Session` instead
|
|
280
|
+
of a fresh connection per call, and supports `close()` / use as a context
|
|
281
|
+
manager (`with DazClient() as client:`) for symmetry with the new async
|
|
282
|
+
client's connection lifecycle.
|
|
283
|
+
|
|
284
|
+
### Truthful Iray/Viewport render-engine selector
|
|
285
|
+
|
|
286
|
+
`DazRenderSettings.render_engine_state()` now reads the effective
|
|
287
|
+
`DzRenderOptions.renderType` value and the active renderer class/name as separate
|
|
288
|
+
facts. Its schema-1 record includes the raw enum value/name, normalized
|
|
289
|
+
`verified_iray` / `verified_non_iray` / `unavailable` status, and field-level
|
|
290
|
+
live-readback provenance. A retained `NVIDIA Iray` active-renderer name never
|
|
291
|
+
overrides a Viewport/OpenGL `renderType`.
|
|
292
|
+
|
|
293
|
+
`DazRenderSettings.set_render_engine("iray" | "viewport")` is an explicit
|
|
294
|
+
persistent setter. It selects the registered `DzIrayRenderer` plus `Software` for
|
|
295
|
+
Iray, or `ScreenShot` for Viewport, calls `applyChanges()`, and returns only after
|
|
296
|
+
exact readback. Missing renderers, mutation/apply faults, malformed replies, and
|
|
297
|
+
readback disagreement raise `RenderError`; unknown engine names raise `ValueError`
|
|
298
|
+
before dispatch.
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
settings = DazRenderSettings(client)
|
|
302
|
+
before = settings.render_engine_state()
|
|
303
|
+
change = settings.set_render_engine("iray")
|
|
304
|
+
assert change["readback"]["status"] == "verified_iray"
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The `engine` field on `/render`, `/render/batch`, and `/render/animation` also
|
|
308
|
+
accepts `viewport` and uses the same fail-closed mutation/readback rules. These
|
|
309
|
+
operations intentionally persist the requested engine. DAZ documents `ScreenShot`
|
|
310
|
+
and `HardwareAssisted` as Viewport/OpenGL operations and `Software` as the active
|
|
311
|
+
software-renderer operation in the
|
|
312
|
+
[DzRenderOptions reference](https://docs.daz3d.com/public/software/dazstudio/4/referenceguide/scripting/api_reference/object_index/renderoptions_dz);
|
|
313
|
+
renderer lookup and activation are documented by
|
|
314
|
+
[DzRenderMgr](https://docs.daz3d.com/public/software/dazstudio/4/referenceguide/scripting/api_reference/object_index/rendermgr_dz).
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
173
318
|
## What's New in v2.8.0
|
|
174
319
|
|
|
175
320
|
### 🚦 Fail Fast When DAZ Studio Is Busy — `503 STUDIO_BUSY`
|
|
@@ -318,7 +463,7 @@ change. For dirty scenes it serialises via `Scene.saveScene()` and restores
|
|
|
318
463
|
the original filename immediately. The `method` field in the response tells
|
|
319
464
|
you which strategy was used.
|
|
320
465
|
|
|
321
|
-
See `
|
|
466
|
+
See [`fundamentals/scene_save_copy`](https://github.com/bluemoonfoundry/daz-script-server-examples/tree/main/fundamentals/scene_save_copy) in the examples repo for a complete example
|
|
322
467
|
with `--compare` and `--dry-run` options.
|
|
323
468
|
|
|
324
469
|
---
|
|
@@ -370,7 +515,7 @@ snapshot = scene.scene_snapshot()
|
|
|
370
515
|
profiles = build_rig_profiles_from_snapshot(snapshot)
|
|
371
516
|
```
|
|
372
517
|
|
|
373
|
-
**Body measurement accuracy improvements** in `
|
|
518
|
+
**Body measurement accuracy improvements** in [`geometry/body_measurements`](https://github.com/bluemoonfoundry/daz-script-server-examples/tree/main/geometry/body_measurements):
|
|
374
519
|
|
|
375
520
|
- Slices now use centroid-weighted torso loop selection to filter arm cross-sections, correcting a ~70 cm overestimate on A-pose figures.
|
|
376
521
|
- The heuristic fallback for unlabelled figures (e.g. a character named
|
|
@@ -550,11 +695,14 @@ print(anim.frame_range()) # (start, end)
|
|
|
550
695
|
anim.bake_pose_to_keyframes(start=0, end=60)
|
|
551
696
|
```
|
|
552
697
|
|
|
553
|
-
### 📐 math3 — Vec3, Quat, BoundingBox
|
|
698
|
+
### 📐 math3 — Vec3, Quat, BoundingBox, AxisRemap
|
|
554
699
|
|
|
555
700
|
New module **`dazpy.math3`** provides lightweight value types returned throughout
|
|
556
701
|
the SDK: `Vec3` (positions, translations), `Quat` (rotations), and `BoundingBox`
|
|
557
702
|
(geometry bounds). All support arithmetic operators and round-trip through JSON.
|
|
703
|
+
`AxisRemap` converts `Vec3`/`Quat`/`BoundingBox` values between axis conventions
|
|
704
|
+
via signed-axis permutations (e.g. the built-in `Y_UP_TO_Z_UP` preset for
|
|
705
|
+
converting DAZ Studio's Y-up scene data to Z-up tools like Blender).
|
|
558
706
|
|
|
559
707
|
### 🌐 Posed Vertex Export & USD Scene Export
|
|
560
708
|
|
|
@@ -562,7 +710,7 @@ the SDK: `Vec3` (positions, translations), `Quat` (rotations), and `BoundingBox`
|
|
|
562
710
|
positions with skinning and morphs already applied — enabling downstream export
|
|
563
711
|
pipelines to read the final mesh without re-solving the rig.
|
|
564
712
|
|
|
565
|
-
New example
|
|
713
|
+
New example **[`export/scene_to_usd`](https://github.com/bluemoonfoundry/daz-script-server-examples/tree/main/export/scene_to_usd)** uses this to export a full live
|
|
566
714
|
DAZ Studio scene to Pixar USD: posed meshes, cameras, lights, PBR materials, and
|
|
567
715
|
strand-based hair as `UsdGeom.BasisCurves`. Pass `--morphs` to write blend
|
|
568
716
|
shapes as `UsdSkel` targets.
|
|
@@ -600,8 +748,8 @@ Studio instance) now ship in the repo root.
|
|
|
600
748
|
|
|
601
749
|
### 📖 Docs & Ergonomics
|
|
602
750
|
|
|
603
|
-
- API reference pages for `DazPose`, `DazAnimation`, `Vec3`, `Quat`, and `
|
|
604
|
-
- Every script in
|
|
751
|
+
- API reference pages for `DazPose`, `DazAnimation`, `Vec3`, `Quat`, `BoundingBox`, and `AxisRemap`
|
|
752
|
+
- Every script in [daz-script-server-examples](https://github.com/bluemoonfoundry/daz-script-server-examples) has an `if __name__ == "__main__":` guard and
|
|
605
753
|
argparse `--help`, making all examples safe to import and self-documenting
|
|
606
754
|
|
|
607
755
|
---
|
|
@@ -654,7 +802,7 @@ See the [full migration guide](MIGRATION.md) for upgrade steps and rollback inst
|
|
|
654
802
|
|
|
655
803
|
Long-running operations (renders, exports, batch jobs) no longer need to block the HTTP connection:
|
|
656
804
|
|
|
657
|
-
- **`POST /execute/async`** — Submit
|
|
805
|
+
- **`POST /execute/async`** — Submit inline `script` or host-side `scriptFile` work asynchronously; returns a `request_id` immediately
|
|
658
806
|
- **`POST /scripts/:id/async`** — Submit a registered script asynchronously
|
|
659
807
|
- **`GET /requests/:id/status`** — Poll for progress (`queued`, `running`, `completed`, `failed`, `cancelled`)
|
|
660
808
|
- **`GET /requests/:id/result`** — Fetch the final result; supports `?wait=true` to long-poll until complete
|
|
@@ -665,6 +813,8 @@ Long-running operations (renders, exports, batch jobs) no longer need to block t
|
|
|
665
813
|
|
|
666
814
|
**TTL:** Completed, failed, and cancelled requests are automatically purged after 1 hour (cleanup timer fires every 5 minutes).
|
|
667
815
|
|
|
816
|
+
**Stale running requests:** If a request has been `running` for more than 30 minutes without completing — e.g. a render blocked behind a DAZ Studio modal dialog — it's automatically marked `failed` so polling clients aren't left waiting forever. The underlying DAZ Studio operation may still be stuck; check the DAZ Studio window directly if this happens.
|
|
817
|
+
|
|
668
818
|
---
|
|
669
819
|
|
|
670
820
|
## What's New in v1.2.0
|
|
@@ -712,7 +862,7 @@ automation code without authoring DazScript by hand.
|
|
|
712
862
|
Download the `.whl` file from the [latest release](https://github.com/bluemoonfoundry/daz-script-server/releases/latest) and install it:
|
|
713
863
|
|
|
714
864
|
```bash
|
|
715
|
-
pip install dazpy-2.
|
|
865
|
+
pip install dazpy-2.9.0-py3-none-any.whl
|
|
716
866
|
```
|
|
717
867
|
|
|
718
868
|
Or install directly from the repo for development:
|
|
@@ -1636,10 +1786,36 @@ result = requests.get(f"{BASE}/requests/{req_id}/result?wait=true&timeout=300",
|
|
|
1636
1786
|
|
|
1637
1787
|
### `POST /execute/async`
|
|
1638
1788
|
|
|
1639
|
-
**Purpose:** Submit an inline script for asynchronous execution
|
|
1789
|
+
**Purpose:** Submit an inline script or host-side script file for asynchronous execution
|
|
1640
1790
|
**Authentication:** Required (if enabled)
|
|
1641
1791
|
|
|
1642
|
-
**Request Body:** Same as `POST /execute`
|
|
1792
|
+
**Request Body:** Same as `POST /execute`, plus optional `reportFile` for
|
|
1793
|
+
structured job observation.
|
|
1794
|
+
|
|
1795
|
+
`reportFile` is an absolute host-side path to a JSONL file owned by this job.
|
|
1796
|
+
The server truncates it when the request is accepted. The script may then write
|
|
1797
|
+
one JSON object per line. The path is injected into the script's argument object
|
|
1798
|
+
as reserved field `getArguments()[0].__dssReportFile`, so callers do not have to
|
|
1799
|
+
duplicate it inside `args`:
|
|
1800
|
+
|
|
1801
|
+
```jsonl
|
|
1802
|
+
{"type":"progress","value":0.25,"current":1,"total":4,"phase":"render","message":"Plate 1"}
|
|
1803
|
+
{"type":"log","level":"info","message":"Loaded scene"}
|
|
1804
|
+
{"type":"output","path":"C:/renders/run/plate-1.png","kind":"image","label":"plate"}
|
|
1805
|
+
```
|
|
1806
|
+
|
|
1807
|
+
Progress events require either `value` (0–1) or `current` plus `total`. Log
|
|
1808
|
+
events require `message`. Output events require `path`; repeating a path updates
|
|
1809
|
+
that manifest entry. A `manifest` event may provide an `outputs` array. Use a
|
|
1810
|
+
unique report file for every job.
|
|
1811
|
+
`reportFile` may not be the submitted `scriptFile`, because the server owns and
|
|
1812
|
+
truncates the former before the job is queued.
|
|
1813
|
+
|
|
1814
|
+
DAZ Studio 6 ingests report events from polling workers, so progress, logs, and
|
|
1815
|
+
outputs are visible while the main thread is occupied by the script. DAZ Studio
|
|
1816
|
+
4's JSON parser is main-thread-only; Studio 4 therefore ingests the final report
|
|
1817
|
+
after execution returns and does not expose report-driven progress while the job
|
|
1818
|
+
is still running.
|
|
1643
1819
|
|
|
1644
1820
|
**Response (immediate):**
|
|
1645
1821
|
```json
|
|
@@ -1673,7 +1849,15 @@ result = requests.get(f"{BASE}/requests/{req_id}/result?wait=true&timeout=300",
|
|
|
1673
1849
|
{
|
|
1674
1850
|
"request_id": "a3f2b891",
|
|
1675
1851
|
"status": "running",
|
|
1676
|
-
"progress": 0.
|
|
1852
|
+
"progress": 0.25,
|
|
1853
|
+
"observation": {
|
|
1854
|
+
"progress": {"value": 0.25, "current": 1, "total": 4, "phase": "render"},
|
|
1855
|
+
"log_tail": [{"level": "info", "source": "script", "message": "Loaded scene"}],
|
|
1856
|
+
"log_total": 1,
|
|
1857
|
+
"log_truncated": false,
|
|
1858
|
+
"output_manifest": {"count": 0, "outputs": []},
|
|
1859
|
+
"report_file": "C:/renders/run/job.jsonl"
|
|
1860
|
+
},
|
|
1677
1861
|
"elapsed_ms": 1240,
|
|
1678
1862
|
"queue_position": 0
|
|
1679
1863
|
}
|
|
@@ -1711,6 +1895,11 @@ result = requests.get(f"{BASE}/requests/{req_id}/result?wait=true&timeout=300",
|
|
|
1711
1895
|
}
|
|
1712
1896
|
```
|
|
1713
1897
|
|
|
1898
|
+
Completed, failed, and cancelled results also include `observation`. The log
|
|
1899
|
+
tail is capped at the newest 100 entries; `log_total` and `log_truncated` make
|
|
1900
|
+
the loss explicit. Ordinary captured `print()` output is folded into the same
|
|
1901
|
+
tail at completion while remaining available in the legacy `output` field.
|
|
1902
|
+
|
|
1714
1903
|
Returns HTTP 404 if the request ID is unknown or has been purged (TTL: 1 hour).
|
|
1715
1904
|
|
|
1716
1905
|
---
|
|
@@ -1,21 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: dazpy
|
|
3
|
-
Version: 2.8.0
|
|
4
|
-
Summary: Python SDK for the DAZ Studio Script Server
|
|
5
|
-
License: MIT
|
|
6
|
-
Project-URL: Homepage, https://github.com/bluemoonfoundry/daz-script-server
|
|
7
|
-
Project-URL: Repository, https://github.com/bluemoonfoundry/daz-script-server
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/bluemoonfoundry/daz-script-server/issues
|
|
9
|
-
Requires-Python: >=3.10
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
Requires-Dist: requests>=2.28
|
|
12
|
-
Provides-Extra: docs
|
|
13
|
-
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
14
|
-
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
|
|
15
|
-
|
|
16
1
|
# DazScript Server
|
|
17
2
|
|
|
18
|
-
**Version 2.
|
|
3
|
+
**Version 2.9.3** | DAZ Studio 4.5+ | DAZ Studio 6.25+ | Windows & macOS
|
|
19
4
|
|
|
20
5
|
[](https://bluemoonfoundry.github.io/daz-script-server/)
|
|
21
6
|
[](https://bluemoonfoundry.github.io/daz-script-server/api-reference/)
|
|
@@ -78,11 +63,26 @@ print(response.json())
|
|
|
78
63
|
|
|
79
64
|
---
|
|
80
65
|
|
|
66
|
+
## Examples
|
|
67
|
+
|
|
68
|
+
**Complete example collection:** [daz-script-server-examples repository](https://github.com/bluemoonfoundry/daz-script-server-examples)
|
|
69
|
+
|
|
70
|
+
Production-ready examples ranging from beginner tutorials to advanced pipeline implementations:
|
|
71
|
+
- **48 examples** across 9 categories
|
|
72
|
+
- Individual READMEs with skill level classification
|
|
73
|
+
- Requirements.txt per example
|
|
74
|
+
- Comprehensive searchable table
|
|
75
|
+
|
|
76
|
+
Categories: fundamentals, character, animation, rendering, geometry, export, ml_data, ai_vision, bvh
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
81
80
|
## 📋 Table of Contents
|
|
82
81
|
|
|
83
82
|
### Getting Started
|
|
84
83
|
- [Quick Start](#-quick-start)
|
|
85
84
|
- [Why This Exists](#why-this-exists)
|
|
85
|
+
- [What's New in v2.9.0](#whats-new-in-v290)
|
|
86
86
|
- [What's New in v2.8.0](#whats-new-in-v280)
|
|
87
87
|
- [What's New in v2.7.2](#whats-new-in-v272)
|
|
88
88
|
- [What's New in v2.7.1](#whats-new-in-v271)
|
|
@@ -170,6 +170,134 @@ DAZ Studio is powerful for 3D content creation, but automation is limited to man
|
|
|
170
170
|
|
|
171
171
|
---
|
|
172
172
|
|
|
173
|
+
## What's New in v2.9.0
|
|
174
|
+
|
|
175
|
+
### 🎬 dazpy.cinematics — camera shot builders
|
|
176
|
+
|
|
177
|
+
`dazpy.cinematics` packages common camera choreography on top of
|
|
178
|
+
`DazCamera`/`DazScene`: `apply_static_shot()` places/aims/configures a
|
|
179
|
+
camera in one call; `apply_orbit_camera()` sweeps a camera around a target
|
|
180
|
+
across a frame range; `apply_frame_subject()` frames a subject at a named
|
|
181
|
+
shot distance (`close_up` / `medium` / `full_body`); and
|
|
182
|
+
`apply_animated_shot()` writes real, interpolated DAZ Studio keyframes at a
|
|
183
|
+
sequence of waypoints instead of baking a value on every frame.
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from dazpy.cinematics import OrbitCamera, apply_orbit_camera
|
|
187
|
+
|
|
188
|
+
apply_orbit_camera(scene, OrbitCamera(target=figure, radius=250, frame_start=0, frame_end=90))
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 💡 dazpy.lighting — three-point rigs and HDRI environments
|
|
192
|
+
|
|
193
|
+
`apply_three_point_light_setup()` creates and places a key/fill/rim light
|
|
194
|
+
rig around a target in one call, via angle/distance placement or explicit
|
|
195
|
+
world-space positions per light. `apply_hdri_environment()` applies
|
|
196
|
+
image-based dome lighting — map, intensity, rotation, dome/scene mode,
|
|
197
|
+
visibility, and IBL resolution — validating the image path up front and
|
|
198
|
+
verifying a post-apply readback so a silent failure never passes as success.
|
|
199
|
+
|
|
200
|
+
### 🧍 dazpy.poses — apply_pose, reset_transforms, zero_figure
|
|
201
|
+
|
|
202
|
+
Common pose operations without hand-assembling a `DazPose`:
|
|
203
|
+
`apply_pose()` applies a pose object or a JSON file path directly to a
|
|
204
|
+
skeleton; `reset_transforms()` zeroes a node's local position/rotation and
|
|
205
|
+
resets scale to 1.0; `zero_figure()` drives every bone rotation and morph
|
|
206
|
+
to zero while leaving the figure's root transform untouched by default.
|
|
207
|
+
|
|
208
|
+
### 📐 dazpy.math3.AxisRemap — coordinate-space conversion
|
|
209
|
+
|
|
210
|
+
A generic signed-axis-permutation converter for `Vec3`/`Quat`/
|
|
211
|
+
`BoundingBox` between coordinate conventions, with a ready-made
|
|
212
|
+
`Y_UP_TO_Z_UP` preset for moving data from DAZ Studio (Y-up) into
|
|
213
|
+
Blender/glTF-style Z-up pipelines.
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
from dazpy.math3 import Y_UP_TO_Z_UP
|
|
217
|
+
|
|
218
|
+
blender_pos = Y_UP_TO_Z_UP.apply_vec3(daz_pos)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 🦴 DazSkeleton.bone_rotations_quat()
|
|
222
|
+
|
|
223
|
+
Returns local-space quaternion rotations for every bone in one HTTP
|
|
224
|
+
round-trip — the composable counterpart to `bone_rotations()` for callers
|
|
225
|
+
feeding the result into another rotation (e.g. `AxisRemap.apply_quat()`),
|
|
226
|
+
sidestepping the per-bone Euler rotation-order tracking that would
|
|
227
|
+
otherwise be required.
|
|
228
|
+
|
|
229
|
+
### 🎞️ Frame-level progress streaming for animation renders
|
|
230
|
+
|
|
231
|
+
The `GET /render/:id/progress` SSE stream now emits a progress event at
|
|
232
|
+
each frame boundary during an animation render, instead of only start/
|
|
233
|
+
terminal events — the DAZ SDK exposes no per-percent render progress
|
|
234
|
+
signal, so this frame-level marker is the most granular progress available.
|
|
235
|
+
|
|
236
|
+
### ⚡ Async client — `dazpy.aio.AsyncDazClient`
|
|
237
|
+
|
|
238
|
+
Async frameworks (FastAPI, FastMCP, ComfyUI custom nodes, asyncio/Temporal
|
|
239
|
+
workflows) previously had to wrap every `DazClient` call in
|
|
240
|
+
`asyncio.to_thread()` or hand-roll an `httpx.AsyncClient` wrapper.
|
|
241
|
+
`dazpy.aio.AsyncDazClient` mirrors `DazClient`'s full method surface —
|
|
242
|
+
`execute`, `execute_file`, inline/file async submit/status/result/list/cancel, render
|
|
243
|
+
submit/batch/animation, USD export, and server-health endpoints — as
|
|
244
|
+
native `async def` methods backed by `httpx.AsyncClient`, including the
|
|
245
|
+
same `retry_on_busy`/`max_wait` backoff semantics (via `asyncio.sleep`
|
|
246
|
+
instead of blocking `time.sleep`).
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from dazpy.aio import AsyncDazClient
|
|
250
|
+
|
|
251
|
+
async def main():
|
|
252
|
+
async with AsyncDazClient() as client:
|
|
253
|
+
request_id = await client.execute_file_async_submit("C:/jobs/pose-probe.dsa")
|
|
254
|
+
result = await client.get_request_result(request_id, wait=True)
|
|
255
|
+
print(result)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Requires the optional `httpx` dependency: `pip install dazpy[aio]`.
|
|
259
|
+
|
|
260
|
+
### `DazClient` connection pooling + `close()`
|
|
261
|
+
|
|
262
|
+
`DazClient` now issues requests through a pooled `requests.Session` instead
|
|
263
|
+
of a fresh connection per call, and supports `close()` / use as a context
|
|
264
|
+
manager (`with DazClient() as client:`) for symmetry with the new async
|
|
265
|
+
client's connection lifecycle.
|
|
266
|
+
|
|
267
|
+
### Truthful Iray/Viewport render-engine selector
|
|
268
|
+
|
|
269
|
+
`DazRenderSettings.render_engine_state()` now reads the effective
|
|
270
|
+
`DzRenderOptions.renderType` value and the active renderer class/name as separate
|
|
271
|
+
facts. Its schema-1 record includes the raw enum value/name, normalized
|
|
272
|
+
`verified_iray` / `verified_non_iray` / `unavailable` status, and field-level
|
|
273
|
+
live-readback provenance. A retained `NVIDIA Iray` active-renderer name never
|
|
274
|
+
overrides a Viewport/OpenGL `renderType`.
|
|
275
|
+
|
|
276
|
+
`DazRenderSettings.set_render_engine("iray" | "viewport")` is an explicit
|
|
277
|
+
persistent setter. It selects the registered `DzIrayRenderer` plus `Software` for
|
|
278
|
+
Iray, or `ScreenShot` for Viewport, calls `applyChanges()`, and returns only after
|
|
279
|
+
exact readback. Missing renderers, mutation/apply faults, malformed replies, and
|
|
280
|
+
readback disagreement raise `RenderError`; unknown engine names raise `ValueError`
|
|
281
|
+
before dispatch.
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
settings = DazRenderSettings(client)
|
|
285
|
+
before = settings.render_engine_state()
|
|
286
|
+
change = settings.set_render_engine("iray")
|
|
287
|
+
assert change["readback"]["status"] == "verified_iray"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The `engine` field on `/render`, `/render/batch`, and `/render/animation` also
|
|
291
|
+
accepts `viewport` and uses the same fail-closed mutation/readback rules. These
|
|
292
|
+
operations intentionally persist the requested engine. DAZ documents `ScreenShot`
|
|
293
|
+
and `HardwareAssisted` as Viewport/OpenGL operations and `Software` as the active
|
|
294
|
+
software-renderer operation in the
|
|
295
|
+
[DzRenderOptions reference](https://docs.daz3d.com/public/software/dazstudio/4/referenceguide/scripting/api_reference/object_index/renderoptions_dz);
|
|
296
|
+
renderer lookup and activation are documented by
|
|
297
|
+
[DzRenderMgr](https://docs.daz3d.com/public/software/dazstudio/4/referenceguide/scripting/api_reference/object_index/rendermgr_dz).
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
173
301
|
## What's New in v2.8.0
|
|
174
302
|
|
|
175
303
|
### 🚦 Fail Fast When DAZ Studio Is Busy — `503 STUDIO_BUSY`
|
|
@@ -318,7 +446,7 @@ change. For dirty scenes it serialises via `Scene.saveScene()` and restores
|
|
|
318
446
|
the original filename immediately. The `method` field in the response tells
|
|
319
447
|
you which strategy was used.
|
|
320
448
|
|
|
321
|
-
See `
|
|
449
|
+
See [`fundamentals/scene_save_copy`](https://github.com/bluemoonfoundry/daz-script-server-examples/tree/main/fundamentals/scene_save_copy) in the examples repo for a complete example
|
|
322
450
|
with `--compare` and `--dry-run` options.
|
|
323
451
|
|
|
324
452
|
---
|
|
@@ -370,7 +498,7 @@ snapshot = scene.scene_snapshot()
|
|
|
370
498
|
profiles = build_rig_profiles_from_snapshot(snapshot)
|
|
371
499
|
```
|
|
372
500
|
|
|
373
|
-
**Body measurement accuracy improvements** in `
|
|
501
|
+
**Body measurement accuracy improvements** in [`geometry/body_measurements`](https://github.com/bluemoonfoundry/daz-script-server-examples/tree/main/geometry/body_measurements):
|
|
374
502
|
|
|
375
503
|
- Slices now use centroid-weighted torso loop selection to filter arm cross-sections, correcting a ~70 cm overestimate on A-pose figures.
|
|
376
504
|
- The heuristic fallback for unlabelled figures (e.g. a character named
|
|
@@ -550,11 +678,14 @@ print(anim.frame_range()) # (start, end)
|
|
|
550
678
|
anim.bake_pose_to_keyframes(start=0, end=60)
|
|
551
679
|
```
|
|
552
680
|
|
|
553
|
-
### 📐 math3 — Vec3, Quat, BoundingBox
|
|
681
|
+
### 📐 math3 — Vec3, Quat, BoundingBox, AxisRemap
|
|
554
682
|
|
|
555
683
|
New module **`dazpy.math3`** provides lightweight value types returned throughout
|
|
556
684
|
the SDK: `Vec3` (positions, translations), `Quat` (rotations), and `BoundingBox`
|
|
557
685
|
(geometry bounds). All support arithmetic operators and round-trip through JSON.
|
|
686
|
+
`AxisRemap` converts `Vec3`/`Quat`/`BoundingBox` values between axis conventions
|
|
687
|
+
via signed-axis permutations (e.g. the built-in `Y_UP_TO_Z_UP` preset for
|
|
688
|
+
converting DAZ Studio's Y-up scene data to Z-up tools like Blender).
|
|
558
689
|
|
|
559
690
|
### 🌐 Posed Vertex Export & USD Scene Export
|
|
560
691
|
|
|
@@ -562,7 +693,7 @@ the SDK: `Vec3` (positions, translations), `Quat` (rotations), and `BoundingBox`
|
|
|
562
693
|
positions with skinning and morphs already applied — enabling downstream export
|
|
563
694
|
pipelines to read the final mesh without re-solving the rig.
|
|
564
695
|
|
|
565
|
-
New example
|
|
696
|
+
New example **[`export/scene_to_usd`](https://github.com/bluemoonfoundry/daz-script-server-examples/tree/main/export/scene_to_usd)** uses this to export a full live
|
|
566
697
|
DAZ Studio scene to Pixar USD: posed meshes, cameras, lights, PBR materials, and
|
|
567
698
|
strand-based hair as `UsdGeom.BasisCurves`. Pass `--morphs` to write blend
|
|
568
699
|
shapes as `UsdSkel` targets.
|
|
@@ -600,8 +731,8 @@ Studio instance) now ship in the repo root.
|
|
|
600
731
|
|
|
601
732
|
### 📖 Docs & Ergonomics
|
|
602
733
|
|
|
603
|
-
- API reference pages for `DazPose`, `DazAnimation`, `Vec3`, `Quat`, and `
|
|
604
|
-
- Every script in
|
|
734
|
+
- API reference pages for `DazPose`, `DazAnimation`, `Vec3`, `Quat`, `BoundingBox`, and `AxisRemap`
|
|
735
|
+
- Every script in [daz-script-server-examples](https://github.com/bluemoonfoundry/daz-script-server-examples) has an `if __name__ == "__main__":` guard and
|
|
605
736
|
argparse `--help`, making all examples safe to import and self-documenting
|
|
606
737
|
|
|
607
738
|
---
|
|
@@ -654,7 +785,7 @@ See the [full migration guide](MIGRATION.md) for upgrade steps and rollback inst
|
|
|
654
785
|
|
|
655
786
|
Long-running operations (renders, exports, batch jobs) no longer need to block the HTTP connection:
|
|
656
787
|
|
|
657
|
-
- **`POST /execute/async`** — Submit
|
|
788
|
+
- **`POST /execute/async`** — Submit inline `script` or host-side `scriptFile` work asynchronously; returns a `request_id` immediately
|
|
658
789
|
- **`POST /scripts/:id/async`** — Submit a registered script asynchronously
|
|
659
790
|
- **`GET /requests/:id/status`** — Poll for progress (`queued`, `running`, `completed`, `failed`, `cancelled`)
|
|
660
791
|
- **`GET /requests/:id/result`** — Fetch the final result; supports `?wait=true` to long-poll until complete
|
|
@@ -665,6 +796,8 @@ Long-running operations (renders, exports, batch jobs) no longer need to block t
|
|
|
665
796
|
|
|
666
797
|
**TTL:** Completed, failed, and cancelled requests are automatically purged after 1 hour (cleanup timer fires every 5 minutes).
|
|
667
798
|
|
|
799
|
+
**Stale running requests:** If a request has been `running` for more than 30 minutes without completing — e.g. a render blocked behind a DAZ Studio modal dialog — it's automatically marked `failed` so polling clients aren't left waiting forever. The underlying DAZ Studio operation may still be stuck; check the DAZ Studio window directly if this happens.
|
|
800
|
+
|
|
668
801
|
---
|
|
669
802
|
|
|
670
803
|
## What's New in v1.2.0
|
|
@@ -712,7 +845,7 @@ automation code without authoring DazScript by hand.
|
|
|
712
845
|
Download the `.whl` file from the [latest release](https://github.com/bluemoonfoundry/daz-script-server/releases/latest) and install it:
|
|
713
846
|
|
|
714
847
|
```bash
|
|
715
|
-
pip install dazpy-2.
|
|
848
|
+
pip install dazpy-2.9.0-py3-none-any.whl
|
|
716
849
|
```
|
|
717
850
|
|
|
718
851
|
Or install directly from the repo for development:
|
|
@@ -1636,10 +1769,36 @@ result = requests.get(f"{BASE}/requests/{req_id}/result?wait=true&timeout=300",
|
|
|
1636
1769
|
|
|
1637
1770
|
### `POST /execute/async`
|
|
1638
1771
|
|
|
1639
|
-
**Purpose:** Submit an inline script for asynchronous execution
|
|
1772
|
+
**Purpose:** Submit an inline script or host-side script file for asynchronous execution
|
|
1640
1773
|
**Authentication:** Required (if enabled)
|
|
1641
1774
|
|
|
1642
|
-
**Request Body:** Same as `POST /execute`
|
|
1775
|
+
**Request Body:** Same as `POST /execute`, plus optional `reportFile` for
|
|
1776
|
+
structured job observation.
|
|
1777
|
+
|
|
1778
|
+
`reportFile` is an absolute host-side path to a JSONL file owned by this job.
|
|
1779
|
+
The server truncates it when the request is accepted. The script may then write
|
|
1780
|
+
one JSON object per line. The path is injected into the script's argument object
|
|
1781
|
+
as reserved field `getArguments()[0].__dssReportFile`, so callers do not have to
|
|
1782
|
+
duplicate it inside `args`:
|
|
1783
|
+
|
|
1784
|
+
```jsonl
|
|
1785
|
+
{"type":"progress","value":0.25,"current":1,"total":4,"phase":"render","message":"Plate 1"}
|
|
1786
|
+
{"type":"log","level":"info","message":"Loaded scene"}
|
|
1787
|
+
{"type":"output","path":"C:/renders/run/plate-1.png","kind":"image","label":"plate"}
|
|
1788
|
+
```
|
|
1789
|
+
|
|
1790
|
+
Progress events require either `value` (0–1) or `current` plus `total`. Log
|
|
1791
|
+
events require `message`. Output events require `path`; repeating a path updates
|
|
1792
|
+
that manifest entry. A `manifest` event may provide an `outputs` array. Use a
|
|
1793
|
+
unique report file for every job.
|
|
1794
|
+
`reportFile` may not be the submitted `scriptFile`, because the server owns and
|
|
1795
|
+
truncates the former before the job is queued.
|
|
1796
|
+
|
|
1797
|
+
DAZ Studio 6 ingests report events from polling workers, so progress, logs, and
|
|
1798
|
+
outputs are visible while the main thread is occupied by the script. DAZ Studio
|
|
1799
|
+
4's JSON parser is main-thread-only; Studio 4 therefore ingests the final report
|
|
1800
|
+
after execution returns and does not expose report-driven progress while the job
|
|
1801
|
+
is still running.
|
|
1643
1802
|
|
|
1644
1803
|
**Response (immediate):**
|
|
1645
1804
|
```json
|
|
@@ -1673,7 +1832,15 @@ result = requests.get(f"{BASE}/requests/{req_id}/result?wait=true&timeout=300",
|
|
|
1673
1832
|
{
|
|
1674
1833
|
"request_id": "a3f2b891",
|
|
1675
1834
|
"status": "running",
|
|
1676
|
-
"progress": 0.
|
|
1835
|
+
"progress": 0.25,
|
|
1836
|
+
"observation": {
|
|
1837
|
+
"progress": {"value": 0.25, "current": 1, "total": 4, "phase": "render"},
|
|
1838
|
+
"log_tail": [{"level": "info", "source": "script", "message": "Loaded scene"}],
|
|
1839
|
+
"log_total": 1,
|
|
1840
|
+
"log_truncated": false,
|
|
1841
|
+
"output_manifest": {"count": 0, "outputs": []},
|
|
1842
|
+
"report_file": "C:/renders/run/job.jsonl"
|
|
1843
|
+
},
|
|
1677
1844
|
"elapsed_ms": 1240,
|
|
1678
1845
|
"queue_position": 0
|
|
1679
1846
|
}
|
|
@@ -1711,6 +1878,11 @@ result = requests.get(f"{BASE}/requests/{req_id}/result?wait=true&timeout=300",
|
|
|
1711
1878
|
}
|
|
1712
1879
|
```
|
|
1713
1880
|
|
|
1881
|
+
Completed, failed, and cancelled results also include `observation`. The log
|
|
1882
|
+
tail is capped at the newest 100 entries; `log_total` and `log_truncated` make
|
|
1883
|
+
the loss explicit. Ordinary captured `print()` output is folded into the same
|
|
1884
|
+
tail at completion while remaining available in the legacy `output` field.
|
|
1885
|
+
|
|
1714
1886
|
Returns HTTP 404 if the request ID is unknown or has been purged (TTL: 1 hour).
|
|
1715
1887
|
|
|
1716
1888
|
---
|