janction-render 0.4.3__tar.gz → 0.4.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janction-render
3
- Version: 0.4.3
3
+ Version: 0.4.5
4
4
  Summary: Cloud GPU render farm for Blender, for AI agents: MCP server (remote + stdio) and CLI. Render Blender without a GPU from Claude, ChatGPT, Claude Code, Codex, Cursor.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -40,13 +40,17 @@ without a GPU, or when rendering locally is slow.
40
40
  - CC0 assets by name: `asset_search("wooden table")` finds Poly Haven models, textures and HDRIs; pass `polyhaven:<id>` in
41
41
  `asset_urls` (remote) or `assets` (stdio) and the files land in `JR_ASSETS_DIR/<id>/` on the GPU, ready to import
42
42
  (`bpy.ops.import_scene.gltf(filepath=os.path.join(os.environ['JR_ASSETS_DIR'], 'wooden_table_02', 'wooden_table_02_1k.gltf'))`).
43
+ Scene scripts can `import jr_assets` for one-liners: `jr_assets.import_model('wooden_table_02')`,
44
+ `jr_assets.apply(floor, jr_assets.material('wood_floor_deck', scale=2))` (diff / normal / roughness / metal wired into a
45
+ Principled BSDF), `jr_assets.world_hdri('studio_small_09')`. See `samples/polyhaven_room.py`.
43
46
  - `scene_info` reads the scene without rendering (cameras, frame range, missing files).
44
47
  - `render_preview` returns 1-4 low-cost frames tiled in one image within seconds, so the agent can look, fix, and repeat.
45
48
  - `render_estimate` says how long a render will take ("about 3 minutes") and whether it fits today's free quota.
46
49
  - `render_final` renders the frames on GPUs and joins them into an MP4; `render_status` reports the remaining time.
47
50
  - Free beta: no charges. Each key gets 10 GPU-minutes per day; a final render is up to 240 frames at 1080p.
48
51
  Paid plans (per GPU second, prepaid credit) will be announced on the service page before they start.
49
- - Inputs and results are deleted 24 hours after last use and are never used for training.
52
+ - Inputs and results are deleted 24 hours after last use and are never used for training. A render you choose to
53
+ share (`render_share`) gets a public page (`/r/<id>`, OGP for X and Discord) that stays until you unshare it.
50
54
 
51
55
  ## Status
52
56
 
@@ -122,6 +126,7 @@ Then, in Claude Code:
122
126
  | `render_final(scene_id, frame_start, frame_end, width, height, samples, fps, output, environment?, blender?, engine?, transparent?, notify_url?)` | frames (png / exr) or video (mp4 / webm / prores / gif / webp); `transparent=True` keeps an alpha background (png / exr / webm / gif / webp); `notify_url` gets one JSON POST when the job finishes; returns job_id + estimate |
123
127
  | `render_status(job_id)` | progress and ETA (`eta.human`); `render_download(job_id, only="mp4" / "frames" / "all")` files or links; `render_cancel(job_id)` |
124
128
  | `billing()` | free-beta quota (used today, daily limit, reset time); later balance and top-up link |
129
+ | `render_share(job_id, title?, note?, include_script?, listed?)` | a public page `/r/<id>` with the image or video, the conditions and (optionally) the script; survives the 24-hour expiry until `render_unshare`; listed pages appear in `/gallery` |
125
130
  | `asset_search(query, kind)` | CC0 models / textures / HDRIs on Poly Haven by words; results carry `polyhaven:<id>` and the entry file |
126
131
  | `render_info()` | workers online or gated (GPU lent to another workload), queue, expected wait, supported inputs, environment presets, Blender versions |
127
132
 
@@ -131,7 +136,7 @@ Options on `render_preview` / `render_final`: `environment` (`studio`, `sunset`,
131
136
  sent with the scene, found in the script through `os.environ["JR_ASSETS_DIR"]`; a `.blend`'s external files are collected
132
137
  automatically with `pip install janction-render[blend]`) or `asset_urls` (remote).
133
138
 
134
- CLI: `janction-render inspect|preview|render|status|download|cancel|jobs|balance|topup|info`.
139
+ CLI: `janction-render inspect|preview|render|status|download|share|unshare|cancel|jobs|balance|topup|info`.
135
140
 
136
141
  ## Writing a scene script
137
142
 
@@ -163,6 +168,8 @@ POST /v1/jobs {scene_id, kind: info|preview|final, frames|frame_start/frame_en
163
168
  environment, environment_strength, environment_visible, blender, orbit, orbit_frames, orbit_elevation}
