Files
vfxreview/VFXReviewConnector.md
T
twotalesanimation bada122f42
Deploy / deploy (push) Successful in 7m4s
feat(pipeline): per-project AE templates, and a workspace that fits any show
Initialise Workspace built UNG_101..UNG_117 from a hardcoded range and
imported one hardcoded AEP, and fourteen findComp calls named UNG's comps
literally. None of it worked for a second show.

Comp names now follow a convention derived from the show id —
{showId}_VFX_OVERLAY, {showId}_EXPORT_TEMPLATE, _SHOW LUT — which
reproduces UNG's existing names exactly, so nothing changes for the show
it was written for. A project whose template departs from the pattern can
override any of the three.

Paths cannot be derived: UNG's show id is UNG while its folder is
UNGO_VFX. Those live on Project.pipelineConfig and are edited under
Project → Settings → Pipeline. They are resolved server-side and handed
to the panel with the project list, and read by buildPreviewManifest for
the headless build, so the panel and the farm cannot disagree about which
template — or which show LUT — a shot was built with. That mattered more
than the folder names: a preview rendered through another show's LUT is
quietly the wrong colour rather than obviously broken.

Episode folders come from the project's episode list, falling back to the
folders under its footage root for a show whose episodes are not entered
yet.

Panel 2.5.0. Adds migration 20260814010000_project_pipeline_config.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 08:24:40 +02:00

611 lines
27 KiB
Markdown

