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,187 @@
1
+ // Shared helpers appended to every recipe by `playrig ae lib run`. ES3 only.
2
+ // Function declarations are hoisted, so recipes can call these from their first line,
3
+ // and appending (not prepending) keeps recipe line numbers intact in error reports.
4
+
5
+ // "#RRGGBB" or "#RGB" -> [r, g, b] in 0..1
6
+ function libHex(h) {
7
+ var s = String(h).replace("#", ""), n;
8
+ if (s.length === 3) { s = s.charAt(0) + s.charAt(0) + s.charAt(1) + s.charAt(1) + s.charAt(2) + s.charAt(2); }
9
+ n = parseInt(s, 16);
10
+ if (isNaN(n) || s.length !== 6) { throw new Error("Bad colour \"" + h + "\" (use #RRGGBB)"); }
11
+ return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];
12
+ }
13
+
14
+ // Find the comp named `name` (error if several); create it if missing.
15
+ function libComp(name, w, h, dur, fps) {
16
+ var found = [], i, it;
17
+ for (i = 1; i <= app.project.numItems; i++) {
18
+ it = app.project.item(i);
19
+ if (it instanceof CompItem && it.name === name) { found.push(it); }
20
+ }
21
+ if (found.length > 1) { throw new Error("Several comps are named \"" + name + "\""); }
22
+ if (found.length === 1) { return found[0]; }
23
+ return app.project.items.addComp(name, w, h, 1, dur, fps);
24
+ }
25
+
26
+ // First layer in `comp` called `name`, or null.
27
+ function libLayer(comp, name) {
28
+ var i;
29
+ for (i = 1; i <= comp.numLayers; i++) { if (comp.layer(i).name === name) { return comp.layer(i); } }
30
+ return null;
31
+ }
32
+
33
+ function libRequireLayer(comp, name) {
34
+ var l = libLayer(comp, name);
35
+ if (!l) { throw new Error("No layer named \"" + name + "\" in comp \"" + comp.name + "\""); }
36
+ return l;
37
+ }
38
+
39
+ function libSolid(comp, name, hex, w, h) {
40
+ return comp.layers.addSolid(libHex(hex), name, w || comp.width, h || comp.height, 1, comp.duration);
41
+ }
42
+
43
+ // Number of temporal-ease objects a property needs (one per dimension; spatial props take one).
44
+ function libEaseDims(prop) {
45
+ var t = prop.propertyValueType;
46
+ if (t === PropertyValueType.ThreeD) { return 3; }
47
+ if (t === PropertyValueType.TwoD) { return 2; }
48
+ return 1;
49
+ }
50
+
51
+ // Set temporal ease (influence %) on key `k`; speed 0 = classic ease. Pass inInfl/outInfl 0 to skip a side.
52
+ function libEaseKey(prop, k, inInfl, outInfl) {
53
+ var d = libEaseDims(prop), a = [], b = [], i;
54
+ for (i = 0; i < d; i++) { a.push(new KeyframeEase(0, Math.max(0.1, inInfl || 16.667))); b.push(new KeyframeEase(0, Math.max(0.1, outInfl || 16.667))); }
55
+ prop.setTemporalEaseAtKey(k, a, b);
56
+ }
57
+
58
+ // Add a keyframe and ease it. Influence 16.7 ~ linear feel, 33 = easy ease, 60-85 = strong ease.
59
+ function libKey(prop, time, value, inInfl, outInfl) {
60
+ prop.setValueAtTime(time, value);
61
+ var k = prop.nearestKeyIndex(time);
62
+ libEaseKey(prop, k, inInfl, outInfl);
63
+ return k;
64
+ }
65
+
66
+ // Frames -> seconds for a comp.
67
+ function libFrames(comp, n) { return n / comp.frameRate; }
68
+
69
+ // Split a "a|b|c" list param (or accept an array).
70
+ function libList(v) {
71
+ var parts, out = [], i, s;
72
+ if (v instanceof Array) { return v; }
73
+ parts = String(v).split("|");
74
+ for (i = 0; i < parts.length; i++) { s = parts[i].replace(/^\s+|\s+$/g, ""); if (s.length) { out.push(s); } }
75
+ return out;
76
+ }
77
+
78
+ // Make sure the target comp exists with the recipe's size/duration/fps, then return it.
79
+ // P.comp is the target; if it already exists it is used as is (so recipes can be layered into one comp).
80
+ function libTargetComp() {
81
+ return libComp(P.comp, P.width || 1920, P.height || 1080, P.duration || 5, P.fps || 24);
82
+ }
83
+
84
+ // Add a text layer (point text) and style it; returns { layer, doc, prop }.
85
+ function libText(comp, text, font, size, hex, tracking, justification) {
86
+ var layer = comp.layers.addText(text), prop = layer.property("ADBE Text Properties").property("ADBE Text Document"), doc = prop.value;
87
+ doc.resetCharStyle();
88
+ doc.fontSize = size;
89
+ doc.font = font;
90
+ doc.applyFill = true;
91
+ doc.fillColor = libHex(hex);
92
+ doc.applyStroke = false;
93
+ doc.tracking = tracking || 0;
94
+ doc.justification = justification || ParagraphJustification.CENTER_JUSTIFY;
95
+ prop.setValue(doc);
96
+ return { layer: layer, prop: prop, doc: doc };
97
+ }
98
+
99
+ // Add a text animator driven by an Expression Selector.
100
+ // basedOn: 1 = characters, 2 = characters excluding spaces, 3 = words, 4 = lines
101
+ // expr: expression for the selector Amount (must return [v, v, v], 0..100)
102
+ // props: { "ADBE Text Position 3D": [x, y, z], "ADBE Text Opacity": 0, ... } animator properties to add
103
+ function libTextAnimator(layer, name, basedOn, expr, props) {
104
+ var a = layer.property("ADBE Text Properties").property("ADBE Text Animators").addProperty("ADBE Text Animator"), sels, sel, k;
105
+ a.name = name;
106
+ sels = a.property("ADBE Text Selectors");
107
+ while (sels.numProperties > 0) { sels.property(1).remove(); } // scripted animators may start with a default Range Selector
108
+ sel = sels.addProperty("ADBE Text Expressible Selector");
109
+ sel.property("ADBE Text Range Type2").setValue(basedOn);
110
+ sel.property("ADBE Text Expressible Amount").expression = expr;
111
+ for (k in props) { if (props.hasOwnProperty(k)) { a.property("ADBE Text Animator Properties").addProperty(k).setValue(props[k]); } }
112
+ return a;
113
+ }
114
+
115
+ // Bold (or otherwise re-font) the first occurrence of `word` inside a text layer. Returns true if applied.
116
+ function libStyleWord(textProp, word, font) {
117
+ var doc = textProp.value, i = word ? doc.text.indexOf(word) : -1, r;
118
+ if (i < 0) { return false; }
119
+ r = doc.characterRange(i, i + word.length);
120
+ r.font = font; // r is a live view into doc
121
+ textProp.setValue(doc);
122
+ return true;
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------------------------------------------
126
+ // Recipe instances: every layer a recipe creates in its target comp is tagged (layer.comment = "aeb:<recipe>:<instance>")
127
+ // so the same recipe can later replace ONLY its own layers in place (playrig ae lib run <id> --replace), keeping stacking order.
128
+ // __AEB is injected by the CLI: { recipe, instance, replace }. __aebPre runs before the recipe, __aebPost after it.
129
+ function __aebFindComp() {
130
+ var i, it;
131
+ for (i = 1; i <= app.project.numItems; i++) {
132
+ it = app.project.item(i);
133
+ if (it instanceof CompItem && it.name === P.comp) { return it; }
134
+ }
135
+ return null;
136
+ }
137
+
138
+ function __aebCount(comp, tag) {
139
+ var i, n = 0;
140
+ for (i = 1; i <= comp.numLayers; i++) { if (comp.layer(i).comment === tag) { n++; } }
141
+ return n;
142
+ }
143
+
144
+ function __aebPre() {
145
+ var A = __AEB, comp = __aebFindComp(), base = A.instance, i, l, victims = [], dependents = 0, top = 0, j, isVictim, n;
146
+ A.before = {};
147
+ A.above = null;
148
+ A.removed = 0;
149
+ if (!comp) { A.tag = "aeb:" + A.recipe + ":" + base; return; }
150
+ if (A.replace) {
151
+ A.tag = "aeb:" + A.recipe + ":" + base;
152
+ for (i = 1; i <= comp.numLayers; i++) {
153
+ if (comp.layer(i).comment === A.tag) { victims.push(comp.layer(i)); if (!top) { top = i; } }
154
+ }
155
+ if (!victims.length) { PLAYRIG.log("--replace: no layers from " + A.recipe + " (instance \"" + base + "\") in " + comp.name + "; building fresh"); }
156
+ else {
157
+ if (top > 1) { A.above = comp.layer(top - 1); }
158
+ for (i = 1; i <= comp.numLayers; i++) { // layers from OTHER recipes parented to a victim will lose their parent
159
+ l = comp.layer(i); isVictim = false;
160
+ for (j = 0; j < victims.length; j++) { if (l.index === victims[j].index) { isVictim = true; } }
161
+ if (isVictim || !l.parent) { continue; }
162
+ for (j = 0; j < victims.length; j++) { if (l.parent.index === victims[j].index) { dependents++; break; } }
163
+ }
164
+ for (i = victims.length - 1; i >= 0; i--) { victims[i].remove(); }
165
+ A.removed = victims.length;
166
+ if (dependents) { PLAYRIG.log("note: " + dependents + " layer(s) from other recipes were parented to the replaced layers and are now unparented. If the replaced recipe is a rig/camera it re-parents them now; otherwise re-run that rig with --replace."); }
167
+ }
168
+ } else {
169
+ n = 1;
170
+ A.tag = "aeb:" + A.recipe + ":" + base;
171
+ while (__aebCount(comp, A.tag) > 0) { n++; A.tag = "aeb:" + A.recipe + ":" + base + "-" + n; }
172
+ if (n > 1) { PLAYRIG.log("note: " + comp.name + " already holds layers from " + A.recipe + "; this run is instance \"" + base + "-" + n + "\". Use --replace (with --instance " + base + ") to update the first set in place instead of adding another."); }
173
+ }
174
+ for (i = 1; i <= comp.numLayers; i++) { A.before[comp.layer(i).id] = true; }
175
+ }
176
+
177
+ function __aebPost() {
178
+ var A = __AEB, comp = __aebFindComp(), i, l, fresh = [], prev;
179
+ if (!comp) { return; }
180
+ for (i = 1; i <= comp.numLayers; i++) { l = comp.layer(i); if (!A.before[l.id]) { fresh.push(l); } }
181
+ for (i = 0; i < fresh.length; i++) { if (!fresh[i].comment) { fresh[i].comment = A.tag; } }
182
+ if (A.removed && A.above) { // put the replacement exactly where the old layers were
183
+ prev = A.above;
184
+ for (i = 0; i < fresh.length; i++) { fresh[i].moveAfter(prev); prev = fresh[i]; }
185
+ }
186
+ PLAYRIG.log("tagged " + fresh.length + " layer(s) as " + A.tag + (A.removed ? " (replaced " + A.removed + " in place)" : ""));
187
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "background": "Full-frame backdrops: gradients, grids, textures.",
3
+ "type": "Text animation: headline reveals, typewriters, exits.",
4
+ "elements": "Shapes and UI elements: chips, stickers, panels, logos.",
5
+ "camera": "Camera moves and 3D rigs.",
6
+ "grade": "Colour treatments applied over a scene.",
7
+ "transition": "Ways of getting from one scene to the next.",
8
+ "structure": "Assembling scenes into a finished sequence (master comps, edits)."
9
+ }
File without changes
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "playrig-ae",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "Playrig for After Effects: the ScriptUI panel, motion library and skills. Versioned on its own (tags ae-v*).",
6
+ "license": "SEE LICENSE IN LICENSE"
7
+ }
@@ -0,0 +1,39 @@
1
+ #targetengine "playrig"
2
+ // Playrig for After Effects: stub. This is the only file in After Effects' ScriptUI Panels folder, so it is also the
3
+ // panel's name in the Window menu. It stays tiny and stable, and loads the real panel (core.jsx) from the newest of:
4
+ // 1. the user's data folder (<userData>/Playrig/ae/current), where verified updates are installed without admin rights
5
+ // 2. "Playrig lib" next to this file, the copy that `playrig install ae` put there
6
+ // If a newer copy fails to load, the bundled one is used. ES3 only.
7
+
8
+ (function (thisObj) {
9
+ var G = $.global;
10
+ var STUB_API = 1; // a payload that needs a newer stub is ignored (see version.json "stubApi")
11
+ var here = new File($.fileName).parent.fsName;
12
+
13
+ function readVersion(dir) {
14
+ var f = new File(dir + "/version.json"), s, m, api;
15
+ if (!f.exists) { return null; }
16
+ try { f.encoding = "UTF-8"; f.open("r"); s = f.read(); f.close(); } catch (e) { return null; }
17
+ m = /"version"\s*:\s*"(\d+)\.(\d+)\.(\d+)/.exec(s);
18
+ api = /"stubApi"\s*:\s*(\d+)/.exec(s);
19
+ if (!m || (api && Number(api[1]) > STUB_API)) { return null; }
20
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
21
+ }
22
+ function newer(a, b) { var i; for (i = 0; i < 3; i++) { if (a[i] !== b[i]) { return a[i] > b[i]; } } return false; }
23
+
24
+ var bundled = here + "/Playrig lib";
25
+ var user = Folder.userData.fsName + "/Playrig/ae/current";
26
+ var bv = readVersion(bundled), uv = readVersion(user), order = [], i, core;
27
+ if (uv && new File(user + "/core.jsx").exists && (!bv || newer(uv, bv))) { order.push(user); }
28
+ if (new File(bundled + "/core.jsx").exists) { order.push(bundled); }
29
+ if (!order.length) {
30
+ try { alert("Playrig is not installed correctly: \"Playrig lib\" is missing next to Playrig.jsx. Run: playrig install ae"); } catch (e0) { }
31
+ return;
32
+ }
33
+ for (i = 0; i < order.length; i++) {
34
+ core = new File(order[i] + "/core.jsx");
35
+ G.__PLAYRIG_PANEL = thisObj;
36
+ G.__PLAYRIG_HERE = order[i];
37
+ try { $.evalFile(core); return; } catch (e) { G.__PLAYRIG_LOAD_ERROR = String(e); }
38
+ }
39
+ })(this);