Scene Manager: request publishes missing on this site

- Request (globe) on casting rows and loaded assets whose latest publish
  is not on this site, and Request Missing for all of them, through the
  new wtdcmds command wtd_request_revisions. Requested publishes show
  "requested, waiting for the download".
- The version picker requests a version that is not on this site.
- A 404 explains that the session is gone or wtdcmds is too old.
- Fake server: car (vehicles) with a publish not on this site,
  wtd_request_revisions and /_test/download; 45 checks.
- Tested live: car_paul modeling.blend v001 -> Requested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
NDemaerschalkandClaude Opus 5.5 committed 2026-10-27 11:45:17 +01:00
1 parent 9ee7710479
commit 9fe38ce58a
8 files changed
+197 -19

No files matched your search

+104 -3
View File
@@ -120,9 +120,36 @@ def version_status(library):
return "LATEST", "latest"
if available is not None and available["name"] != current:
return "UPDATE", f"{available['name']} available"
if latest.get("sync_status") == "Requested":
return "PENDING", f"{latest['name']} requested, waiting for the download"
return "PENDING", f"{latest['name']} published, not available on this site yet"
def file_data(file_oid, default=None):
"""Latest known revisions of a file (refreshed by requests and version checks)."""
return STATE.files.get(file_oid) or default
def to_request(file):
"""The latest revision of a file when it is not on this site and not requested yet, else None."""
latest = (file or {}).get("latest")
if latest and latest.get("oid") and not latest.get("available") and latest.get("sync_status") != "Requested":
return latest
return None
def missing_files(scene):
"""File oids of the cast and loaded assets whose latest publish can be requested."""
casting = STATE.casting.get(context_oid(scene)) or {"assets": []}
file_oids = [a["file"]["file_oid"] for a in casting["assets"] if a.get("file")]
file_oids += [library["wtd_file_oid"] for library in lib.managed_libraries()]
return [oid for oid in dict.fromkeys(file_oids) if to_request(file_data(oid))]
def _asset_of(file_oid):
return file_oid.split("/assets/", 1)[-1].split("/", 1)[0]
# --------------------------------------------------------------------------- #
# Background requests
# --------------------------------------------------------------------------- #
@@ -225,6 +252,41 @@ def refresh_versions(quiet=False):
)
def request_revisions(revisions):
"""Ask Libreflow to download revisions on this site ({revision oid: file oid})."""
uid = session_uid()
def done(result):
requested, messages = [], []
for oid, data in result.items():
file_oid = revisions.get(oid, "")
what = f"{_asset_of(file_oid)} {data.get('name') or oid.rsplit('/', 1)[-1]}"
if data.get("error"):
messages.append(f"{what}: {data['error']}")
continue
file = STATE.files.get(file_oid)
if file is not None and (file.get("latest") or {}).get("oid") == oid:
file["latest"] = data
if data.get("available"):
file["latest_available"] = data
for revision in STATE.revisions.get(file_oid, []):
if revision.get("oid") == oid:
revision.update(data)
if data.get("available"):
messages.append(f"{what} is already on this site: refresh")
else:
requested.append(what)
text = f"Requested {', '.join(requested)}" if requested else "Nothing requested"
report(text + ("; " + "; ".join(messages) if messages else ""), "WARNING" if messages else "INFO")
ipc.run(
"Requesting",
lambda: ipc.call("wtd_request_revisions", uid, revision_oids=list(revisions)),
done,
_on_error("Request"),
)
# --------------------------------------------------------------------------- #
# Settings
# --------------------------------------------------------------------------- #
@@ -486,6 +548,34 @@ class WTD_OT_scene_update(bpy.types.Operator):
return {"FINISHED"}
class WTD_OT_scene_request(bpy.types.Operator):
bl_idname = "wtd_scene.request"
bl_label = "Request"
bl_description = (
"Ask Libreflow to download the latest publish on this site (from the exchange site, "
"or from the site that published it). Refresh once it is downloaded"
)
file_oid: StringProperty(description="Only the latest publish of this file", options={"SKIP_SAVE"})
@classmethod
def poll(cls, context):
return connected()
def execute(self, context):
file_oids = [self.file_oid] if self.file_oid else missing_files(context.scene)
revisions = {}
for file_oid in file_oids:
revision = to_request(file_data(file_oid))
if revision is not None:
revisions[revision["oid"]] = file_oid
if not revisions:
self.report({"INFO"}, "Nothing to request")
return {"CANCELLED"}
request_revisions(revisions)
return {"FINISHED"}
def _revision_items(self, context):
return _REVISION_ITEMS
@@ -524,7 +614,12 @@ class WTD_OT_scene_pick_revision(bpy.types.Operator):
(
r["name"],
f"{r['name']} {time.strftime('%Y-%m-%d', time.localtime(r['date'])) if r.get('date') else ''}"
f" {r.get('user') or ''}" + ("" if r.get("available") else " (not on this site)"),
f" {r.get('user') or ''}"
+ (
""
if r.get("available")
else " (requested)" if r.get("sync_status") == "Requested" else " (not on this site: request)"
),
r.get("comment") or "",
"CHECKMARK" if r["name"] == current else "NONE",
index,
@@ -542,9 +637,14 @@ class WTD_OT_scene_pick_revision(bpy.types.Operator):
library = bpy.data.libraries.get(self.library)
if library is None:
return {"CANCELLED"}
revision = next(r for r in STATE.revisions[library["wtd_file_oid"]] if r["name"] == self.revision)
file_oid = library["wtd_file_oid"]
revision = next(r for r in STATE.revisions[file_oid] if r["name"] == self.revision)
if not revision.get("available"):
self.report({"WARNING"}, f"{revision['name']} is not available on this site")
if revision.get("sync_status") == "Requested":
self.report({"WARNING"}, f"{revision['name']} is requested, waiting for the download")
else:
request_revisions({revision["oid"]: file_oid})
self.report({"INFO"}, f"{revision['name']} is not on this site: requested, pick it again once downloaded")
return {"CANCELLED"}
updated, messages = _update_libraries([self.library], target=revision)
if messages:
@@ -844,6 +944,7 @@ classes = (
WTD_OT_scene_refresh,
WTD_OT_scene_load,
WTD_OT_scene_update,
WTD_OT_scene_request,
WTD_OT_scene_pick_revision,
WTD_OT_scene_remove,
WTD_OT_scene_select_instance,