playrig 0.0.0-stage → 0.1.0

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.
Files changed (118) hide show
  1. package/AGENTS.md +106 -0
  2. package/LICENSE +37 -0
  3. package/README.md +77 -2
  4. package/bin/playrig.js +4 -0
  5. package/editions/ae/README.md +144 -0
  6. package/editions/ae/client/aeb.js +9 -0
  7. package/editions/ae/examples/hello.jsx +3 -0
  8. package/editions/ae/examples/inspect.jsx +7 -0
  9. package/editions/ae/examples/snap.jsx +15 -0
  10. package/editions/ae/lib/actions.json +159 -0
  11. package/editions/ae/lib/bridge-api.jsxinc +474 -0
  12. package/editions/ae/lib/dev.jsxinc +21 -0
  13. package/editions/ae/lib/icons/README.md +4 -0
  14. package/editions/ae/lib/icons/dot-error.svg +3 -0
  15. package/editions/ae/lib/icons/dot-idle.svg +3 -0
  16. package/editions/ae/lib/icons/dot-running.svg +3 -0
  17. package/editions/ae/lib/icons/make-png.js +25 -0
  18. package/editions/ae/lib/icons/png/dot-error.png +0 -0
  19. package/editions/ae/lib/icons/png/dot-error@2x.png +0 -0
  20. package/editions/ae/lib/icons/png/dot-idle.png +0 -0
  21. package/editions/ae/lib/icons/png/dot-idle@2x.png +0 -0
  22. package/editions/ae/lib/icons/png/dot-running.png +0 -0
  23. package/editions/ae/lib/icons/png/dot-running@2x.png +0 -0
  24. package/editions/ae/lib/icons/png/power-off.png +0 -0
  25. package/editions/ae/lib/icons/png/power-off@2x.png +0 -0
  26. package/editions/ae/lib/icons/png/power-on.png +0 -0
  27. package/editions/ae/lib/icons/png/power-on@2x.png +0 -0
  28. package/editions/ae/lib/icons/png/settings.png +0 -0
  29. package/editions/ae/lib/icons/png/settings@2x.png +0 -0
  30. package/editions/ae/lib/icons/png/status-error.png +0 -0
  31. package/editions/ae/lib/icons/png/status-error@2x.png +0 -0
  32. package/editions/ae/lib/icons/png/status-ok.png +0 -0
  33. package/editions/ae/lib/icons/png/status-ok@2x.png +0 -0
  34. package/editions/ae/lib/icons/png/status-rejected.png +0 -0
  35. package/editions/ae/lib/icons/png/status-rejected@2x.png +0 -0
  36. package/editions/ae/lib/icons/png/status-timeout.png +0 -0
  37. package/editions/ae/lib/icons/png/status-timeout@2x.png +0 -0
  38. package/editions/ae/lib/icons/png/status-unknown.png +0 -0
  39. package/editions/ae/lib/icons/png/status-unknown@2x.png +0 -0
  40. package/editions/ae/lib/icons/power-off.svg +4 -0
  41. package/editions/ae/lib/icons/power-on.svg +4 -0
  42. package/editions/ae/lib/icons/settings.svg +4 -0
  43. package/editions/ae/lib/icons/status-error.svg +4 -0
  44. package/editions/ae/lib/icons/status-ok.svg +4 -0
  45. package/editions/ae/lib/icons/status-rejected.svg +4 -0
  46. package/editions/ae/lib/icons/status-timeout.svg +4 -0
  47. package/editions/ae/lib/icons/status-unknown.svg +5 -0
  48. package/editions/ae/lib/panel-core.jsxinc +249 -0
  49. package/editions/ae/lib/update-key.pem +11 -0
  50. package/editions/ae/lib/updater.jsxinc +148 -0
  51. package/editions/ae/library-starter/INDEX.md +5 -0
  52. package/editions/ae/library-starter/README.md +161 -0
  53. package/editions/ae/library-starter/_common.jsx +187 -0
  54. package/editions/ae/library-starter/categories.json +9 -0
  55. package/editions/ae/library-starter/recipes/.gitkeep +0 -0
  56. package/editions/ae/package.json +7 -0
  57. package/editions/ae/panel/Playrig.jsx +39 -0
  58. package/editions/ae/panel/core.jsx +1026 -0
  59. package/editions/ae/skills/create-ae-recipe/SKILL.md +85 -0
  60. package/editions/ae/skills/create-ae-recipe/references/ae-patterns.md +88 -0
  61. package/editions/ae/skills/create-ae-recipe/references/video-analysis.md +72 -0
  62. package/editions/ae/skills/create-ae-recipe/scripts/media +5 -0
  63. package/editions/ae/skills/create-ae-recipe/scripts/media.py +253 -0
  64. package/editions/ae/skills/create-ae-recipe/scripts/setup.sh +17 -0
  65. package/editions/ae/skills/create-ae-video/SKILL.md +88 -0
  66. package/editions/ae/skills/create-ae-video/references/templates.md +90 -0
  67. package/editions/ae/skills/create-ae-video/scripts/preflight.js +99 -0
  68. package/editions/ae/skills/use-ae-recipes/SKILL.md +99 -0
  69. package/editions/ae/skills/use-ae-recipes/scripts/contact-sheet.jsx +10 -0
  70. package/editions/ae/skills/use-ae-recipes/scripts/edit-text.jsx +12 -0
  71. package/editions/ae/skills/use-ae-recipes/scripts/remove-comp.jsx +18 -0
  72. package/editions/pr/README.md +60 -0
  73. package/editions/pr/SCORE_PROCESS.md +41 -0
  74. package/editions/pr/client/aeb.js +9 -0
  75. package/editions/pr/examples/frame.js +6 -0
  76. package/editions/pr/examples/hello.js +4 -0
  77. package/editions/pr/organize.default.json +245 -0
  78. package/editions/pr/package.json +7 -0
  79. package/editions/pr/plugin/actions.js +79 -0
  80. package/editions/pr/plugin/dev.js +19 -0
  81. package/editions/pr/plugin/icons/README.md +4 -0
  82. package/editions/pr/plugin/icons/file-code.svg +6 -0
  83. package/editions/pr/plugin/icons/folder.svg +3 -0
  84. package/editions/pr/plugin/icons/power-off.svg +4 -0
  85. package/editions/pr/plugin/icons/power-on.svg +4 -0
  86. package/editions/pr/plugin/icons/settings.svg +4 -0
  87. package/editions/pr/plugin/icons/status-error.svg +4 -0
  88. package/editions/pr/plugin/icons/status-ok.svg +4 -0
  89. package/editions/pr/plugin/icons/status-rejected.svg +4 -0
  90. package/editions/pr/plugin/icons/status-timeout.svg +4 -0
  91. package/editions/pr/plugin/icons/status-unknown.svg +5 -0
  92. package/editions/pr/plugin/incremental.js +212 -0
  93. package/editions/pr/plugin/index.html +195 -0
  94. package/editions/pr/plugin/index.js +710 -0
  95. package/editions/pr/plugin/manifest.json +45 -0
  96. package/editions/pr/plugin/organize.js +223 -0
  97. package/editions/pr/skills/score-video/SKILL.md +69 -0
  98. package/editions/pr/skills/score-video/scripts/mix.py +112 -0
  99. package/editions/pr/skills/score-video/scripts/place.js +63 -0
  100. package/lib/ae.js +754 -0
  101. package/lib/common.js +132 -0
  102. package/lib/install.js +148 -0
  103. package/lib/main.js +107 -0
  104. package/lib/payload.js +60 -0
  105. package/lib/pr.js +239 -0
  106. package/lib/update.js +118 -0
  107. package/package.json +35 -4
  108. package/skills/create-video/SKILL.md +72 -0
  109. package/skills/create-video/references/lessons.md +31 -0
  110. package/skills/create-video/references/project-conventions.md +46 -0
  111. package/skills/create-video/scripts/ae/list-project.jsx +8 -0
  112. package/skills/create-video/scripts/ae/move-chips.jsx +14 -0
  113. package/skills/create-video/scripts/ae/organize-project.jsx +25 -0
  114. package/skills/create-video/scripts/ae/quadrant-chips.jsx +143 -0
  115. package/skills/create-video/scripts/ae/render-comps.jsx +16 -0
  116. package/skills/create-video/scripts/premiere/export-sequences.js +22 -0
  117. package/skills/create-video/scripts/premiere/make-sequences.js +24 -0
  118. package/skills/create-video/scripts/premiere/swap-media.js +43 -0