164
169
  GET /v1/jobs/{id} status, progress, eta, artifacts[], cost, warnings, info DELETE /v1/jobs/{id} cancel
165
170
  GET /v1/jobs/{id}/artifacts/{name} PNG / MP4
171
+ POST /v1/jobs/{id}/share {title?, note?, include_script?, listed?} -> {share_id, url} DELETE /v1/jobs/{id}/share GET /v1/shares
172
+ GET /r/{share_id} public page (no key) GET /gallery
166
173
  GET /v1/assets/search?q=&kind=models|textures|hdris CC0 assets (Poly Haven) -> spec polyhaven:<id>
167
174
  POST /v1/files/{id}/assets/urls {urls: ["https://...", "polyhaven:<id>"]} fetch assets server-side
168
175
  POST /mcp remote MCP (Streamable HTTP; Bearer api key or OAuth)
@@ -23,13 +23,17 @@ without a GPU, or when rendering locally is slow.
23
23
  - CC0 assets by name: `asset_search("wooden table")` finds Poly Haven models, textures and HDRIs; pass `polyhaven:<id>` in
24
24
  `asset_urls` (remote) or `assets` (stdio) and the files land in `JR_ASSETS_DIR/<id>/` on the GPU, ready to import
25
25
  (`bpy.ops.import_scene.gltf(filepath=os.path.join(os.environ['JR_ASSETS_DIR'], 'wooden_table_02', 'wooden_table_02_1k.gltf'))`).
26
+ Scene scripts can `import jr_assets` for one-liners: `jr_assets.import_model('wooden_table_02')`,
27
+ `jr_assets.apply(floor, jr_assets.material('wood_floor_deck', scale=2))` (diff / normal / roughness / metal wired into a
28
+ Principled BSDF), `jr_assets.world_hdri('studio_small_09')`. See `samples/polyhaven_room.py`.
26
29
  - `scene_info` reads the scene without rendering (cameras, frame range, missing files).
27
30
  - `render_preview` returns 1-4 low-cost frames tiled in one image within seconds, so the agent can look, fix, and repeat.
28
31
  - `render_estimate` says how long a render will take ("about 3 minutes") and whether it fits today's free quota.
29
32
  - `render_final` renders the frames on GPUs and joins them into an MP4; `render_status` reports the remaining time.
30
33
  - Free beta: no charges. Each key gets 10 GPU-minutes per day; a final render is up to 240 frames at 1080p.
31
34
  Paid plans (per GPU second, prepaid credit) will be announced on the service page before they start.
32
- - Inputs and results are deleted 24 hours after last use and are never used for training.
35
+ - Inputs and results are deleted 24 hours after last use and are never used for training. A render you choose to
36
+ share (`render_share`) gets a public page (`/r/<id>`, OGP for X and Discord) that stays until you unshare it.
33
37
 
34
38
  ## Status
35
39
 
@@ -105,6 +109,7 @@ Then, in Claude Code:
105
109
  | `render_final(scene_id, frame_start, frame_end, width, height, samples, fps, output, environment?, blender?, engine?, transparent?, notify_url?)` | frames (png / exr) or video (mp4 / webm / prores / gif / webp); `transparent=True` keeps an alpha background (png / exr / webm / gif / webp); `notify_url` gets one JSON POST when the job finishes; returns job_id + estimate |
106
110
  | `render_status(job_id)` | progress and ETA (`eta.human`); `render_download(job_id, only="mp4" / "frames" / "all")` files or links; `render_cancel(job_id)` |
107
111
  | `billing()` | free-beta quota (used today, daily limit, reset time); later balance and top-up link |
112
+ | `render_share(job_id, title?, note?, include_script?, listed?)` | a public page `/r/<id>` with the image or video, the conditions and (optionally) the script; survives the 24-hour expiry until `render_unshare`; listed pages appear in `/gallery` |
108
113
  | `asset_search(query, kind)` | CC0 models / textures / HDRIs on Poly Haven by words; results carry `polyhaven:<id>` and the entry file |
109
114
  | `render_info()` | workers online or gated (GPU lent to another workload), queue, expected wait, supported inputs, environment presets, Blender versions |
110
115
 
@@ -114,7 +119,7 @@ Options on `render_preview` / `render_final`: `environment` (`studio`, `sunset`,
114
119
  sent with the scene, found in the script through `os.environ["JR_ASSETS_DIR"]`; a `.blend`'s external files are collected
115
120
  automatically with `pip install janction-render[blend]`) or `asset_urls` (remote).
116
121
 
