@deepblend/dsh-blender-provider-local 0.2.10 → 0.2.11

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepblend/dsh-blender-provider-local",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "type": "module",
5
5
  "description": "Local Blender Provider: the BlenderRuntime Service over ctx.subprocess (batch execution).",
6
6
  "main": "lib/index.js",
@@ -11,7 +11,7 @@
11
11
  "./package.json": "./package.json"
12
12
  },
13
13
  "dependencies": {
14
- "@deepblend/dsh-blender-contracts": "0.2.10"
14
+ "@deepblend/dsh-blender-contracts": "0.2.11"
15
15
  },
16
16
  "license": "MIT",
17
17
  "repository": {
@@ -140,6 +140,29 @@ class _Bridge:
140
140
  self.thread = threading.Thread(target=self._serve, name="deepblend-bridge", daemon=True)
141
141
  self.thread.start()
142
142
 
143
+ @staticmethod
144
+ def derive_workspace(open_file):
145
+ """The workspace a Blender belongs to, read from the file it has open — or nothing at all.
146
+
147
+ WHY THIS IS A DERIVATION AND NOT A GUESS. A checkpoint the product wrote lives at
148
+ `<workspace>/.deepblend/projects/<id>/revisions/<rev>/scene.blend`, so a Blender that has one
149
+ of those open IS working in that workspace — the path says so, in the product's own layout.
150
+ Anything else (an empty path, a file the user made themselves, a project laid out differently)
151
+ returns nothing, and nothing is what the product treats as "serves anything".
152
+
153
+ THAT ASYMMETRY IS THE POINT: a bridge that states a workspace it cannot be sure of would be
154
+ refused by the product for a mismatch the user did not cause, and the fix would look like a
155
+ configuration problem when it is a wrong guess. Silence is the honest answer when the path does
156
+ not say.
157
+ """
158
+ if not isinstance(open_file, str) or open_file == "":
159
+ return ""
160
+ marker = os.sep + ".deepblend" + os.sep
161
+ head, separator, _ = open_file.partition(marker)
162
+ if separator == "" or head == "":
163
+ return ""
164
+ return head
165
+
143
166
  def _someone_is_listening(self):
144
167
  """Whether a live bridge is behind this path, rather than a file left by a dead one."""
145
168
  probe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@@ -292,7 +315,11 @@ class DEEPBLEND_PT_bridge(bpy.types.Panel):
292
315
  def register():
293
316
  global _BRIDGE
294
317
  socket_path = os.environ.get("DEEPBLEND_BRIDGE_SOCKET", DEFAULT_SOCKET)
295
- _BRIDGE = _Bridge(socket_path, {"workspace": os.environ.get("DEEPBLEND_BRIDGE_WORKSPACE", "")})
318
+ workspace = os.environ.get("DEEPBLEND_BRIDGE_WORKSPACE", "")
319
+ if workspace == "":
320
+ # In a GUI the user has a file open, and if the product wrote it, it names the workspace.
321
+ workspace = _Bridge.derive_workspace(getattr(bpy.data, "filepath", ""))
322
+ _BRIDGE = _Bridge(socket_path, {"workspace": workspace})
296
323
  try:
297
324
  _BRIDGE.start()
298
325
  except Exception as exc:
@@ -331,6 +358,10 @@ def main():
331
358
  continue
332
359
  index += 1
333
360
  socket_path = options.get("socket", DEFAULT_SOCKET)
361
+ if workspace == "":
362
+ # WHAT THE OPEN FILE SAYS, when it says anything. An operator's explicit setting always wins;
363
+ # this only fills a silence, and only from a path that follows the product's own layout.
364
+ workspace = _Bridge.derive_workspace(getattr(bpy.data, "filepath", ""))
334
365
 
335
366
  if bootstrap is None:
336
367
  print(json.dumps({"kind": "error", "message": BOOTSTRAP_PROBLEM}), flush=True)