@@ -0,0 +1,474 @@
1
+ // Playrig helper API (the job global is PLAYRIG; BRIDGE is kept as an alias so older job scripts still run). Evaluated by the panel before every job.
2
+ // ES3 only: no let/const, arrow functions, JSON, Array.prototype.map/forEach/indexOf.
3
+ (function () {
4
+ var G = $.global;
5
+ var ctx = G.__PLAYRIG_CTX || {};
6
+ var B = {};
7
+ var logLines = [];
8
+ var snapQueue = [];
9
+ var retVal = null;
10
+ var MAX_SNAPS = 500;
11
+
12
+ // ---------------------------------------------------------------- utils
13
+ function isArray(v) { return Object.prototype.toString.call(v) === "[object Array]"; }
14
+ function safe(fn) { try { return fn(); } catch (e) { return null; } }
15
+ function pad(n, w) { var s = String(n); while (s.length < w) { s = "0" + s; } return s; }
16
+ function safeName(s) { return String(s).replace(/[^A-Za-z0-9_\-]+/g, "_"); }
17
+
18
+ function isoDate(d) {
19
+ var off = -d.getTimezoneOffset();
20
+ var sign = off < 0 ? "-" : "+";
21
+ off = Math.abs(off);
22
+ return d.getFullYear() + "-" + pad(d.getMonth() + 1, 2) + "-" + pad(d.getDate(), 2) + "T" +
23
+ pad(d.getHours(), 2) + ":" + pad(d.getMinutes(), 2) + ":" + pad(d.getSeconds(), 2) +
24
+ sign + pad(Math.floor(off / 60), 2) + ":" + pad(off % 60, 2);
25
+ }
26
+
27
+ // ---------------------------------------------------------------- JSON
28
+ function quote(s) {
29
+ var out = '"', i, c, code, h;
30
+ for (i = 0; i < s.length; i++) {
31
+ c = s.charAt(i);
32
+ code = s.charCodeAt(i);
33
+ if (c === '"') { out += '\\"'; }
34
+ else if (c === "\\") { out += "\\\\"; }
35
+ else if (c === "\n") { out += "\\n"; }
36
+ else if (c === "\r") { out += "\\r"; }
37
+ else if (c === "\t") { out += "\\t"; }
38
+ else if (code < 32 || code > 126) {
39
+ h = code.toString(16);
40
+ while (h.length < 4) { h = "0" + h; }
41
+ out += "\\u" + h;
42
+ } else { out += c; }
43
+ }
44
+ return out + '"';
45
+ }
46
+
47
+ function ser(v, indent, cur, depth) {
48
+ var t = typeof v, parts, i, k, nl, nxt, s;
49
+ if (v === null || v === undefined) { return "null"; }
50
+ if (t === "number") { return isFinite(v) ? String(v) : "null"; }
51
+ if (t === "boolean") { return v ? "true" : "false"; }
52
+ if (t === "string") { return quote(v); }
53
+ if (t === "function") { return "null"; }
54
+ if (depth > 40) { return "null"; }
55
+ if (v instanceof Date) { return quote(isoDate(v)); }
56
+ if (v instanceof File || v instanceof Folder) { return quote(v.fsName); }
57
+ nl = indent ? "\n" : "";
58
+ nxt = indent ? cur + indent : "";
59
+ parts = [];
60
+ if (isArray(v)) {
61
+ for (i = 0; i < v.length; i++) { parts.push(nxt + ser(v[i], indent, nxt, depth + 1)); }
62
+ if (!parts.length) { return "[]"; }
63
+ return "[" + nl + parts.join("," + nl) + nl + cur + "]";
64
+ }
65
+ try {
66
+ for (k in v) {
67
+ if (!v.hasOwnProperty(k)) { continue; }
68
+ if (v[k] === undefined || typeof v[k] === "function") { continue; }
69
+ s = ser(v[k], indent, nxt, depth + 1);
70
+ parts.push(nxt + quote(k) + (indent ? ": " : ":") + s);
71
+ }
72
+ } catch (e) { return quote(String(v)); }
73
+ if (!parts.length) { return "{}"; }
74
+ return "{" + nl + parts.join("," + nl) + nl + cur + "}";
75
+ }
76
+
77
+ function stringify(v, indent) {
78
+ var ind = "";
79
+ if (typeof indent === "number") { while (ind.length < indent) { ind += " "; } }
80
+ return ser(v, ind, "", 0);
81
+ }
82
+
83
+ function parse(text) {
84
+ var i = 0, n;
85
+ text = String(text);
86
+ if (text.charCodeAt(0) === 0xFEFF) { text = text.substr(1); }
87
+ n = text.length;
88
+ function err(m) { throw new Error("JSON parse error at " + i + ": " + m); }
89
+ function ws() { while (i < n && " \t\r\n".indexOf(text.charAt(i)) >= 0) { i++; } }
90
+ function str() {
91
+ var out = "", c, e;
92
+ i++;
93
+ while (i < n) {
94
+ c = text.charAt(i++);
95
+ if (c === '"') { return out; }
96
+ if (c === "\\") {
97
+ e = text.charAt(i++);
98
+ if (e === "n") { out += "\n"; }
99
+ else if (e === "r") { out += "\r"; }
100
+ else if (e === "t") { out += "\t"; }
101
+ else if (e === "b") { out += "\b"; }
102
+ else if (e === "f") { out += "\f"; }
103
+ else if (e === "u") { out += String.fromCharCode(parseInt(text.substr(i, 4), 16)); i += 4; }
104
+ else { out += e; }
105
+ } else { out += c; }
106
+ }
107
+ err("unterminated string");
108
+ }
109
+ function num() {
110
+ var m = /^-?\d+(\.\d+)?([eE][+\-]?\d+)?/.exec(text.substr(i));
111
+ if (!m) { err("unexpected token"); }
112
+ i += m[0].length;
113
+ return parseFloat(m[0]);
114
+ }
115
+ function arr() {
116
+ var a = [];
117
+ i++; ws();
118
+ if (text.charAt(i) === "]") { i++; return a; }
119
+ while (i < n) {
120
+ a.push(val()); ws();
121
+ if (text.charAt(i) === ",") { i++; continue; }
122
+ if (text.charAt(i) === "]") { i++; return a; }
123
+ err("expected , or ]");
124
+ }
125
+ err("unterminated array");
126
+ }
127
+ function obj() {
128
+ var o = {}, k;
129
+ i++; ws();
130
+ if (text.charAt(i) === "}") { i++; return o; }
131
+ while (i < n) {
132
+ ws();
133
+ if (text.charAt(i) !== '"') { err("expected string key"); }
134
+ k = str(); ws();
135
+ if (text.charAt(i) !== ":") { err("expected :"); }
136
+ i++;
137
+ o[k] = val(); ws();
138
+ if (text.charAt(i) === ",") { i++; continue; }
139
+ if (text.charAt(i) === "}") { i++; return o; }
140
+ err("expected , or }");
141
+ }
142
+ err("unterminated object");
143
+ }
144
+ function val() {
145
+ var c;
146
+ ws();
147
+ c = text.charAt(i);
148
+ if (c === "{") { return obj(); }
149
+ if (c === "[") { return arr(); }
150
+ if (c === '"') { return str(); }
151
+ if (text.substr(i, 4) === "true") { i += 4; return true; }
152
+ if (text.substr(i, 5) === "false") { i += 5; return false; }
153
+ if (text.substr(i, 4) === "null") { i += 4; return null; }
154
+ return num();
155
+ }
156
+ var result = val();
157
+ ws();
158
+ if (i < n) { err("trailing characters"); }
159
+ return result;
160
+ }
161
+
162
+ // ---------------------------------------------------------------- basics
163
+ B.json = { stringify: stringify, parse: parse };
164
+ B.root = ctx.root ? new Folder(ctx.root) : null;
165
+ B.jobId = ctx.id || null;
166
+ B.outDir = ctx.outDir ? new Folder(ctx.outDir) : null;
167
+ B.isoDate = isoDate;
168
+ B.safeName = safeName;
169
+
170
+ B.log = function (msg) {
171
+ var s;
172
+ if (typeof msg === "string") { s = msg; }
173
+ else { try { s = stringify(msg); } catch (e) { s = String(msg); } }
174
+ logLines.push(s);
175
+ };
176
+
177
+ B.ret = function (value) { retVal = value; };
178
+
179
+ B.comp = function (name) {
180
+ var found = [], i, it;
181
+ for (i = 1; i <= app.project.numItems; i++) {
182
+ it = app.project.item(i);
183
+ if (it instanceof CompItem && it.name === name) { found.push(it); }
184
+ }
185
+ if (found.length === 0) { throw new Error("PLAYRIG.comp: no comp named \"" + name + "\""); }
186
+ if (found.length > 1) { throw new Error("PLAYRIG.comp: " + found.length + " comps are named \"" + name + "\" (names must be unique)"); }
187
+ return found[0];
188
+ };
189
+
190
+ B.snap = function (compName, time) {
191
+ if (snapQueue.length >= MAX_SNAPS) { throw new Error("PLAYRIG.snap: snapshot limit (" + MAX_SNAPS + ") reached"); }
192
+ snapQueue.push({ comp: compName, time: Number(time) || 0 });
193
+ };
194
+
195
+ B.contactSheet = function (compName, start, end, step) {
196
+ var t, n = 0;
197
+ start = Number(start); end = Number(end); step = Number(step);
198
+ if (!(step > 0)) { throw new Error("PLAYRIG.contactSheet: step must be > 0"); }
199
+ for (t = start; t <= end + 1e-9; t += step) {
200
+ n++;
201
+ if (snapQueue.length >= MAX_SNAPS) { throw new Error("PLAYRIG.contactSheet: snapshot limit (" + MAX_SNAPS + ") reached"); }
202
+ snapQueue.push({ comp: compName, time: t, name: safeName(compName) + "_sheet_" + pad(n, 3) + "_" + t.toFixed(3) + ".png" });
203
+ }
204
+ };
205
+
206
+ // ---------------------------------------------------------------- snapshots
207
+ function waitForFile(f, maxMs) {
208
+ var waited = 0;
209
+ while (waited <= maxMs) {
210
+ if (f.exists && f.length > 0) { return true; }
211
+ $.sleep(100);
212
+ waited += 100;
213
+ }
214
+ return f.exists && f.length > 0;
215
+ }
216
+
217
+ // Takes every queued/requested snapshot. Returns the result.snapshots array.
218
+ B._takeSnapshots = function (list, outDir) {
219
+ var res = [], i, s, r, comp, name, f;
220
+ for (i = 0; i < list.length; i++) {
221
+ s = list[i];
222
+ r = { comp: s.comp, time: s.time, file: null, ok: false };
223
+ try {
224
+ comp = B.comp(s.comp);
225
+ name = s.name || (safeName(s.comp) + "_" + Number(s.time).toFixed(3) + ".png");
226
+ f = new File(outDir.fsName + "/" + name);
227
+ if (f.exists) { f.remove(); }
228
+ comp.saveFrameToPng(Number(s.time), f);
229
+ r.file = name;
230
+ r.ok = waitForFile(f, 10000);
231
+ if (!r.ok) { r.error = "PNG not written within 10 s"; }
232
+ } catch (e) {
233
+ r.error = e.message;
234
+ }
235
+ res.push(r);
236
+ }
237
+ return res;
238
+ };
239
+
240
+ // ---------------------------------------------------------------- inspection
241
+ var TRANSFORMS = [
242
+ ["anchorPoint", "ADBE Anchor Point"],
243
+ ["position", "ADBE Position"],
244
+ ["scale", "ADBE Scale"],
245
+ ["rotation", "ADBE Rotate Z"],
246
+ ["opacity", "ADBE Opacity"]
247
+ ];
248
+ var TRANSFORMS_3D = [
249
+ ["orientation", "ADBE Orientation"],
250
+ ["rotationX", "ADBE Rotate X"],
251
+ ["rotationY", "ADBE Rotate Y"]
252
+ ];
253
+
254
+ function enumName(enumObj, value) {
255
+ var k, props, i;
256
+ try { for (k in enumObj) { if (enumObj[k] === value) { return k; } } } catch (e) { }
257
+ try {
258
+ props = enumObj.reflect.properties;
259
+ for (i = 0; i < props.length; i++) { if (enumObj[props[i].name] === value) { return props[i].name; } }
260
+ } catch (e2) { }
261
+ return value;
262
+ }
263
+
264
+ function propInfo(prop, time) {
265
+ var o = {}, i, keys;
266
+ try { o.value = prop.valueAtTime(time, false); }
267
+ catch (e) { try { o.value = prop.value; } catch (e2) { o.value = null; } }
268
+ o.numKeys = prop.numKeys;
269
+ if (prop.numKeys > 0) {
270
+ keys = [];
271
+ for (i = 1; i <= prop.numKeys; i++) { keys.push(prop.keyTime(i)); }
272
+ o.keyTimes = keys;
273
+ }
274
+ if (prop.canSetExpression && prop.expressionEnabled) { o.expression = prop.expression; }
275
+ return o;
276
+ }
277
+
278
+ function layerType(layer) {
279
+ if (layer instanceof CameraLayer) { return "camera"; }
280
+ if (layer instanceof LightLayer) { return "light"; }
281
+ if (layer instanceof ShapeLayer) { return "shape"; }
282
+ if (layer instanceof TextLayer) { return "text"; }
283
+ if (layer.adjustmentLayer) { return "adjustment"; }
284
+ if (layer.nullLayer) { return "null"; }
285
+ if (layer.source instanceof CompItem) { return "precomp"; }
286
+ if (layer.source && layer.source.mainSource instanceof SolidSource) { return "solid"; }
287
+ return "av";
288
+ }
289
+
290
+ function transformInfo(layer, time, is3D) {
291
+ var out = {}, grp, i, def, p, list = TRANSFORMS, j;
292
+ grp = layer.property("ADBE Transform Group");
293
+ if (!grp) { return out; }
294
+ if (is3D) { list = TRANSFORMS.concat(TRANSFORMS_3D); }
295
+ for (i = 0; i < list.length; i++) {
296
+ def = list[i];
297
+ p = grp.property(def[1]);
298
+ if (!p) { continue; }
299
+ if (def[1] === "ADBE Position" && p.dimensionsSeparated) {
300
+ out.positionSeparated = {};
301
+ for (j = 0; j < (is3D ? 3 : 2); j++) {
302
+ var sp = grp.property("ADBE Position_" + j);
303
+ if (sp) { out.positionSeparated["ADBE Position_" + j] = propInfo(sp, time); }
304
+ }
305
+ continue;
306
+ }
307
+ out[def[0]] = propInfo(p, time);
308
+ }
309
+ return out;
310
+ }
311
+
312
+ function textInfo(layer, time) {
313
+ var o = {}, tp, doc, an, i, j, a, sels, selList;
314
+ tp = layer.property("ADBE Text Properties");
315
+ if (!tp) { return o; }
316
+ doc = tp.property("ADBE Text Document").valueAtTime(time, false);
317
+ o.text = doc.text;
318
+ o.font = doc.font;
319
+ o.fontFamily = safe(function () { return doc.fontFamily; });
320
+ o.fontStyle = safe(function () { return doc.fontStyle; });
321
+ o.fontSize = doc.fontSize;
322
+ o.animators = [];
323
+ an = tp.property("ADBE Text Animators");
324
+ if (an) {
325
+ for (i = 1; i <= an.numProperties; i++) {
326
+ a = an.property(i);
327
+ sels = a.property("ADBE Text Selectors");
328
+ selList = [];
329
+ if (sels) { for (j = 1; j <= sels.numProperties; j++) { selList.push({ name: sels.property(j).name, matchName: sels.property(j).matchName }); } }
330
+ o.animators.push({ name: a.name, selectors: selList });
331
+ }
332
+ }
333
+ return o;
334
+ }
335
+
336
+ function shapeInfo(layer) {
337
+ // Listed top to bottom: the FIRST group draws on top.
338
+ var out = [], root, i, g, items, c, k;
339
+ root = layer.property("ADBE Root Vectors Group");
340
+ if (!root) { return out; }
341
+ for (i = 1; i <= root.numProperties; i++) {
342
+ g = root.property(i);
343
+ items = [];
344
+ if (g.matchName === "ADBE Vector Group") {
345
+ c = g.property("ADBE Vectors Group");
346
+ if (c) { for (k = 1; k <= c.numProperties; k++) { items.push(c.property(k).matchName); } }
347
+ }
348
+ out.push({ index: i, name: g.name, matchName: g.matchName, items: items });
349
+ }
350
+ return out;
351
+ }
352
+
353
+ function effectsInfo(layer) {
354
+ var out = [], fx, i;
355
+ fx = layer.property("ADBE Effect Parade");
356
+ if (!fx) { return out; }
357
+ for (i = 1; i <= fx.numProperties; i++) { out.push({ name: fx.property(i).name, matchName: fx.property(i).matchName }); }
358
+ return out;
359
+ }
360
+
361
+ function layerInfo(layer, time, depth) {
362
+ var o, type, is3D, childTime, src;
363
+ type = layerType(layer);
364
+ o = { index: layer.index, name: layer.name, type: type };
365
+ o.enabled = layer.enabled;
366
+ o.solo = layer.solo;
367
+ o.shy = layer.shy;
368
+ o.inPoint = layer.inPoint;
369
+ o.outPoint = layer.outPoint;
370
+ o.startTime = layer.startTime;
371
+ o.parent = layer.parent ? layer.parent.name : null;
372
+ is3D = !!safe(function () { return layer.threeDLayer; });
373
+ o.threeDLayer = is3D;
374
+ if (layer instanceof AVLayer) {
375
+ o.blendingMode = enumName(BlendingMode, layer.blendingMode);
376
+ o.motionBlur = layer.motionBlur;
377
+ o.stretch = layer.stretch;
378
+ if (layer.source) { o.source = layer.source.name; }
379
+ if (layer.hasTrackMatte) {
380
+ o.trackMatte = { type: enumName(TrackMatteType, layer.trackMatteType),
381
+ layer: safe(function () { return layer.trackMatteLayer ? layer.trackMatteLayer.name : null; }) };
382
+ } else { o.trackMatte = null; }
383
+ }
384
+ try { o.transform = transformInfo(layer, time, is3D); } catch (e1) { o.transform = { error: e1.message }; }
385
+ try { o.effects = effectsInfo(layer); } catch (e2) { o.effects = { error: e2.message }; }
386
+ if (type === "text") { try { o.textInfo = textInfo(layer, time); } catch (e3) { o.textInfo = { error: e3.message }; } }
387
+ if (type === "shape") { try { o.shapeGroups = shapeInfo(layer); } catch (e4) { o.shapeGroups = { error: e4.message }; } }
388
+ if (type === "precomp" && depth > 0) {
389
+ try {
390
+ childTime = (time - layer.startTime) * (layer.stretch ? 100 / layer.stretch : 1);
391
+ o.precomp = inspectComp(layer.source, depth - 1, childTime);
392
+ } catch (e5) { o.precomp = { error: e5.message }; }
393
+ }
394
+ return o;
395
+ }
396
+
397
+ function inspectComp(comp, depth, time) {
398
+ var o, i;
399
+ o = {
400
+ name: comp.name,
401
+ width: comp.width,
402
+ height: comp.height,
403
+ pixelAspect: comp.pixelAspect,
404
+ fps: comp.frameRate,
405
+ duration: comp.duration,
406
+ bgColor: comp.bgColor,
407
+ numLayers: comp.numLayers,
408
+ renderer: safe(function () { return comp.renderer; }),
409
+ time: time,
410
+ layers: []
411
+ };
412
+ for (i = 1; i <= comp.numLayers; i++) { o.layers.push(layerInfo(comp.layer(i), time, depth)); }
413
+ return o;
414
+ }
415
+
416
+ // PLAYRIG.inspect(compName, depth, time): depth 0 = this comp only, N = recurse into precomps N levels.
417
+ B.inspect = function (compName, depth, time) {
418
+ var d = (depth === undefined || depth === null) ? 1 : Number(depth);
419
+ return inspectComp(B.comp(compName), d, Number(time) || 0);
420
+ };
421
+
422
+ // ---------------------------------------------------------------- asset manifest
423
+ // Describes what the open project contains so a person (or Claude) on another machine can see what exists.
424
+ // Paths inside projectDir are reported relative to it; anything else is flagged external.
425
+ function relPath(fsName, baseDir) {
426
+ var a = String(fsName).replace(/\\/g, "/"), b = String(baseDir).replace(/\\/g, "/");
427
+ if (b.charAt(b.length - 1) !== "/") { b += "/"; }
428
+ if (a.toLowerCase().indexOf(b.toLowerCase()) === 0) { return { path: a.substr(b.length), external: false }; }
429
+ return { path: a, external: true };
430
+ }
431
+
432
+ B._assets = function (projectDir) {
433
+ var items = [], comps = [], fonts = {}, fontList = [], i, j, it, o, src, f, layer, k, font;
434
+ for (i = 1; i <= app.project.numItems; i++) {
435
+ it = app.project.item(i);
436
+ o = { name: it.name, folder: safe(function () { return it.parentFolder && it.parentFolder !== app.project.rootFolder ? it.parentFolder.name : null; }) };
437
+ if (it instanceof CompItem) {
438
+ o.type = "comp"; o.width = it.width; o.height = it.height; o.fps = it.frameRate; o.duration = it.duration; o.numLayers = it.numLayers;
439
+ comps.push(o);
440
+ for (j = 1; j <= it.numLayers && j <= 500; j++) {
441
+ layer = it.layer(j);
442
+ if (layer instanceof TextLayer) {
443
+ font = safe(function () { return layer.property("ADBE Text Properties").property("ADBE Text Document").value.font; });
444
+ if (font && !fonts[font]) { fonts[font] = true; fontList.push(font); }
445
+ }
446
+ }
447
+ } else if (it instanceof FolderItem) {
448
+ continue;
449
+ } else {
450
+ src = safe(function () { return it.mainSource; });
451
+ o.type = (src && src instanceof SolidSource) ? "solid" : "footage";
452
+ o.width = safe(function () { return it.width; });
453
+ o.height = safe(function () { return it.height; });
454
+ o.duration = safe(function () { return it.duration; });
455
+ f = safe(function () { return it.file; });
456
+ if (f) {
457
+ k = relPath(f.fsName, projectDir);
458
+ o.file = k.path; o.external = k.external; o.missing = !f.exists;
459
+ }
460
+ items.push(o);
461
+ }
462
+ }
463
+ return { generatedAt: isoDate(new Date()), footage: items, comps: comps, fonts: fontList };
464
+ };
465
+
466
+ // ---------------------------------------------------------------- panel hooks
467
+ B._log = function () { return logLines; };
468
+ B._snapQueue = function () { return snapQueue; };
469
+ B._ret = function () { return retVal; };
470
+
471
+ G.PLAYRIG = B;
472
+ G.PLAYPILOT = B; // legacy alias (former name)
473
+ G.BRIDGE = B; // legacy alias
474
+ })();
@@ -0,0 +1,21 @@
1
+ // DEV ONLY. Developer-facing extras for the Playrig for After Effects panel. This whole file is left out of packaged builds: the panel loads it only
2
+ // if it exists (File.exists + $.evalFile) and only uses $.global.PlayrigDev when it is defined, so removing the file removes these features
3
+ // without any other change. Currently: the "Job script" section of the job details window (the script as it was run, for debugging jobs).
4
+ $.global.PlayrigDev = {
5
+ // Called with { logDir: Folder|null, id: String, readText: fn(File) }; returns extra detail sections [{ title, text, multi }].
6
+ jobDetail: function (ctx) {
7
+ var out = [], f, src, lines;
8
+ if (!ctx.logDir) { return out; }
9
+ f = new File(ctx.logDir.fsName + "/jobs/" + ctx.id + ".jsx");
10
+ if (f.exists) {
11
+ try {
12
+ src = ctx.readText(f);
13
+ lines = src.split(/\r\n|\r|\n/);
14
+ out.push({ title: "Job script (dev only, " + lines.length + " lines)", multi: true,
15
+ text: lines.slice(0, 60).join("\n") + (lines.length > 60 ? "\n... " + (lines.length - 60) + " more lines in jobs/" + ctx.id + ".jsx" : "") });
16
+ } catch (e) { }
17
+ }
18
+ out.push({ title: "Script file (dev only)", text: f.fsName });
19
+ return out;
20
+ }
21
+ };
@@ -0,0 +1,4 @@
1
+ Icons are from Tabler Icons (https://tabler.io/icons), MIT License, (c) 2020-2024 Pawel Kuna. Outline set v3.34.1, stroke width 1.
2
+ The `.svg` files are the sources (same per-path attribute convention as premiere-bridge/plugin/icons). ScriptUI cannot draw SVG, so the panel loads the generated `png/<name>.png` (16 px) files; `png/<name>@2x.png` (32 px) are generated for a future hi-dpi path.
3
+ Regenerate after editing an SVG: `node make-png.js` (in this folder) (macOS only: uses a small Swift script that renders the SVG with AppKit).
4
+ The `dot-*` files are plain filled circles used for the status dot.
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" fill="#f85149" stroke="#f85149" stroke-width="1"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" fill="#8c8c8c" stroke="#8c8c8c" stroke-width="1"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M12 12m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" fill="#3fb950" stroke="#3fb950" stroke-width="1"/>
3
+ </svg>
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // Renders every icons/*.svg to png/<name>.png (16 px) and png/<name>@2x.png (32 px). macOS only (Swift + AppKit).
4
+ const fs = require('fs'), path = require('path'), cp = require('child_process'), os = require('os');
5
+ const dir = __dirname, out = path.join(dir, 'png');
6
+ fs.mkdirSync(out, { recursive: true });
7
+ const swiftSrc = path.join(os.tmpdir(), 'aeb-svg2png.swift'), bin = path.join(os.tmpdir(), 'aeb-svg2png');
8
+ fs.writeFileSync(swiftSrc, `import AppKit
9
+ let a = CommandLine.arguments
10
+ guard let img = NSImage(contentsOfFile: a[1]) else { print("cannot load " + a[1]); exit(1) }
11
+ let px = Int(a[3])!
12
+ let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: px, pixelsHigh: px, bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0)!
13
+ NSGraphicsContext.saveGraphicsState()
14
+ NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep)
15
+ img.draw(in: NSRect(x: 0, y: 0, width: px, height: px))
16
+ NSGraphicsContext.restoreGraphicsState()
17
+ try! rep.representation(using: .png, properties: [:])!.write(to: URL(fileURLWithPath: a[2]))
18
+ `);
19
+ cp.execFileSync('swiftc', ['-O', swiftSrc, '-o', bin], { stdio: 'inherit' });
20
+ for (const f of fs.readdirSync(dir).filter((x) => x.endsWith('.svg'))) {
21
+ const n = f.replace(/\.svg$/, '');
22
+ cp.execFileSync(bin, [path.join(dir, f), path.join(out, n + '.png'), '16']);
23
+ cp.execFileSync(bin, [path.join(dir, f), path.join(out, n + '@2x.png'), '32']);
24
+ }
25
+ console.log('wrote', fs.readdirSync(out).length, 'files to', out);
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M7 6a7.75 7.75 0 1 0 10 0" fill="none" stroke="#8c8c8c" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M12 4l0 8" fill="none" stroke="#8c8c8c" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M7 6a7.75 7.75 0 1 0 10 0" fill="none" stroke="#3fb950" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M12 4l0 8" fill="none" stroke="#3fb950" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" fill="none" stroke="#c8c8c8" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" fill="none" stroke="#c8c8c8" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" fill="none" stroke="#f85149" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M10 10l4 4m0 -4l-4 4" fill="none" stroke="#f85149" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" fill="none" stroke="#3fb950" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M9 12l2 2l4 -4" fill="none" stroke="#3fb950" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" fill="none" stroke="#f0883e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M5.7 5.7l12.6 12.6" fill="none" stroke="#f0883e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" fill="none" stroke="#e3b341" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M12 7v5l3 3" fill="none" stroke="#e3b341" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
2
+ <path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" fill="none" stroke="#8c8c8c" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M12 16v.01" fill="none" stroke="#8c8c8c" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
4
+ <path d="M12 13a2 2 0 0 0 .914 -3.782a1.98 1.98 0 0 0 -2.414 .483" fill="none" stroke="#8c8c8c" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
5
+ </svg>