# VFXReview Connector
A dockable ScriptUI panel for Adobe After Effects 2024+ that integrates with the VFXReview platform to streamline shot management, render queuing, and workspace setup for VFX production pipelines.
---
## Configuration
Settings are stored per-machine via the **⚙ Settings** button in the panel (persisted in After Effects' built-in preferences — no need to edit the JSX file).
| Setting | Purpose |
|---|---|
| **Server URL** | Base URL of the VFXReview server (e.g. `https://review.twotalesvfx.com`) |
| **API Token** | Bearer token matching `API_SECRET_KEY` in the server `.env` |
| **Export Root** | Local path where renders and deliverables are written |
| **Footage Root** | Local path where EXR plate folders are read from |
| **Banner Path** | Path to the panel banner image |
| **Artist Email** | Used for render attribution in the pipeline |
| **Handle Frames** | Plate handle frames prepended before the shot's source TC in-point (default: 8) |
| **Path Mappings** | *Optional.* Rewrites paths the **server** supplies — see below |
### Server paths vs. this machine
The server addresses render folders from its own root (`render.outputRoot`,
e.g. `//SAN`), which a workstation may not resolve — the render worker
translates them with its own `pathMappings` because it runs as a service
account. The panel needs the same translation to open a render for QC.
It resolves this itself in most cases, trying in order:
1. **Path Mappings**, if configured — format `//SAN/=V:/_EXPORTS/`, several
separated by `;`.
2. **The server path as given** — correct when `render.outputRoot` is already
a path this machine can reach.
3. **Re-based on Export Root** — the server's export layout mirrors the panel's
from the show folder down (`{root}/{showId}/{episode}/{shot}/{version}`), so
the tail is simply re-attached to Export Root.
Step 3 is why this usually needs no configuration. **Diagnostics** checks the
whole thing against a real render folder from the QC queue and names every path
it tried when it fails.
### More than one on-disk convention
Every candidate is tested for existence and the first that resolves wins, so a
studio whose output root changed at some point can map the **same prefix more
than once** and let the panel work out which applies per export:
```
//SAN/=V:/_EXPORTS/;//SAN/=V:/_EXPORTS/UNG/
```
Older exports keep whatever path was stored on them when they were queued —
changing `render.outputRoot` on the server only affects new ones — so mixed
conventions in one database are normal and permanent. Add a mapping per
convention rather than trying to make one rewrite cover them all.
---
## Pipeline Folder Structure
### Export Root
All outputs from the connector are written under **`<Export Root>/{SHOW_ID}/`** (e.g. `V:/_EXPORTS`). The show ID is appended automatically from the selected project — configure `exportRoot` as the bare root without a show ID.
```
<Export Root>/
└── {SHOW_ID}/ # e.g. UNG/
├── {SHOT_CODE}/ # e.g. UNG_108_001_010/
│ ├── {shot}_cmp_TT_{ver}.[#####].exr # Queue EXR / Queue EXR (Review)
│ └── {shot}_cmp_TT_{ver}.mp4 / .mov # Queue MP4 / Queue MOV
│
├── DELIVERY/
│ └── {EPISODE_OR_SCENE}/ # e.g. UNG_108/
│ └── {YYYYMMDD}_Delivery/
│ └── {SHOT_CODE}/
│ └── {shot}_cmp_TT_{ver}.[#####].exr
│
└── (render farm outputs — managed by the server)
└── {PROJECT_CODE}/{EPISODE}/{SHOT_CODE}/{ver}/
└── {shot}_cmp_TT_{ver}.[#####].exr
```
**Version naming:** `{SHOT_CODE}_cmp_TT_{version}` — e.g. `UNG_108_001_010_cmp_TT_v003.[01001].exr`
Versions are set manually via **Increment Shot Version** or the shot detail page. Queuing a render does **not** auto-increment the version.
---
### Footage Root
Source EXR plates are read from **`<Footage Root>/{SHOW_ID}/`**. Configure `footageRoot` as the bare root (e.g. `V:/_FOOTAGE`).
```
<Footage Root>/
└── {SHOW_ID}/ # e.g. UNG/ or DH2_VFX/
├── {EPISODE_CODE}/ # episodic only: e.g. UNG_108/
│ └── {SHOT_CODE}/ # standard layout: plates grouped per shot
│ └── {SHOT_CODE}_{ELEM}_{VER}/
│ └── *.exr
│
└── _PICLOCKS/
└── {EPISODE_CODE}_*.mov # matched by episode prefix
```
### Frame rate
Comps are built at the **shot record's `fps`**, and plates are conformed to it.
That value is the show's delivery rate and the authority; a plate's own rate is
whatever happens to be on disk.
`fps` defaults to 24 when a shot is created, so **check it after importing a
show that is not 24** — every shot carries its own value, and a wrong one
produces comps at the wrong rate, plates conformed to the wrong speed, and
timecode-to-frame sums that are quietly off.
A plate at a whole-number multiple of the show rate (50 fps on a 25 fps show) is
a deliberate over-crank and is conformed as intended. Validation reports the
difference either way, saying which of the two it looks like.
**Scenes are not folders.** Standard projects have no level between the show
folder and the shot folder — the scene picker filters the shot list only.
**Two layouts are supported, and neither is migrated.** The connector prefers the
grouped layout above; where a `{SHOT_CODE}/` folder does not exist it falls back
to scanning the parent for folders named `{SHOT_CODE}_*` (the original UNG
layout, where plates sit directly in the episode folder). All matching folders
are imported as separate footage layers. If a `{SHOT_CODE}/` folder exists but
holds no plate subfolders, that is reported as no footage — the fallback scan is
not attempted, so stray plate folders sitting beside the shot cannot be picked up
as its footage. Underscore-prefixed folders (`_ARCHIVE`, `_GFX`, `_PULLS`, …) are
never treated as shots or plates.
---
### Episodic vs. Standard productions
| | Episodic | Standard |
|---|---|---|
| Shot code format | `{SHOW}_{EP}_{SCENE}_{NUM}` | `{SHOW}_{SCENE}_{NUM}` |
| Shot Builder | Episode dropdown → Shot dropdown | Scene dropdown → Shot dropdown |
| Footage path | `{footageRoot}/{showId}/{EPISODE}/` | `{footageRoot}/{showId}/{SCENE}/` |
| Export path | `{exportRoot}/{showId}/{SHOT_CODE}/` | same |
| Delivery subfolder | `DELIVERY/{EPISODE}/…` | `DELIVERY/{SCENE}/…` |
---
## Migrating an existing project (e.g. UNG)
If you previously set `exportRoot = V:/_EXPORTS/UNG`, update it to `V:/_EXPORTS`. The code now appends `/{showId}/` automatically, so `V:/_EXPORTS/UNG/…` is still produced — no files need to move.
Same applies to `footageRoot`: change `V:/_FOOTAGE/UNG` → `V:/_FOOTAGE`.
---
## New production checklist
1. Create `<Footage Root>/{SHOW_ID}/[{EPISODE_CODE}/]{SHOT_CODE}/` — episode level for episodic shows only, never a scene level — and place each plate in its own `{SHOT_CODE}_{ELEM}_{VER}/` folder inside it.
2. `<Export Root>/{SHOW_ID}/` is created automatically on first render.
3. Add the project to the VFXReview server with the correct **Show ID** set.
4. Open the panel in After Effects, click **⚙**, enter the Server URL, API Token, bare `exportRoot`, and bare `footageRoot`, then click **Save**.
5. The **Project** dropdown will populate automatically. Select the production and the episode/scene list will load.
| Variable | Default | Description |
|---|---|---|
| `TOKEN` | `am3O0PWU…` | Bearer token used to authenticate all API requests |
| `BASE_URL` | `https://review.twotalesvfx.com` | Base URL of the VFXReview platform |
| `PROJECT_CODE` | `UNG_S1` | Project identifier used in all API endpoints |
| `EXPORT_ROOT` | `V:/_EXPORTS/UNG` | Root path where rendered outputs are written |
| `FOOTAGE_ROOT` | `V:/_FOOTAGE/UNG` | Root path where source EXR footage sequences live |
| `BANNER_IMAGE_PATH` | `V:/VFXReviewConnector/banner.png` | Optional banner image displayed at the top of the panel |
---
## Panel Layout
The panel is divided into seven sections:
1. **Shot Builder** — Episode/shot selection and comp creation
2. **Render Pipeline** — Queue Export, status polling, retry, regenerate previews
3. **QC** — Technical QC queue for rendered exports
4. **Actions** — Per-shot and batch operations
5. **Workspace** — One-time project initialisation
6. **Color Space** — OCIO effect management
7. **Status** — Live feedback, plus Diagnostics and the last report
---
## Shot Builder
### Episode Dropdown
Populated automatically on panel load by calling `GET /api/ext/projects/{PROJECT_CODE}/episodes`. Each item stores the episode label (e.g. `UNG_106`) and the raw API episode value used for subsequent shot queries.
### Shot Dropdown
Populated when an episode is selected by calling `GET /api/ext/projects/{PROJECT_CODE}/shots?episode={value}`. Displays shot codes (e.g. `UNG_106_010_020`).
### Build Shot
Constructs a full shot composite in the open AE project from the episode and shot selected in the dropdowns.
**Process:**
1. Calls the shot lookup API to retrieve metadata (description, notes, episode, scene, etc.).
2. If a comp with the shot code already exists, updates its overlay instead of rebuilding.
3. Scans `FOOTAGE_ROOT/{episodeCode}/` for subfolders matching `{shotCode}_*` and imports each as an EXR image sequence at 24 fps.
4. Creates a `{shotCode}_FOOTAGE` precomp containing all imported sequences, each with an OCIO Color Space Transform effect added (disabled by default).
5. Creates the main `{shotCode}` comp and places the footage precomp inside it.
6. If a `_SHOW LUT` comp exists in the project, adds it as a collapsed layer on top.
7. Calls **Add Overlay** logic to attach the `UNG_VFX_OVERLAY` comp.
8. Organises items into `__SHOTS/{episodeCode}`, `_FOOTAGE_4K`, and `_PRECOMPS` project folders.
If any step fails, the status line names the stage and a copyable report opens —
see [Diagnostics and logging](#diagnostics-and-logging).
---
## Render Pipeline
### Queue Export
Saves the project, builds the render manifest from the selected comp and the
shot record, and submits it to the farm.
| Option | Effect |
|---|---|
| **Urgent** | Priority 20 — claimable outside the render window, ahead of normal jobs |
| **No preview** | Renders the EXRs but builds no preview, inheriting the previous export's MOV, review MP4 and thumbnail |
**No preview** exists for deliverables framed differently from the review —
unscaled or un-punched-in EXRs. A preview rebuilt from those frames would be
the wrong picture, while the earlier export's MOV is already correct, so it
carries over instead. The export still goes through QC, and the delivery
package still gets a MOV.
Leave it unticked for an ordinary submission: the preview is what QC and the
client review see.
---
## QC
Technical QC on the **rendered EXRs**, not the H.264 preview — the point is
catching mask slips, export glitches and metadata faults at full resolution.
QC is per-export: a new export always needs new QC.
| Control | Behaviour |
|---|---|
| **Load QC Queue** | Lists every export in `READY_FOR_QC` for the selected project, oldest first. Entries flagged with a warning count had a non-blocking validation warning worth a closer look. |
| **Open Render** (or double-click) | Imports that export's actual EXR sequence into a `QC_{shot}_{version}` comp at full res and opens it. Re-selecting an already-imported shot just reopens the comp. |
| **Notes** | Sent with the decision. Optional on pass; a fail with no notes asks for confirmation, because the artist only sees what you write here. |
| **QC Pass** | Marks the version **ready for delivery** and nothing else — nothing is shared to the client, no approval state changes, no task moves. |
| **QC Fail** | Sets the export to `QC_FAILED` and the comp task to **CHANGES**. The artist fixes and queues a new version; the old export is superseded. |
Passing an export is what makes it eligible for a delivery package on the web
app's **Pipeline → Deliveries** page.
---
## Actions
### Refresh
Updates the **Status** panel to show the current count of selected `CompItem`s. Has no effect on the project.
---
### Fix Comp Names
Renames selected comps so their names match the shot code extracted from their current name.
- Shot codes are read by anchoring on the selected project's showId — see [Shot Code Convention](#shot-code-convention).
- If a comp name contains a valid shot code but has extra text, it is trimmed to the code only.
- Wrapped in an undo group.
---
### Add Overlay
Adds or updates the `UNG_VFX_OVERLAY` comp as a layer on each selected comp (or the active comp if nothing is selected).
**Process:**
1. Looks up each shot via the API to confirm it exists.
2. If the overlay layer is not already present, adds the `UNG_VFX_OVERLAY` comp and moves it to the top of the layer stack.
3. Sets two Essential Properties on the overlay layer:
- **DATE** — today's date in `YYYY/MM/DD` format.
- **SHOT NAME** — `{shotCode}_cmp_TT_v001`.
4. Reports how many overlays were added vs. updated vs. skipped.
5. Wrapped in an undo group.
**Requirement:** A comp named `UNG_VFX_OVERLAY` must exist in the project.
---
### Build Preview
Creates a `{shotCode}_PREVIEW` comp for each selected (or active) comp using a standardised export template.
**Process:**
1. Duplicates the `UNG_EXPORT_TEMPLATE` comp and names the copy `{shotCode}_PREVIEW`.
2. Moves the copy into the `_PREVIEWS` project folder if it exists.
3. Replaces the `SHOT` and `THUMBNAIL` layers with the shot comp.
4. Populates the `NETFLIX_SLATE` Essential Properties:
- Shot name (`{shotCode}_cmp_TT_v001`)
- Date, Description, Notes, Shot Code, Episode, Scene, Frame count
5. Adjusts the preview comp duration to match the shot comp duration.
6. Wrapped in an undo group.
**Requirements:**
- `UNG_EXPORT_TEMPLATE` comp must exist in the project.
- Shot metadata must be available from the API.
---
### Queue EXR
Queues each selected comp for EXR sequence render using the **EXR Sequence** output module template.
**Process:**
1. Looks up each shot via the API to get the `exrOutput` filename stem.
2. Sets the comp's display start frame to `1001`.
3. Disables any layers named `UNG_VFX_OVERLAY` or `_SHOW LUT` in the comp.
4. Ensures the output folder `EXPORT_ROOT/{shotCode}/` exists.
5. Queues the comp with output path `{folderPath}/{exrOutput}.[#####].exr`.
6. Wrapped in an undo group.
**Requirement:** The `EXR Sequence` output module template must be configured in AE preferences.
---
### Queue MP4
Queues a preview render for each selected (or active) comp as an MP4 file.
- Uses the **REVIEW_PREVIEW** output module template.
- Output path: `EXPORT_ROOT/{shotCode}_cmp_TT_v001.mp4`
- Automatically calls **Build Preview** first if a `{shotCode}_PREVIEW` comp does not exist.
- Updates the overlay on the main shot comp before queuing.
- Wrapped in an undo group.
**Requirement:** The `REVIEW_PREVIEW` output module template must be configured in AE preferences.
---
### Queue MOV
Identical to Queue MP4 but uses the **4444 Tri** output module template and writes a `.mov` file.
- Output path: `EXPORT_ROOT/{shotCode}_cmp_TT_v001.mov`
**Requirement:** The `4444 Tri` output module template must be configured in AE preferences.
---
### Import Renders
Imports rendered EXR sequences for the active/selected shot comp from the shared renders folder.
**Process:**
1. Resolves the renders folder at `X:/shared_projects_2026/UNGO_VFX/production/renders/{shotCode}`.
2. Iterates subfolders first (separate render passes/layers), importing each as an EXR sequence at 24 fps. Falls back to sequences directly in the root folder.
3. Adds each imported footage item to the `_RENDERS` project folder.
4. Adds all footage as layers to the target comp and applies the **Extractor** effect to each layer.
5. Pre-composes all added layers into a single `{shotCode}_RENDER` comp.
6. Wrapped in an undo group.
---
### Pull Picture Lock
Imports a picture-lock (offline cut) video file and trims it to the correct sequence timecode range for the shot, including handles.
**Process:**
1. Calls the API to retrieve `seqTimecodeStart` and `seqTimecodeEnd` for the shot.
2. Searches the `_PICLOCKS` project folder for already-imported footage matching the episode prefix — `{showId}_{episode}` on episodic shows (e.g. `UNG_106`), plain `{showId}` on standard ones.
3. If not found, scans `FOOTAGE_ROOT/_PICLOCKS/` for a file matching `{episodePrefix}_*` and imports it.
4. Calculates the in/out offsets from the sequence timecodes relative to the piclock's start (assumed `01:00:00:00` / 3590 s unless embedded timecode is available), with **±8 frame handles**.
5. Adds the piclock footage as a layer at the top of the comp, scales it to fill the frame, and sets `startTime`, `inPoint`, and `outPoint` to isolate the correct range.
6. Wrapped in an undo group.
**Requirement:** Shot must have `seqTimecodeStart` and `seqTimecodeEnd` populated in the VFXReview database.
---
## Workspace
### Per-project templates
Each show has its own template AEP — its overlay, slate/export template and,
most importantly, its **show LUT**. These are configured per project on the web
app under **Project → Settings → Pipeline**, not in the panel, because the
render farm's headless preview build reads the same values: if the two
disagreed, a farm preview would be quietly the wrong colour.
| Setting | Purpose |
|---|---|
| **Template project (.aep)** | Imported by Initialise Workspace and by every headless preview build |
| **Renders root** | Where per-shot pre-renders live; the panel appends the shot code (Import Renders) |
| **Overlay / Slate / LUT comp** | Only needed when a template departs from the naming convention |
Comp names default to a convention derived from the show ID:
`{showId}_VFX_OVERLAY`, `{showId}_EXPORT_TEMPLATE`, `_SHOW LUT`. A show whose
template follows the pattern needs no comp configuration at all — leave the
fields blank and they follow the convention, including any later change to it.
### Initialise Workspace
One-time setup that creates the standard project folder hierarchy, imports the slate/overlay template AEP, and configures color settings.
Works for any project: the episode folders and the template both come from the
selected project rather than being fixed to one show.
**Folder structure created:**
```
__SHOTS/
{EPISODE_CODE}/ ← one per episode, from the project's episode list
… (episodic shows only; standard shows get none)
_PREVIEWS/
_PRECOMPS/
_PICLOCKS/
_FOOTAGE_4K/
_STOCK/
_RENDERS/
_UTILITIES/
```
Episodes come from `GET /api/ext/projects/{code}/episodes`. If a show has none
entered yet, the folders found under its footage root are used instead, so the
button still does something useful on day one.
**Template import:**
Imports the project's **Template project (.aep)** as a project merge — see
[Per-project templates](#per-project-templates). If none is configured the
folders and colour settings are still applied, and the status line says the
template was not set.
**Color settings (applied outside the undo group):**
- Sets project bit depth to **32-bit**.
- Enables color management via `app.project.colorSettings.enabled`.
- Attempts to set the working color space to **ACES 1.2** by searching the available space list. Falls back to `"ACES - ACES2065-1"` if the list API is unavailable. If the scripting API does not support this, a message prompts manual configuration.
---
## Color Space
### Add OCIO to Footage Layers
Adds an **OCIO Color Space Transform** effect (disabled) to every footage layer in each selected (or active) comp.
- Only targets `FootageItem` layers backed by a `FileSource` (skips solids, comps, etc.).
- Sets the **Output Color Space** property to index `93`.
- The effect is added in a disabled state so it can be enabled selectively during grading.
- Wrapped in an undo group.
---
## API Reference
All HTTP calls use `system.callSystem` with `curl`. Requests include:
```
Authorization: Bearer {TOKEN}
Accept: application/json
```
| Endpoint | Method | Description |
|---|---|---|
| `/api/ext/projects/{PROJECT_CODE}/episodes` | GET | List all episodes for the project |
| `/api/ext/projects/{PROJECT_CODE}/shots?episode={value}` | GET | List shots for a given episode |
| `/api/ext/shots/lookup?shotCode={code}&projectCode={code}` | GET | Retrieve full metadata for a single shot |
### Shot Metadata Fields Used
| Field | Used By |
|---|---|
| `shotCode` | All operations — canonical identifier |
| `exrOutput` | Queue EXR — filename stem for EXR renders |
| `description` | Build Preview — slate Description property |
| `notes` | Build Preview — slate Notes property |
| `episode` | Build Preview — slate Episode property |
| `scene` | Build Preview — slate Scene property |
| `seqTimecodeStart` | Pull Picture Lock — in-point calculation |
| `seqTimecodeEnd` | Pull Picture Lock — out-point calculation |
---
## Shot Code Convention
Shot codes are the project's **showId** followed by three-digit tokens:
| Project type | Pattern | Example |
|---|---|---|
| Episodic | `{showId}_{episode}_{scene}_{shot}` | `UNG_106_010_020` (showId `UNG`) |
| Standard | `{showId}_{scene}_{shot}` | `DH2_VFX_027_010` (showId `DH2_VFX`) |
The showId is **not** always three letters and may itself contain underscores, so
codes are parsed by stripping the known showId prefix — never by counting
underscores or matching a fixed `AAA_` pattern. Everything after the showId is
three digits per token.
Episode codes follow `{showId}_{episode}` (e.g. `UNG_106`).
---
## Required Project Assets
The following comps/items must exist in the AE project for certain features to work:
| Name | Type | Required By |
|---|---|---|
| `UNG_VFX_OVERLAY` | Comp | Add Overlay, Build Preview, Queue MP4/MOV |
| `UNG_EXPORT_TEMPLATE` | Comp | Build Preview, Queue MP4/MOV |
| `_SHOW LUT` | Comp | Build Shot (optional — applied if present) |
| `_PREVIEWS` | Folder | Build Preview (optional — used if present) |
| `_PRECOMPS` | Folder | Build Shot (optional — used if present) |
| `_FOOTAGE_4K` | Folder | Build Shot (optional — used if present) |
| `_RENDERS` | Folder | Import Renders (optional — created if absent) |
| `_PICLOCKS` | Folder | Pull Picture Lock (optional — created if absent) |
| `__SHOTS` | Folder | Build Shot / Initialise Workspace |
---
## Output Module Templates Required
These must be created in **After Effects > Edit > Templates > Output Module** before using the corresponding queue buttons:
| Template Name | Used By | Format |
|---|---|---|
| `EXR Sequence` | Queue EXR | OpenEXR image sequence |
| `REVIEW_PREVIEW` | Queue MP4 | H.264 / MP4 |
| `4444 Tri` | Queue MOV | ProRes 4444 / MOV |
---
## Status Panel
The **Status** panel at the bottom of the UI shows two lines:
- **Line 1** — Number of currently selected `CompItem`s.
- **Line 2** — Result or progress message from the last operation.
Both lines are updated throughout batch operations so progress is visible in real time.
Below them are two buttons:
- **Diagnostics** — runs the environment self-test described below.
- **Show Last Report** — re-opens the trace from the last button pressed.
---
## Versions
The panel's version is the `PANEL_VERSION` constant at the top of
`VFXReviewConnector.jsx`. It appears in the panel title, in every diagnostics
report, and is sent to the server when the panel loads.
**Pipeline → Machines → Deployed tool versions** then shows which version each
workstation is running, alongside the render workers. Anything behind the newest
version seen for that tool is flagged `→ rebuild to vX` — so updating one
machine is what surfaces the ones still to do. No expected-version list to keep
in sync; the newest thing reporting in is the benchmark.
**Bump `PANEL_VERSION` whenever you copy the .jsx to a workstation.** If two
machines report the same version but behave differently, one of them was copied
without the bump.
---
## Diagnostics and logging
Every button press is traced: what it tried, in what order, with the paths and
HTTP codes it was working with. The trace goes to an in-memory buffer and to a
log file, and it is shown automatically when something fails.
**Log file:** `%APPDATA%/VFXReviewConnector/connector.log` (next to `config.json`).
It is truncated once it passes 1 MB, so it can always be pasted into a message.
The API token is never written to it — only whether one is set.
### When Build Shot fails
The status line now names the stage it failed at, and a scrollable report opens
with the whole trace. The stages, and what each one means:
| Stage | Meaning | Usual fix |
|---|---|---|
| `project` | No project selected in the panel | The project dropdown is empty because the server URL or token is wrong |
| `shot lookup` | The server returned no shot record | Report shows the HTTP code: `0` = server unreachable, `401/403` = bad token, `404` = shot not in that project |
| `configuration` | Footage Root is blank in Settings | Settings are **per-machine**; a fresh workstation starts blank |
| `footage folder` | The show/episode folder does not exist here | Report names the deepest folder that *does* exist — usually an unmapped drive or a different drive letter/UNC on that machine |
| `plate folders` | Folder exists, no plates for that shot code | Report lists the folders that *are* there, so a naming mismatch is visible |
| `plate import` | Plate folders matched but held no importable EXRs | Report lists each folder's contents (`n .exr, n other files`) |
| `comp creation` | After Effects threw while building the comps | Not a path or server problem — the AE error text is in the report |
| `unexpected error` | Anything thrown that was not anticipated | Report carries the error text, line number and stack |
Non-fatal conditions are traced too rather than passing silently: a missing
`_SHOW LUT`, an overlay that could not be applied, a missing `__SHOTS/{group}`
folder.
### Diagnostics button
Checks everything Build Shot depends on **without building anything**, so a
workstation can be cleared before an artist is blocked. It verifies:
1. `curl.exe` is present and runnable (every server call goes through it).
2. Server URL and API token are set, and `GET /api/ext/projects` succeeds.
3. Footage Root resolves to a folder that exists, and lists the plate folders
found for the currently selected shot.
4. Export Root resolves to a folder that exists.
5. The shot lookup succeeds for the currently selected shot.
6. The open AE project has `UNG_VFX_OVERLAY`, `UNG_EXPORT_TEMPLATE`,
`_SHOW LUT`, and the `__SHOTS` / `_FOOTAGE_4K` / `_PRECOMPS` / `_PREVIEWS`
folders.
Each failure is reported as a `PROBLEM` line with the offending path or HTTP
response, and the report ends with a problem count.
### Errors that used to be invisible
ScriptUI silently swallows exceptions thrown inside a button handler, so a hard
failure looked like a button that did nothing at all. Every handler is now
wrapped: anything thrown is caught, logged with its line number and stack, shown
in the status line, and surfaced in the report dialog.