117
- CLI: `janction-render inspect|preview|render|status|download|cancel|jobs|balance|topup|info`.
122
+ CLI: `janction-render inspect|preview|render|status|download|share|unshare|cancel|jobs|balance|topup|info`.
118
123
 
119
124
  ## Writing a scene script
120
125
 
@@ -146,6 +151,8 @@ POST /v1/jobs {scene_id, kind: info|preview|final, frames|frame_start/frame_en
146
151
  environment, environment_strength, environment_visible, blender, orbit, orbit_frames, orbit_elevation}
147
152
  GET /v1/jobs/{id} status, progress, eta, artifacts[], cost, warnings, info DELETE /v1/jobs/{id} cancel
148
153
  GET /v1/jobs/{id}/artifacts/{name} PNG / MP4
154
+ POST /v1/jobs/{id}/share {title?, note?, include_script?, listed?} -> {share_id, url} DELETE /v1/jobs/{id}/share GET /v1/shares
155
+ GET /r/{share_id} public page (no key) GET /gallery
149
156
  GET /v1/assets/search?q=&kind=models|textures|hdris CC0 assets (Poly Haven) -> spec polyhaven:<id>
150
157
  POST /v1/files/{id}/assets/urls {urls: ["https://...", "polyhaven:<id>"]} fetch assets server-side
151
158
  POST /mcp remote MCP (Streamable HTTP; Bearer api key or OAuth)
@@ -1,3 +1,3 @@
1
1
  """JANCTION Render — ターミナルの AI エージェントから GPU で 3DCG を描く。"""
2
2
 
3
- __version__ = "0.4.3"
3
+ __version__ = "0.4.5"
@@ -136,6 +136,17 @@ def cmd_cancel(c: Client, args: argparse.Namespace) -> int:
136
136
  return 0
137
137
 
138
138
 
139
+ def cmd_share(c: Client, args: argparse.Namespace) -> int:
140
+ v = c.share(args.job, title=args.title or "", note=args.note or "", include_script=bool(args.script), listed=not args.unlisted)
141
+ print(v["url"])
142
+ return 0
143
+
144
+
145
+ def cmd_unshare(c: Client, args: argparse.Namespace) -> int:
146
+ print(_j(c.unshare(args.job)))
147
+ return 0
148
+
149
+
139
150
  def cmd_jobs(c: Client, args: argparse.Namespace) -> int:
140
151
  rows = c.jobs(args.limit)
141
152
  for j in rows:
@@ -221,6 +232,16 @@ def build_parser() -> argparse.ArgumentParser:
221
232
  s.add_argument("--wait", type=float, default=0, help="seconds to wait for the job first")
222
233
  s.set_defaults(fn=cmd_download)
223
234
 
235
+ s = sub.add_parser("share", help="publish a finished job as a public page (/r/<id>)")
236
+ s.add_argument("job")
237
+ s.add_argument("--title", default=None)
238
+ s.add_argument("--note", default=None)
239
+ s.add_argument("--script", action="store_true", help="include the bpy script on the page")
240
+ s.add_argument("--unlisted", action="store_true", help="do not list the page in the gallery")
241
+ s.set_defaults(fn=cmd_share)
242
+ s = sub.add_parser("unshare", help="remove the public page of a job")
243
+ s.add_argument("job")
244
+ s.set_defaults(fn=cmd_unshare)
224
245
  s = sub.add_parser("cancel")
225
246
  s.add_argument("job")
226
247
  s.set_defaults(fn=cmd_cancel)
@@ -361,6 +361,23 @@ class Client:
361
361
  self._raise(r)
362
362
  return r.json()
363
363
 
364
+ def share(self, job_id: str, title: str = "", note: str = "", include_script: bool = False, listed: bool = True) -> dict[str, Any]:
365
+ """描けた結果を公開ページにする(/r/<id>)。"""
366
+ r = self.s.post(f"{self.server}/v1/jobs/{job_id}/share", headers=self._headers(),
367
+ json={"title": title, "note": note, "include_script": include_script, "listed": listed}, timeout=120)
368
+ self._raise(r)
369
+ return r.json()
370
+
371
+ def unshare(self, job_id: str) -> dict[str, Any]:
372
+ r = self.s.delete(f"{self.server}/v1/jobs/{job_id}/share", headers=self._headers(), timeout=60)
373
+ self._raise(r)
374
+ return r.json()
375
+
376
+ def shares(self) -> dict[str, Any]:
377
+ r = self.s.get(f"{self.server}/v1/shares", headers=self._headers(), timeout=30)
378
+ self._raise(r)
379
+ return r.json()
380
+
364
381
  def fetch_asset_urls(self, scene_id: str, urls: list[str]) -> dict[str, Any]:
365
382
  """URL か polyhaven:<id> の素材を受付に取らせてシーンに足す(受付が取るので手元には要らない)。"""
366
383
  r = self.s.post(f"{self.server}/v1/files/{scene_id}/assets/urls", headers=self._headers(),
@@ -89,7 +89,8 @@ ENV_DOC = ("environment: '' keeps the scene's own world; 'studio' | 'sunset' | '
89
89
  "os.environ['JR_ASSETS_DIR'].")
90
90
  POLYHAVEN_DOC = (" assets may also hold 'polyhaven:<id>' (a CC0 model, texture or HDRI from polyhaven.com, fetched by the "
91
91
  "service into JR_ASSETS_DIR/<id>/; find ids with asset_search) or an https URL. Import a model with "
92
- "bpy.ops.import_scene.gltf(filepath=os.path.join(os.environ['JR_ASSETS_DIR'], '<id>', '<id>_1k.gltf')).")
92
+ "bpy.ops.import_scene.gltf(filepath=os.path.join(os.environ['JR_ASSETS_DIR'], '<id>', '<id>_1k.gltf')). "
93
+ "Inside the scene script, `import jr_assets` gives helpers: jr_assets.import_model('<id>', location=(x,y,z), scale=1.0) imports a Poly Haven model (or any file under JR_ASSETS_DIR) and returns the objects; jr_assets.material('<id>', scale=1.0) builds a Principled material from a Poly Haven texture set (diff / nor_gl / rough / arm / disp); jr_assets.apply(obj, mat) assigns it; jr_assets.world_hdri('<id>', strength=1.0) uses a Poly Haven HDRI as the world.")
93
94
  ENV_DOC = ENV_DOC + POLYHAVEN_DOC
94
95
 
95
96
 
@@ -516,6 +517,27 @@ def render_cancel(job_id: str) -> str:
516
517
  return _j({"ok": False, "error": str(exc)})
517
518
 
518
519
 
520
+ @mcp.tool()
521
+ def render_share(job_id: str, title: str = "", note: str = "", include_script: bool = False, listed: bool = True) -> str:
522
+ """Publish a finished render as a public page the user can send to anyone (X, Discord, a client): the image or
523
+ video, the render conditions, an optional title and note, and the bpy script if include_script=True. The page
524
+ keeps a copy of the result after the job's 24-hour expiry, until render_unshare. listed=True also puts it in the
525
+ public gallery. Ask the user before sharing; return the url to them."""
526
+ try:
527
+ return _j(_client().share(job_id, title=title, note=note, include_script=include_script, listed=listed))
528
+ except ClientError as exc:
529
+ return _j({"ok": False, "error": str(exc)})
530
+
531
+
532
+ @mcp.tool()
533
+ def render_unshare(job_id: str) -> str:
534
+ """Remove the public share page of a job (the copy of the result is deleted)."""
535
+ try:
536
+ return _j(_client().unshare(job_id))
537
+ except ClientError as exc:
538
+ return _j({"ok": False, "error": str(exc)})
539
+
540
+
519
541
  @mcp.tool()
520
542
  def billing(topup_yen: int = 0) -> str:
521
543
  """Check the account's quota or credit. During the free beta it returns today's GPU-time usage,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janction-render
3
- Version: 0.4.3
3
+ Version: 0.4.5
4
4
  Summary: Cloud GPU render farm for Blender, for AI agents: MCP server (remote + stdio) and CLI. Render Blender without a GPU from Claude, ChatGPT, Claude Code, Codex, Cursor.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -40,13 +40,17 @@ without a GPU, or when rendering locally is slow.
40
40
  - CC0 assets by name: `asset_search("wooden table")` finds Poly Haven models, textures and HDRIs; pass `polyhaven:<id>` in
41
41
  `asset_urls` (remote) or `assets` (stdio) and the files land in `JR_ASSETS_DIR/<id>/` on the GPU, ready to import
42
42
  (`bpy.ops.import_scene.gltf(filepath=os.path.join(os.environ['JR_ASSETS_DIR'], 'wooden_table_02', 'wooden_table_02_1k.gltf'))`).
43
+ Scene scripts can `import jr_assets` for one-liners: `jr_assets.import_model('wooden_table_02')`,
44
+ `jr_assets.apply(floor, jr_assets.material('wood_floor_deck', scale=2))` (diff / normal / roughness / metal wired into a
45
+ Principled BSDF), `jr_assets.world_hdri('studio_small_09')`. See `samples/polyhaven_room.py`.
43
46
  - `scene_info` reads the scene without rendering (cameras, frame range, missing files).
44
47
  - `render_preview` returns 1-4 low-cost frames tiled in one image within seconds, so the agent can look, fix, and repeat.
45
48
  - `render_estimate` says how long a render will take ("about 3 minutes") and whether it fits today's free quota.
46
49
  - `render_final` renders the frames on GPUs and joins them into an MP4; `render_status` reports the remaining time.
47
50
  - Free beta: no charges. Each key gets 10 GPU-minutes per day; a final render is up to 240 frames at 1080p.
48
51
  Paid plans (per GPU second, prepaid credit) will be announced on the service page before they start.
49
- - Inputs and results are deleted 24 hours after last use and are never used for training.
52
+ - Inputs and results are deleted 24 hours after last use and are never used for training. A render you choose to
53
+ share (`render_share`) gets a public page (`/r/<id>`, OGP for X and Discord) that stays until you unshare it.
50
54
 
51
55
  ## Status
52
56
 
@@ -122,6 +126,7 @@ Then, in Claude Code:
122
126
  | `render_final(scene_id, frame_start, frame_end, width, height, samples, fps, output, environment?, blender?, engine?, transparent?, notify_url?)` | frames (png / exr) or video (mp4 / webm / prores / gif / webp); `transparent=True` keeps an alpha background (png / exr / webm / gif / webp); `notify_url` gets one JSON POST when the job finishes; returns job_id + estimate |
123
127
  | `render_status(job_id)` | progress and ETA (`eta.human`); `render_download(job_id, only="mp4" / "frames" / "all")` files or links; `render_cancel(job_id)` |
124
128
  | `billing()` | free-beta quota (used today, daily limit, reset time); later balance and top-up link |
129
+ | `render_share(job_id, title?, note?, include_script?, listed?)` | a public page `/r/<id>` with the image or video, the conditions and (optionally) the script; survives the 24-hour expiry until `render_unshare`; listed pages appear in `/gallery` |
125
130
  | `asset_search(query, kind)` | CC0 models / textures / HDRIs on Poly Haven by words; results carry `polyhaven:<id>` and the entry file |
126
131
  | `render_info()` | workers online or gated (GPU lent to another workload), queue, expected wait, supported inputs, environment presets, Blender versions |
127
132
 
@@ -131,7 +136,7 @@ Options on `render_preview` / `render_final`: `environment` (`studio`, `sunset`,
131
136
  sent with the scene, found in the script through `os.environ["JR_ASSETS_DIR"]`; a `.blend`'s external files are collected
132
137
  automatically with `pip install janction-render[blend]`) or `asset_urls` (remote).
133
138
 
134
- CLI: `janction-render inspect|preview|render|status|download|cancel|jobs|balance|topup|info`.
139
+ CLI: `janction-render inspect|preview|render|status|download|share|unshare|cancel|jobs|balance|topup|info`.
135
140
 
136
141
  ## Writing a scene script
137
142
 
@@ -163,6 +168,8 @@ POST /v1/jobs {scene_id, kind: info|preview|final, frames|frame_start/frame_en
163
168
  environment, environment_strength, environment_visible, blender, orbit, orbit_frames, orbit_elevation}
164
169
  GET /v1/jobs/{id} status, progress, eta, artifacts[], cost, warnings, info DELETE /v1/jobs/{id} cancel
165
170
  GET /v1/jobs/{id}/artifacts/{name} PNG / MP4
171
+ POST /v1/jobs/{id}/share {title?, note?, include_script?, listed?} -> {share_id, url} DELETE /v1/jobs/{id}/share GET /v1/shares
172
+ GET /r/{share_id} public page (no key) GET /gallery
166
173
  GET /v1/assets/search?q=&kind=models|textures|hdris CC0 assets (Poly Haven) -> spec polyhaven:<id>
167
174
  POST /v1/files/{id}/assets/urls {urls: ["https://...", "polyhaven:<id>"]} fetch assets server-side
168
175
  POST /mcp remote MCP (Streamable HTTP; Bearer api key or OAuth)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "janction-render"
7
- version = "0.4.3"
7
+ version = "0.4.5"
8
8
  description = "Cloud GPU render farm for Blender, for AI agents: MCP server (remote + stdio) and CLI. Render Blender without a GPU from Claude, ChatGPT, Claude Code, Codex, Cursor."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes